文字列操作

Rubyではとても手軽にできた、「文字列同士の大小比較」や 「改行文字の切り取り(chomp)」も、C言語ではとてもややこしい。

文字列の構造

C言語で文字列の操作をするときには、C言語の文字列がどんな構造になって いるかをつねに考えていないとできないと言っても過言ではない。 先週学習したことを繰り返そう。

これを終始意識していないとすぐに理解の限界がやってくる。 復習しよう。

char s[] = "Hello";

というCの文があったとき、コンピュータ内部では次のような配列が 作られる。

s
s[0]s[1]s[2]s[3] s[4]s[5]
'H''e''l''l''o' '\0'
72101108108111 0
  1. C言語では文字をシングルクォート(')で括ったものは、その文字の 文字コードを意味する。
    例: 'A' == Aの文字コード == 65

  2. ある文字の文字コードを知りたいときはEmacsで知りたい文字の上に カーソルを合わせて C-x = をタイプする。たとえば、 Aの文字に合わせて、C-x = すると、

    Char: A (0101, 65, 0x41) point=1 of 46 (0%) column 0

    と出る。括弧内の数値(0101,65,0x41)が文字コードで、それぞれ 8進数、10進数、16進数表記となっている。

  3. 参考: ASCIIコード表

C言語プログラムで文字列 "Hello" が出てきたら

  1. char型配列があるんだな
  2. 見かけは5文字だがおしりに '\0' が付いているんだぜ
  3. だから配列は6個なんだ

ということを瞬時に思い浮かべるようにする。

文字列の比較

Ruby言語では文字列の大小関係の比較は数値と同様 == <= >=を使えば良かった。C言語では文字列専用の比較関数である strcmp を使う。

実際にstrcmp を使って文字列の比較をしてみよう。

文字列の長さ

文字列の長さは文字列の先頭から '\0' が現れるまでの 文字数を数えれば良い。これを行なうのが strlen 関数である。 これも

#include <string.h>

が必要である。

strlenは、文字列のおしりの位置を探す場合にも使える。

たとえば、fgetsを使って名前を読み込んだとしよう。

char s[10];  /* 10バイト確保 */
  :
printf("あなたの名前は?: ");
fgets(s, sizeof s, stdin);
  :

あなたの名前は?: taro

"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'????????????
1169711411110 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の文字) を 代入することで、めでたく末尾の改行文字を削除できる。


目次