文字列リテラル

6.文字列処理(教科書 p.301-p.332)
今回はC言語の文字列処理について復習し、文字列の探索手法について学ぶ。
文字列とは
プログラム上での文字の並びを表すのが文字列(string)である。これは中身が空であっても
同様に呼ばれる。C言語では、"STRING"のように、文字の並びを二重引用符"で囲んだものを文字
列リテラル(string literal)と呼んでいた。ASCII コードではその内部は図6-1のようにな
っている。
文字列リテラル"STRING"
S
T
R
I
N
G
\0
各文字は記憶域上に連続的に配置され、
末尾にはナル文字が入る
図6-1
0
1
0
1
0
0
1
1
文字 'S'
0
1
0
1
0
1
0
0
文字 'T'
0
1
0
1
0
0
1
0
文字 'R'
0
1
0
0
1
0
0
1
文字 'I'
0
1
0
0
1
1
1
0
文字 'N'
0
1
0
0
0
1
1
1
文字 'G'
0
0
0
0
0
0
0
0
ナル文字 '\0'
文字列リテラル
このように、各文字はメモリ上に連続して配置され、文字に対応する数値データによって管理
されている。各文字の対応する数値は文字コードの体系によって異なる。一般的に Linux では UTF
が、Windows では SJIS と呼ばれる体系が使われている。char 型は今 8 ビットとして図示してある
が、このサイズは処理系によっては 9 ビットや 32 ビットの場合もある。文字列リテラルには終端
を示すためのナル文字(null character)が自動的に付加される。ナル文字は文字コードによら
ず、全てのビットが 0 である。文字列リテラルは定数として扱われる。文字を再変更する際の動
作はC言語では保証されていない。以下の宣言を見て欲しい。以下は文字列リテラルによって、
ポインタ pt を初期化しているが、文字列リテラル"1234"はこれだけで、メモリ上に確保されてい
る。
char *pt = "1234";
ポインタ
0
1
2
3
4
1
2
3
4
\0
文字列リテラル
ptは文字列リテラルの先頭文字'1'を指す
図6-2
ポインタと文字列リテラル
以下のプログラム(教科書 p.230、List8-4 の変更)で実際に利用してみる。ポインタを利用す
る際にはそのポインタが指し示す実体が必要となるが、"1234"はどこに格納したかを記述してい
ない。しかし、問題なく表示することができ、ポインタ変数の値を操作することで1文字ずつ出
力することもできる。しかし、*(pt+1)= '9';というような値の変更を行うことは C 言語では保証
されていない。
プログラム例
/* 文字列を表示(ポインタと文字列リテラル) */
#include
<stdio.h>
int main(void)
{
char *pt = "1234";
printf("ポインタptは\"%s\"を指しています。\n", pt);
-1-
printf("*pt
は'%c'です。\n",
printf("*(pt+1)は'%c'です。\n",
printf("*(pt+2)は'%c'です。\n",
printf("*(pt+3)は'%c'です。\n",
*pt);
*(pt+1));
*(pt+2));
*(pt+3));
return (0);
}
実行例
ポインタptは"1234"を指しています。
*pt
は'1'です。
*(pt+1)は'2'です。
*(pt+2)は'3'です。
*(pt+3)は'4'です。
もし、その文字列は変更される予定であれば、以下のように配列を確保した上で行う。
プログラム例
/* 文字列を格納して表示 */
#include
<stdio.h>
int main(void)
{
char st[10] = "ABCD";
printf("文字列stには\"%s\"が格納されています。\n", st);
st[1] = 'D';
printf("文字列stには\"%s\"が格納されています。\n", st);
return (0);
}
実行例
文字列stには"ABCD"が格納されています。
文字列stには"ADCD"が格納されています。
文字列の長さ
配列を用いた文字列を利用することを考える。文字列の最後にはナル文字が入っている。文字
列の長さを求める場合、配列の先頭から始めて、ナル文字を線形探索していけばよい。その際、
ナル文字が見つかった添字が文字列の長さと一致する。
ナル文字を線形探索する
0
1
2
3
4
5
⑥
S
T
R
I
N
G
\0
図6-3
7
8
9
文字列と長さ
文字列の長さを求めるプログラム(教科書 p.308、List8-6)を以下に転載する。
List8-6
/* 文字列の長さを求める */
-2-
#include <stdio.h>
/*--- 文字列 s の長さを求める(その1)---*/
int str_len(const char *s)
{
int len = 0;
while (s[len])
len++;
return len;
}
int main(void)
{
char str[256];
printf("文字列:");
scanf("%s", str);
printf("その文字列は%d 文字です。\n", str_len(str));
return 0;
}
実行例
文字列:ABCD
その文字列は4文字です。
関数 int str_len(const char *s)を確認しよう。while の条件式は s[len]である。s[]は線形
探索を行おうとしている配列であり、len は走査するための変数である。char 型の配列の各要素
についての条件判断が行われているが、C言語では 0 以外の値が真とみなされ、ナル文字は文字
コードによらず、全てのビットが 0 であるため、このような比較方法でプログラミングが可能と
なる。なお、C言語では、標準ライブラリの string.h として、strlen 関数(教科書 p.309 参照)
が用意されており、これを使えば文字列の長さを求めることができる。
文字列から文字の探索
文字列からナル文字ではなく、任意の文字を探索する手続きを考える。例として文字列
"SURROUND"から文字'R'を探索するものとすると、図6-4のように、配列の先頭から線形探索を
行うことになる。
文字'R'を線形探索する
0
1
②
3
4
5
6
7
8
S
U
R
R
O
U
N
D
\0
図6-4
このプログラム(教科書 p.234、List8-9)を以下に転載する。
List8-9
/* 文字列からの文字の探索 */
#include <stdio.h>
-3-
9
/*--- 文字列 s から文字 c を探索 ---*/
int str_chr(const char *s, int c)
{
int i = 0;
c = (char)c;
while (s[i] != c) {
if (s[i] == '\0')
return -1;
i++;
}
return i;
/* 探索失敗 */
/* 探索成功 */
}
int main(void)
{
char str[64];
char tmp[64];
int ch;
int idx;
/* この文字列から探索 */
/* 探す文字 */
printf("文字列:");
scanf("%s", str);
printf("探す文字:");
scanf("%s", tmp);
ch = tmp[0];
/* いったん文字列として読み込んで */
/* その最初の文字を探索文字とする */
if ((idx = str_chr(str, ch)) == -1)
/* 先頭の出現を探索 */
printf("文字'%c'は文字列中に存在しません。\n", ch);
else
printf("文字'%c'は%d 文字目に存在します。\n", ch, idx + 1);
return 0;
}
実行例
文字列:SURROUND
探す文字:R
文字'R'は3文字目に存在します。
関数 str_chr は、文字列 s から文字 c を線形探索する。探索に成功時にはその位置の添字を返
し、失敗時には-1 を返す。なお、C言語では、標準ライブラリの string.h として、strchr 関数
と strrchr 関数(教科書 p.310 参照)が用意されており、文字列から文字の探索を行うことがで
きる。
文字列の比較
C言語では、二つの文字列の大小関係を判定するため、標準ライブラリの string.h として、
strcmp 関数と strncmp 関数が提供されている。これらについて学習していく。
◆strcmp 関数
この関数の仕様を以下に示す。
-4-
ヘッダ
形式
解説
返却値
#include <string.h>
int strcmp(const char *s1, const char *s2);
s1 が指す文字列と s2 が指す文字列の大小関係(先頭から順に 1 文字ずつ比較していき、
異なる文字が出現したときに、それらの文字の対に成立する大小関係とする)の比較を
行う。
等しければ 0、s1 が s2 より大きければ正の整数値、s1 が s2 より小さければ負の整数
値を返す。
この関数では、図6-5に示すように、二つの文字列を先頭から順に比較して、すべての文字
が等しければ 0 を返す。
(a)文字列は一致
0
1
2
3
4
5
6
S
T
R
I
N
G
\0
0
1
2
3
4
5
6
S
T
R
I
N
G
\0
7
8
9
10
11
7
8
9
10
11
7
8
9
10
11
7
8
9
10
11
0を返す
(b)文字列は不一致
0
1
2
3
4
5
6
S
T
R
I
N
G
\0
0
1
2
3
4
5
6
S
T
R
I
K
E
\0
0以外の値を返す
図6-5
strcmp 関数による比較
図(b)に示すように途中の文字が等しくない場合には 0 以外を返すことになるが、その値は文字
コード体系と処理系の両方に依存することなる。'N '- 'K 'の値を返却値として返す場合や'N '
が'K 'より大きいので単純に 1 を返す場合もある。
この strcmp 関数の仕様に準じて、同様の処理を行う str_cmp 関数の実装例(教科書 p.313、
List8-10)を以下に転載する。
List8-10
/* 文字列の比較 */
#include <stdio.h>
#include <string.h>
/*--- 二つの文字列 s1 と s2 を比較 ---*/
int str_cmp(const char *s1, const char *s2)
{
while (*s1 == *s2) {
if (*s1 == '\0')
/* 等しい */
return 0;
s1++;
s2++;
}
return (unsigned char)*s1 - (unsigned char)*s2;
}
int main(void)
{
char st[128];
puts("\"ABCD\"との比較を行います。");
-5-
puts("\"XXXX\"で終了します。");
while (1) {
printf("文字列 st:");
scanf("%s", st);
if (strcmp("XXXX", st) == 0)
break;
printf("str_cmp(\"ABCD\", st) = %d\n", str_cmp("ABCD", st));
}
return 0;
}
実行例
"ABCD"との比較を行います。
"XXXX"で終了します。
文字列st:AX
str_cmp("ABCD", st) = -22
文字列st:AA
str_cmp("ABCD", st) = 1
文字列st:ABCD
str_cmp("ABCD", st) = 0
文字列st:XXXX
◆strncmp 関数
この関数の仕様を以下に示す。
ヘッダ
#include <string.h>
形式
int strncmp(const char *s1, const char *s2, size_t n);
解説
s1 が指す文字の配列と s2 が指す文字の配列の先頭 n 文字までの大小関係(先頭から順
に 1 文字ずつ比較していき、異なる文字が出現したときに、それらの文字の対に成立す
る大小関係とする)の比較を行う。
返却値
等しければ 0、s1 が s2 より大きければ正の整数値、s1 が s2 より小さければ負の整数
値を返す。
この関数では、図6-6に示すように、二つの文字列を先頭から順に比較して、指定した文字
数までが一致しているかどうかを比較する。
(a)先頭3文字は一致
strncmp("STRING", "STRIKE", 3);
0
1
2
3
4
5
6
S
T
R
I
N
G
\0
0
1
2
3
4
5
6
S
T
R
I
K
E
\0
7
8
9
10
11
7
8
9
10
11
0を返す
-6-
(b)先頭5文字は不一致
strncmp("STRING", "STRIKE", 5);
0
1
2
3
4
5
6
S
T
R
I
N
G
\0
0
1
2
3
4
5
6
S
T
R
I
K
E
\0
7
8
9
10
11
7
8
9
10
11
0以外の値を返す
図6-6
strncmp 関数による比較
図6-6に示すように二つの文字列"STRING"と"STRIKE"の先頭 3 文字を比較すると一致するが、
先頭 5 文字を比較すると一致しないことがわかる。strncmp 関数を利用したプログラム例(教科
書 p.315、List8-11)を以下に転載する。
List8-11
/* 文字列の比較(strncmp 関数)*/
#include <stdio.h>
#include <string.h>
int main(void)
{
char st[128];
puts("\"STRING\"の先頭 3 文字と比較します。");
puts("\"XXXX\"で終了します。");
while (1) {
printf("文字列 st:");
scanf("%s", st);
if (strncmp("XXXX", st, 3) == 0)
break;
printf("strncmp(\"STRING\", st, 3) = %d\n", strncmp("STRING", st, 3));
}
return 0;
}
実行例
"STRING"の先頭3文字と比較します。
"XXXX"で終了します。
文字列st:STAR
strncmp("STRING", st, 3) = 1
文字列st:STRIKE
strncmp("STRING", st, 3) = 0
文字列st:XXXX
演習
課題
List8-6、List8-9、List8-10 を作成し、出力を確認せよ。
strncmp 関数と同じ動作をする以下の関数を作成せよ。
int str_ncmp(const char *s1, const char *s2, int n);
動作の確認は List8-11 に従い、main 関数を用いて、行うものとする。
-7-