Rubyではとても手軽にできた、「文字列同士の大小比較」や 「改行文字の切り取り(chomp)」も、C言語ではとてもややこしい。
C言語で文字列の操作をするときには、C言語の文字列がどんな構造になって いるかをつねに考えていないとできないと言っても過言ではない。 先週学習したことを繰り返そう。
Cの文字列は、char型(8ビット)数値の配列である
文字列の終わりを意味するマークとして最後の配列要素には
\0
(文字コード0の文字) が入る
これを終始意識していないとすぐに理解の限界がやってくる。 復習しよう。
char s[] = "Hello";
というCの文があったとき、コンピュータ内部では次のような配列が 作られる。
s | |||||
---|---|---|---|---|---|
s[0] | s[1] | s[2] | s[3] | s[4] | s[5] |
'H' | 'e' | 'l' | 'l' | 'o' | '\0' |
72 | 101 | 108 | 108 | 111 | 0 |
C言語では文字をシングルクォート(')で括ったものは、その文字の
文字コードを意味する。
例: 'A'
== Aの文字コード == 65
ある文字の文字コードを知りたいときはEmacsで知りたい文字の上に カーソルを合わせて C-x = をタイプする。たとえば、 Aの文字に合わせて、C-x = すると、
Char: A (0101, 65, 0x41) point=1 of 46 (0%) column 0
と出る。括弧内の数値(0101,65,0x41)が文字コードで、それぞれ 8進数、10進数、16進数表記となっている。
C言語プログラムで文字列 "Hello"
が出てきたら
'\0'
が付いているんだぜ
ということを瞬時に思い浮かべるようにする。
Ruby言語では文字列の大小関係の比較は数値と同様 == <=
>=
を使えば良かった。C言語では文字列専用の比較関数である
strcmp
を使う。
strcmp
- 文字列の大小比較
プログラムの先頭に
#include <string.h>
を追加した上で以下のような書式で利用する。
strcmp(文字列その1, 文字列その2)
strcmp
関数は、文字列その1と
文字列その2 の大小(辞書順並び)を調べて、その結果を
int型の数値で返す。
返す。イメージとしては、文字コードを使って
「文字列その1」-「文字列その2」
という引き算を計算した結果を返しているのだと 考えて良い。
実際にstrcmp
を使って文字列の比較をしてみよう。
【入力した文字が y か n かで処理を切り替える】
:
:
char line[3]; /* y か n なら3文字で足りるだろう(改行と\0を忘れずに) */
:
printf("続けますか?(y/n): ");
fgets(line, sizeof line, stdin); /* 実際の読みこみ y \n \0 か n \n \0 */
if (0 == strcmp(line, "y\n")) {
/* y と答えた場合の処理をここに書く */
} else {
/* n と答えた場合の処理をここに書く */
}
:
:
文字列の長さは文字列の先頭から '\0'
が現れるまでの
文字数を数えれば良い。これを行なうのが strlen
関数である。
これも
#include <string.h>
が必要である。
strlen
- 文字列の長さを返す
文字列を持つchar型配列を渡して以下のように利用する。
strlen(文字列)
与えた「文字列」の長さを整数で返す。
strlen
は、文字列のおしりの位置を探す場合にも使える。
たとえば、fgets
を使って名前を読み込んだとしよう。
char s[10]; /* 10バイト確保 */ : printf("あなたの名前は?: "); fgets(s, sizeof s, stdin); :
↓
"taro" と入力しリターンを押すと、char型配列には以下のようなデータが 入る。
s | |||||||||
---|---|---|---|---|---|---|---|---|---|
s[0] | s[1] | s[2] | s[3] | s[4] | s[5] | s[6] | s[7] | s[8] | s[9] |
't' | 'a' | 'r' | 'o' | '\n' |
'\0' | ??? | ??? | ??? | ??? |
116 | 97 | 114 | 111 | 10 |
0 | ??? | ??? | ??? | ??? |
ここでRubyの達人は思うだろう。
最後の改行文字、要らねー
このような場合は、
文字列の最後の文字が改行文字か調べて、もしそうなら削る
という処理を書けば良い。このときにstrlen
が役に立つ。
"taro\n"
という文字列の長さは5である。文字列の最後の
'\n'
の位置(添字)は4である。つまり、
strlen
で帰って来た結果から1を引くと必ず 文字列の最後の文字の位置(添字)になる
という規則を利用すると、Rubyのchomp
に相当する
処理は以下のように書ける。
char s[10]; printf("あなたの名前は?: "); fgets(s, sizeof s, stdin); if ('\n' == s[strlen(s)-1]) { s[strlen(s)-1] = '\0'; }
文字列 s
は配列 であるから、その個別の要素を
指定するときはRubyと同様 s[添字]
とする。その添字に
文字列の長さ-1 を指定すると文字列の最後の文字 を
指定することになる。
strlen(s) → 文字列sの長さ strlen(s)-1 → 文字列sの長さ-1 s[x] → 文字列sのx番目の配列要素 ↓ ∴ s[strlen(s)-1] は 文字列の最後の文字を指し示す配列要素
そして、本来改行文字 '\n'
が入っていた場所に
文字列の終端を表す '\0'
(文字コード0の文字) を
代入することで、めでたく末尾の改行文字を削除できる。