C言語でsplitっぽいものを書く

もうすぐあるらしい学科のプログラミングテストとやらに備えて、少しC言語を書いてみる。書いたのはPerlやRubyやPythonでは実装されている、split関数*1っぽいもの。

結局、strtokのラッパー関数みたいになってしまった。

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

#define MAX_STR 256
#define MAX_WORD 128

// strtokと同じ引数をとる
// 文字列さすポインタを要素とする配列を返す
char **split(char *str, char *sep)
{
    char *word = NULL;
    char *str_work = NULL;
    int last = 0;

    //うけとった文字列のコピーを作成
    str_work = (char *)malloc(sizeof(char) * (strlen(str) + 1));
    strcpy(str_work, str);

    //文字列のポインタを保持する配列
    char **results = (char **)malloc(sizeof(char *) * MAX_WORD);
    
    //strtokで得たトークンのポインタを配列に代入
    last = 0;
    for(word = strtok(str_work,sep); word; word = strtok(NULL,sep),last++) {
        results[last] = word;
    }
    results[last] = NULL;
    
    return results;   
}

// splitで作成した配列のメモリを解放する
void split_free(char **words)
{   
    //words[0]をfreeすればすべてのトークンのメモリは解放される?
    free(words[0]);
    free(words);
}

int main(int argc, char *argv[])
{
    int i = 0;
    char str[MAX_STR] = "Hello-my-world"; //splitする前の文字列
    char *sep = "-"; //セパレータ
    char **words = NULL;
        
    //splitされたトークンを保持する配列を得る
    words = split(str, sep);
        
    for (i = 0; words[i] != NULL; i++) {
        printf("%2d: %s\n", i,words[i]);
    }
    
    split_free(words);
    words = NULL;
        
    return 0;
}

splitが返す配列にはトークン文字列が一つづつ確保されているので、扱いやすい。これを、tokという名前でコンパイルして動作させると、

$ ./tok 
 0: Hello
 1: my
 2: world

となります。

うーん、やはりメモリを解放するのに気を使わなきゃダメなのはめんどうだなぁ。ガベージコレクションマンセー。えー、これを、Perlで書くと、

$str = 'Hello-my-world';
@words = split(/-/,$str);

$count = 0;
foreach (@words) {
    printf "%2d: $_\n", $count;
    $count += 1;
}

となるね。短いね。RubyやPythonだと…。悲しくなるのでもういいや。Lightweight Languageマンセー。

プログラミング言語C 第2版 ANSI規格準拠

プログラミング言語C 第2版 ANSI規格準拠

*1:もしくはメソッド