C言語で文字列を扱う際に覚えておくと便利なのが文字列操作関数です。
このページでは、よく利用する文字列操作関数と、その使用方法について解説していきます。
私も毎回ググってるよ…
まあ、なので、覚えなくても良いようにこんなまとめページを作ったんだ!
私も文字列操作関数は毎回調べながら使用しています。
おそらくそういう方も多いと思うので、よく使う文字列操作関数をこのページにまとめました!
是非文字列操作関数やその使い方に迷ったらこのページを参考にしてください。
Contents
文字列の長さを取得する
文字列の長さ(文字数)を取得する時に使用する関数は「strlen
関数」です。
strlen
関数
strlen
関数は文字列の長さを取得する関数になります。
strlen
関数を実行することで、例えば文字列 "aiueo"
の文字列の長さ 5
を取得することができます。
strlen
関数の定義ファイル、関数定義は下記の通りです。
#include <string.h>
size_t strlen(const char* str);
strlen
関数の引数
strlen
関数の引数には str
、文字列へのアドレスを指定します。より具体的には、文字列が格納された配列やメモリのアドレスを指定します。
strlen
関数の返却値
strlen
関数の返却値は引数 str
の文字列の長さになります。
この文字列の長さにはヌル文字(\0
)の分はカウントされません。
strlen
関数の詳細
では、この strlen
関数が返却する「文字列の長さ」とは具体的にはどのような値になるでしょうか?
これは、引数で指定するアドレス str
からアドレスを1バイトずつ増やしていき、次にヌル文字(\0
)が見つかるまでのバイト数になります。
バイト数ですので、日本語などマルチバイト文字を格納した文字列に対して strlen
関数を実行すると文字数と一致しないので注意しましょう。
ただ、特にC言語習いたての方は1バイト文字(英数字)を利用することが多いので、まずは「strlen
関数は文字数を取得する関数」と覚えておいて差し支えないと思います。
また、ヌル文字(\0
)が存在することを期待して動作しますので、ヌル文字(\0
)が含まれないデータへのアドレスを strlen
関数に渡してしまうと動作がおかしくなります。
\0
)が含まれないとどうなるの?おそらくだけど、配列のサイズに関係なく、次にヌル文字(\0
)があるアドレスまでヌル文字の探索が行われるよ
この場合、配列のサイズを超えてデータの参照が行われるのでバッファオーバーフローになる可能性もある
スポンサーリンク
strlen
関数の使用例
strlen
関数を使用して文字列の長さを取得するプログラムの例は下記になります。
#include <stdio.h>
#include <string.h>
int main(void){
char *str1 = "hitotsume";
size_t len1;
/* 文字列の長さ取得 */
len1 = strlen(str1);
/* 表示 */
printf("str1 = %s, len1 = %lu\n", str1, len1);
return 0;
}
実行結果は下のようになります。
str1 = hitotsume, len1 = 9
文字列をコピーする
文字列をコピーする時に利用する関数は「strcpy
関数」です。
strcpy
関数
strcpy
関数は文字列をコピーする関数です。
strcpy
関数の定義ファイル、関数定義は下記の通りです。
#include <string.h>
char* strcpy(char* str1, const char* str2);
strcpy
関数の引数
第1引数の str1
には文字列をコピーする “先” の配列やメモリのアドレスを指定し、第2引数の str2
には文字列をコピーする “元” の配列やメモリのアドレスを指定します。
strcpy
関数の返却値
返却値は文字列をコピーした “先” の先頭アドレスになります。つまり、str1
と一致します。
strcpy
関数の詳細
strcpy
を実行することで、str2
のアドレスからヌル文字(\0
)までの文字列が str1
のアドレスの配列やメモリにコピーされます。
\0
)もコピーされるの?ヌル文字(\0
)も含めてコピーされると考えて良いです。
ただし、コピー先のアドレスである str1
の指す配列やメモリは、ヌル文字(\0
)を含めたサイズにしておく必要があります。
スポンサーリンク
strcpy
関数の使用例
strcpy
関数を使用して文字列をコピーするプログラムの例は下記になります。
#include <stdio.h>
#include <string.h>
#define MAX_CHAR_NUM 10
int main(void){
char str1[MAX_CHAR_NUM];
char str2[MAX_CHAR_NUM];
/* 文字列コピー */
strcpy(str1, "hitotsume");
printf("str1 = %s\n", str1);
printf("\n");
strcpy(str2, str1);
printf("str1 = %s\n", str1);
printf("str2 = %s\n", str2);
printf("\n");
strcpy(str2, "futatsume");
printf("str1 = %s\n", str1);
printf("str2 = %s\n", str2);
printf("\n");
return 0;
}
実行結果は下のようになります。ポイントは "hitotsume"
などのような「文字列自体」を strcpy
関数の第2引数にも指定可能という点だと思います。
この場合もしっかりヌル文字(\0
)が含まれる形で文字列のコピーが行われます。
str1 = hitotsume str1 = hitotsume str2 = hitotsume str1 = hitotsume str2 = futatsume
文字列を比較する
文字列の比較する時に利用する関数は「strcmp
関数」です。
strcmp
関数
strcmp
関数は2つの文字列を比較する関数です。
strcmp
関数の定義ファイル、関数定義は下記の通りです。
#include <string.h>
int strcmp(const char* str1, const char* str2);
strcmp
関数の引数
strcmp
関数の第1引数 str1
・第2引数 str2
に比較したい2つの文字列が格納された配列やメモリの先頭アドレスを指定します。
単に同じかどうかを比較したい場合は、第1引数 str1
と第2引数 str2
の順序に特に意味はありません。
ただし、大小関係を調べたい場合は第1引数 str1
と第2引数 str2
の指定順で返却値が異なりますので注意してください。
strcmp
関数の返却値
引数 str1
と str2
で指定したアドレスの先の2つの文字列が同じである場合、strcmp
関数は 0
を返却します。
同じでない場合は 0
以外の値を返却します。
より具体的には、”文字コード的に” 第1引数の方が大きい場合は “正” の値、第2引数の方が大きい場合は “負” の値を返却します。
正確に言うと「文字に割り当てられた数字」に大きいや小さいがあるんだ
各文字には文字コードという数字が割り当てられてて、その数字の大小関係に応じて値が返却される感じだね
strcmp
関数の詳細
strcmp
関数は引数 str1
と str2
で指定された2つの文字列の比較を行います。
比較は先頭の文字から1文字ずつ行われます。
そして、両方の文字列の文字がヌル文字(\0
)である場合もしくは文字が異なる場合に比較を終了します。
この時、両方文字列の文字がヌル文字(\0
)である場合は、2つの文字列の先頭から文字列の最後(ヌル文字)まで同じであると判断できるので、文字列が同じであることを示す 0
が返却されます。
一方、文字が異なる場合は、2つの文字列が異なると判断できるので、文字列が異なることを示す 0
以外の値が返却されます。
ここからは環境によって異なるかもしれませんが、私の環境では異なった文字の文字コードの差が返却されました。
具体的には、引数 str1
と引数 str2
の第 n
文字目が異なる場合は、下記の値が返却されるようでした。
str1[n] - str2[n];
例えば str1 = "abc"
、str2 = "abx"
の場合、'c' - 'x' = -21
が返却されました。
この辺りの細かい返却値については環境依存で結果が異なる可能性がありますが、str1
の方が文字コード的に大きい場合は正の値、str2
の方が文字コード的に大きい場合は負の値が返却される点はどの環境でも同じではないかと思います。
こういった文字コードの大小関係を strcmp
関数の返却値で取得することもできますので、2分探索を行う場合にも便利な関数です。
スポンサーリンク
strcmp
関数の使用例
strcmp
関数を使用して文字列を比較するプログラムの例は下記になります。
#include <stdio.h>
#include <string.h>
int main(void){
char str[] = "abc";
char str1[] = "abc";
char str2[] = "abcd";
char str3[] = "abd";
char str4[] = "abb";
char str5[] = "Acd";
printf("strcmp(%s, %s) : %d\n", str, str1, strcmp(str, str1));
printf("strcmp(%s, %s) : %d\n", str, str2, strcmp(str, str2));
printf("strcmp(%s, %s) : %d\n", str, str3, strcmp(str, str3));
printf("strcmp(%s, %s) : %d\n", str, str4, strcmp(str, str4));
printf("strcmp(%s, %s) : %d\n", str, str5, strcmp(str, str5));
return 0;
}
実行結果は下のようになります。
strcmp(abc, abc) : 0 strcmp(abc, abcd) : -100 strcmp(abc, abd) : -1 strcmp(abc, abb) : 1 strcmp(abc, Acd) : 32
文字列を連結(結合)する
文字列を連結することができるのは strcat
関数です。
strcat
関数
strcat
関数は2つの文字列を連結する関数です。
例えば "ai"
という文字列と "ueo"
という文字列を連結して "aiueo"
という文字列を生成すすることができます。
strcat
関数の定義ファイル、関数定義は下記の通りです。
#include <string.h>
char* strcat(char* str1, const char* str2);
strcat
関数の引数
strcat
関数の第1引数 str1
には連結 “元” になる文字列が格納された配列やメモリのアドレスを、第2引数 str2
には第1引数 str1
に連結したい文字列が格納された配列やメモリのアドレスを指定します。
strcat
関数の返却値
2つの文字列を連結した結果が格納される配列やメモリの先頭アドレスが返却されます。
といっても、strcat
関数では後述するように第1引数で指定した文字列の後ろ側に第2引数で指定した文字列が連結されるため、結局第1引数で指定したアドレスと同じものが返却されることになります。
strcat
関数の詳細
strcat
関数は引数で指定した文字列 str1
の後ろに文字列 str2
を連結します。
より具体的に言うと、str1
の終端を表すヌル文字(\0
)部分から後ろ側に文字列 str2
が連結されます。
str1
の終端を表すヌル文字(\0
)は str2
に上書きされて無くなりますが、連結後も str2
側のヌル文字(\0
)は残ります。
strcat
関数の注意点
strcat
関数には注意点が2つあります。
1点目は、第1引数で指定した str1
の指す配列やメモリの内容が下記変わってしまうことです。具体的には、strcat
関数を実行することで、str1
の指す配列やメモリには str1
に str2
を連結した文字列が格納されることになります。
2点目は第1引数で指定した str1
の指す配列やメモリのサイズです。
strcat
関数を実行すると、str1
の後ろ側に str2
の文字列が連結されることになりますので、str1
の指す配列やメモリのサイズは str2
連結後の文字列が十分に格納できるだけのものでなければなりません。
連結後の文字列が配列やメモリに入りきらない場合、メモリの不正アクセスになってしまうので注意してください。
スポンサーリンク
strcat
関数の使用例
下記は strcat
関数を用いて文字列の連結を行うプログラムの例です。
#include <stdio.h>
#include <string.h>
int main(void) {
char str1[10] = "ai";
char str2[] = "ueo";
strcat(str1, str2);
printf("str1 = %s\n", str1);
}
実行すると下記のように表示され、str1
に str2
が連結されていることが確認できると思います。
aiueo
下記はダメな例です。
#include <stdio.h>
#include <string.h>
int main(void) {
char str1[] = "ai";
char str2[] = "ueo";
strcat(str1, str2);
printf("str1 = %s\n", str1);
}
str1
には str2
が連結されるので、str2
の文字列の長さも考慮して str1
の配列のサイズを設定する必要があります。
下記だと str1
の配列のサイズが "ai"
の文字列分しか確保されていないため、strcat
関数で str1
に str2
に連結する時にバッファオーバーフローが発生します。
書式を指定して文字列を生成する
書式を指定して文字列を生成するのは sprintf
関数です。
printf
関数同様の感覚で文字列の生成や文字列の結合などを行うことができます。sprintf
関数に関しては下記ページで詳しく解説しておりますので、詳しく知りたい方は下記ページをご参照いただければと思います。
文字列を探索する
文字列を探索する時に使用するのは strstr
関数です。
スポンサーリンク
strstr
関数
strstr
関数は文字列の中から指定した文字列を探索する関数です。
strstr
関数の定義ファイル、書式は下記の通りです。
#include <string.h>
char* strstr(const char* str1, const char* str2);
strstr
関数の引数
strstr
関数の第1引数 str1
には探索 “先” になる文字列が格納された配列やメモリのアドレスを、第2引数 str2
には第1引数 str1
で指定した文字列から探索したい文字列が格納された配列やメモリのアドレスを指定します。
strstr
関数の返却値
文字列 str1
の中から文字列 str2
が見つかった位置(アドレス)を返却します。
見つからなかった場合は NULL
を返却します。
strstr
関数の詳細
strstr
関数では str1
の文字列(str1
の指すアドレスから、str1
の終端を表すヌル文字('\0'
)までの間のデータ)の中から str2
の指す文字列の探索が行われます。
str1
の文字列の中から str2
が見つかった場合、str2
が見つかった位置のアドレスを返却します。
str1
の文字列に str2
の文字列が複数存在する場合は、先頭に近い方のアドレスが返却されます。
strstr
関数の使用例
下記は strstr
関数を用いて文字列の探索を行うプログラムの例です。
#include <string.h>
#include <stdio.h>
#define MAX_CHAR_NUM 10
#define MAX_LINE 5
int main(void) {
char str[MAX_LINE][MAX_CHAR_NUM] = {
"I am cat",
"scan",
"melon",
"cat can",
"cccaaaa"
};
char search[] = "ca";
int i;
char *ret;
for (i = 0; i < MAX_LINE; i++) {
ret = strstr(str[i], search);
if (ret != NULL) {
printf("%s:%s\n", str[i], ret);
} else {
printf("%s:Notfound\n", str[i]);
}
}
return 0;
}
実行結果は下記のようになります。strstr
関数の返却値は、str2
で指定した文字列が見つかった位置ですので、返却値を表示すると str1
の途中に str2
が存在する場合はその位置から文字列が表示されることになります。
I am cat:cat scan:can melon:Notfound cat can:cat can cccaaaa:caaaa
文字列を分離する(切り離す)
文字列を分離(切り離し)したい時に使用するのは strtok
関数です。
strtok
関数に関しては注意点が多いので下記ページにまとめています。strtok
関数について詳しく知りたい方は下記ページを参照していただければと思います。
スポンサーリンク
まとめ
このページでは下記の文字列を操作する関数について解説しました!
-
strlen
strcpy
strcmp
strcat
strstr
C言語でも文字列の操作を行う関数が数多く用意されています。
とにかくこれらの関数では、文字列の最後にヌル文字('\0'
)が存在することを期待して動作します。なので、ヌル文字('\0'
)が含まれない文字列を引数に指定すると基本的にバッファオーバーフローが起こります。ここが文字列操作関数における1番の注意点だと思います。
また、便利なものが多いですが、strtok
のようにクセの強い関数も結構多い印象です!
使い方に困った時などは是非コメントなどで質問いただければと思いますのでよろしくお願いいたします。