文字列分割用ライブラリを勉強の為に書いてみた

URLパーサをつくるときに文字列分割用にstrtok_r()関数を利用したのだけど、なんかしっくりとこなかった(ただの勉強不足なわけですが...)ので勉強の為にも文字列分割ライブラリをつくってみた。

今回勉強になったもの

  • ユーザー関数
  • 構造体による連結リスト
  • malloc()によるメモリの動的確保
  • プログラムのモジュール化
    • ヘッダーファイル
    • オブジェクトのリンク方法

なんかいきなり盛りだくさんです。

今回つくったソース

とりあえず細かい説明の前に今回つくったソースを貼りつけておきます。(JavaDoc風のコメントをつけてみました。)

ヘッダーファイル
/**
 * split_text.h
 * 文字列分割用ライブラリ
 */

/**
 * @struct 分割された文字列を格納するための連結リスト用構造体
 * 分割された数分ノードが連結されます
 */
struct splitedtext {
    char *string;				//分割された文字列
    struct splitedtext *next;	//次のノード 
};

/**
 * テキストを分割して連結リストに格納する
 * @param string 分割対象テキスト
 * @param delimiter 区切り文字(デリミタ)
 * @param res 分割結果を格納するための連結リスト
 * @return 分割した回数を返す(分割されなかった場合は0を返す)
 */
int split_text (const char *string, const char *delimiter, struct splitedtext **res);

/**
 * リンクリスト res に対して動的に割り当てられたメモリを解放する
 * @param res 開放対象のsplitedtext構造体
 */
int freesplitedtext (struct splitedtext *res);

/**
 * 区切り文字の出現ポイントを返す
 * @param string 対象テキスト
 * @param delimiter 区切り文字
 * @return 区切り文字の出現ポイント
 */
int search_delimiter (const char *string, const char *delimiter);
実動作部
/**
 * split_text.c
 * 文字列分割用ライブラリ
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "split_text.h"

/**
 * テキストを分割して連結リストに格納する
 * @param string 分割対象テキスト
 * @param delimiter 区切り文字(デリミタ)
 * @param res 分割結果が格納された連結リストの最初のノード
 * @return 分割した回数を返す(分割されなかった場合は0を返す)
 */
int split_text (char const *string, char const *delimiter, struct splitedtext **res)
{
	
	char *chunk;
	int	counter,
		current_index,
		search_index,
		len;
	struct	splitedtext *current_splitedtext, *prev;
	
	/* chunkを空文字で初期化 */
	chunk = malloc(sizeof(char));
	chunk[0] = '\0';
	
	/* resを初期化 */
	*res = malloc(sizeof(struct splitedtext));
	(*res)->string = chunk;
	(*res)->next = NULL;
	
	current_splitedtext = *res; /* current_splitedtextをresで初期化 */
	prev = NULL;
	
	/* 各変数を初期化 */
	counter = 0;
	current_index = 0;
	search_index = 0;
	
	while (search_index != -1) { /* 探索点以降にデリミタが見つからなかった場合は分割を終了 */
		
		/* 探索点を起点としてデリミタの位置を検索 */
		search_index = search_delimiter(&string[current_index], delimiter);
		
		/* 探索点にデリミタがあった場合は分割せずスルー */
		if (search_index != 0) {
				
			/* 探索点からデリミタが見つかった点までの長さを算出:デリミタが探索点以降見つからなかった場合は残りの文字列の長さ */	
			len = (search_index == -1)? strlen(string) - current_index : search_index;
			
			/* 探索点からデリミタが見つかった地点までの文字列を抜き出して、
			 * 構造体に代入
			 */
			chunk = malloc(sizeof(char) * (len + 1));
			strncpy(chunk, &string[current_index], len);
			chunk[len] = '\0'; /* 抜き出した文字列の最後がNULL文字であるとは限らないため、NULL文字を追加。 */
			current_splitedtext->string = chunk;
			current_splitedtext->next = NULL;
			
			if(prev != NULL) {
				prev->next = current_splitedtext; /* 一つ前のノードに連結 */
			}
			
			/* 新しいノードを作成 */
			prev = current_splitedtext;
			current_splitedtext = malloc(sizeof(struct splitedtext));
			
			/* 分割した数を記録 */
			counter++;
		
		}
		
		/* デリミタが見つかった位置の後に探索点を移動 */
		current_index += search_index + strlen(delimiter);
		
		/* 探索点が文字長を超えた場合は分割を終了 */
		if(current_index >= strlen(string)) {
			break;
		}
		
	}
	
	return counter; /* 分割数を返す */
	
}

/**
 * リンクリスト res に対して動的に割り当てられたメモリを解放する
 * @param res 開放対象のsplitedtext構造体
 */
int freesplitedtext (struct splitedtext *res) {
	
	struct splitedtext *pr, *deleted;
	pr = res;
	
	do {
		free(pr->string);
		
		deleted = pr;
		pr=deleted->next;
		
		free(deleted);
	} while (pr != NULL);
	
	return 0;
	
}

/**
 * 区切り文字の出現ポイントを返す
 * @param string 対象テキスト
 * @param delimiter 区切り文字
 * @return 区切り文字の出現ポイント
 */
int search_delimiter (const char *string, const char *delimiter)
{
	
	char *pointer;
	int index;
	
	pointer = strstr(string, delimiter);
	index = (pointer == NULL)? -1 : pointer-string;
	
	return index;
	
}