■この記事の概要
この記事では、C言語のstrtok
関数を使用する際に文字列リテラルを渡すとセグメンテーションフォルトが発生する理由を解説しています。strtok
は文字列を書き換えるため、変更不可なリテラルを渡すと異常終了します。安全な利用法として、変更可能な文字列バッファを使用する方法が示されています。
■strtokの意味
strtok=string+token
string=文字列
token=単語
strtokは文字列を単語に分解する関数です。
■strtokの基本的な使い方
//strtokの簡単な使い方
#include <stdio.h>
#include <string.h>
int main(void){
char str[] = "1st,2nd,3rd,4th,5th";
puts(strtok(str, ","));//初回は分割対象文字列指定
puts(strtok(NULL, ","));//2回目以降NULL指定
puts(strtok(NULL, ","));
puts(strtok(NULL, ","));
puts(strtok(NULL, ","));
}
./a.out
1st
2nd
3rd
4th
5th
strtokを呼ぶ初回は分割対象文字列を指定し
2回目以降はNULLを指定します。
■区切り文字は複数指定できる
//区切り文字が複数の場合
#include <stdio.h>
#include <string.h>
int main(void){
char str[] = "1st,2nd/3rd,4th/5th";
const char *sep = ",/";//区切り文字が複数の場合
puts(strtok(str, sep));
puts(strtok(NULL, sep));
puts(strtok(NULL, sep));
puts(strtok(NULL, sep));
puts(strtok(NULL, sep));
}
strtokの区切り文字に”,”と”/”を指定した例です。
■注1:strtokは原文を書き変える
//strtokは原文を書き変える
#include <stdio.h>
#include <string.h>
int main(void){
char str[] = "1st,2nd,3rd,4th,5th";
printf("処理前=%s\n",str);
puts(strtok(str, ","));
puts(strtok(NULL, ","));
puts(strtok(NULL, ","));
puts(strtok(NULL, ","));
puts(strtok(NULL, ","));
printf("処理後=%s\n",str);
}
./a.out
処理前=1st,2nd,3rd,4th,5th
1st
2nd
3rd
4th
5th
処理後=1st
strtokは原文内の区切り文字を¥0に書き換えます。
■注2:strtokに文字列定数を指定するとSegmentation faultする
//strtokに文字列定数は指定できない
#include <stdio.h>
#include <string.h>
int main(void){
char *str = "1st,2nd,3rd,4th,5th";//文字列定数指定は誤り
printf("処理前=%s\n",str);
puts(strtok(str, ","));
puts(strtok(NULL, ","));
puts(strtok(NULL, ","));
puts(strtok(NULL, ","));
puts(strtok(NULL, ","));
printf("処理後=%s\n",str);
}
./a.out
処理前=1st,2nd,3rd,4th,5th
Segmentation fault
strtokは第一引数の指す先を書き換えようとするので、書き換えられない文字列定数を指定すると環境によって Segmentation fault 等の異常終了します。
■注3:戻り値のNULLチェックが必要
//strtokの戻り値をチェックしないとNULL参照が発生する
#include <stdio.h>
#include <string.h>
int main(void){
char str[] = "1st,2nd,3rd,4th,5th";
printf("処理前=%s\n",str);
puts(strtok(str, ","));//1st
puts(strtok(NULL, ","));//2nd
puts(strtok(NULL, ","));//3rd
puts(strtok(NULL, ","));//4th
puts(strtok(NULL, ","));//5th
puts(strtok(NULL, ","));//6thは無いのでNULLが返る
printf("処理後=%s\n",str);
}
./a.out
処理前=1st,2nd,3rd,4th,5th
1st
2nd
3rd
4th
5th
Segmentation fault
strtokは次の単語が無ければNULLを返り値とします。
NULLチェクをしないと環境によって Segmentation fault 等の異常終了します。
■strtokをループの中で使う方法
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void 文字列を分離文字で分解(char *文字列)
{
char *単語;
const char *分離文字 = "/";
while((単語 = strtok(文字列,分離文字)) != NULL){
printf("\t処理中->:%s:\n",単語);
//strtok第1引数は2回目からNULL
文字列 = NULL;
}
}
int main(void)
{
char 文字列[] = "/usr/include/stdio.h";// 正常終了
printf("処理前:%s\n", 文字列);
文字列を分離文字で分解( 文字列);
printf("処理後:%s\n", 文字列);
}
./a.out
処理前:/usr/include/stdio.h
処理中->:usr:
処理中->:include:
処理中->:stdio.h:
処理後:/usr
-------------------
参考: