文字列

プログラミングBのページへ戻る

文字

 C言語では char 型(文字型)(char は character の略)というデータ型がある。これは1バイトのメモリサイズをもつデータ型である。コンピュータのメモリアクセスは,1バイトが最小の単位であるから,char 型は最小のメモリサイズを持つ型であると言える。 では,char 型を用いた簡単なプログラムを見てみよう。

#include <stdio.h>

int main()
{
    char x;             /* char型変数 x を宣言 */

    x = 'a';            /* x に文字'a'を代入 */  
    printf("%c\n", x);  /* %c は文字を1文字表示する書式指定 */
    return 0;
}
a

 このプログラムの x = 'a' という代入文の右辺にある 'a' は, 「aという文字」を表す char 型定数である。 このように,char 型定数は1文字をシングルクォーテーション「'」で括ったもので表される。 ここでの1文字とはアルファベットや数字や記号などの1バイト文字,いわゆる半角文字と言われる文字である。日本語(漢字やひらがなやカタカナ)を表す2バイト文字,いわゆる全角文字は「'あ'」のように「'」で括っても char 型定数とはならない。

 たとえば, printf("Hello\n"); というプログラムにある「\n 」は, 「改行」を意味する1文字を表している。 このような「\」で始まる2文字で1文字を表したものを,エスケープシーケンスあるいは特殊文字と呼ぶ。よく使われるエスケープシーケンスには次のものがある。

記号意味
\''
\""
\\\
\tタブ
\n改行
\0ヌル文字

'」という文字は文字定数を表すために使用されているので,「'」自身を意味する文字は「\'」と表すことになっている。したがって,「'」を表す文字定数はこれを「'」で括った「'\''」である。

"」という文字は文字列を表すために使用されているので,「"」自身を意味する文字は「\"」と表すことになっている。

\」という文字は特殊文字を表すために使用されているので,「\」自身を意味する文字は「\\」と表すことになっている。

表の最後にあるヌル文字「\0」は,次節で説明する文字列の終端を表す文字として使用される。


 char 型は文字を表す型ではあるが,その本質は1バイトで表された整数(-128〜127)を表す型である。したがって, 'a' などの文字定数も整数としての意味を持つ。それを次のプログラムを実行して確認しよう。(%d は整数値を10進数で表示することを指定する)

#include <stdio.h>

int main()
{
    printf("%d %d %d %d %d %d %d %d\n", 
           'a', 'b', 'c', '1', '2', '3', '\n', '\0');
    return 0;
}
97 98 99 49 50 51 10 0

このプログラムは,a, b, c, 1, 2, 3, 改行文字,ヌル文字という文字がそれぞれ持つ整数値を表示する。その結果を見て,a, b, c の数値,1, 2, 3 の数値が一つずつ増えていることと,ヌル文字(\0)の数値が0であることに注意しておこう。

 次のプログラムは 'a' から 'z' までの文字とその数値とを表示する。

#include <stdio.h>

int main()
{
    char c;
    
    for (c = 'a'; c <= 'z'; c++) {
        printf("%4c%4d", c, c);   /* %4cは文字を4桁で表示(空白が3個入る)
                                     %4dは整数値を4桁で表示(左側には空白が入る)*/
    }
    printf("\n");
    return 0;
}
   a  97   b  98   c  99   d 100   e 101   f 102   g 103   h 104   i 105   j 106
   k 107   l 108   m 109   n 110   o 111   p 112   q 113   r 114   s 115   t 116 
   u 117   v 118   w 119   x 120   y 121   z 122

アスキーコード

 各文字にどのような数値が割り振られているかは,システムによって異なる場合があるが, 現在のコンピュータではほとんどの場合,次のようなアスキーコード (ASCII Code) と呼ばれる数値が割り振られている。

アスキーコード表
00 10 20 30 40 50 60 70
00 /0 ヌル文字 空白 0 @ P ` p
01 ! 1 A Q a q
02 " 2 B R b r
03 # 3 C S c s
04 $ 4 D T d t
05 % 5 E U e u
06 & 6 F V f v
07 /a アラームベル ' 7 G W g w
08 /b バックスペース ( 8 H X h x
09 /t 水平タブ ) 9 I Y i y
0A /n 改行 * : J Z j z
0B /v 垂直タブ + ; K [ k {
0C /f 改ページ , < L \ l |
0D /r 行先頭へ復帰 - = M ] m }
0E . > N ^ n ~
0F / ? O _ o

各文字に割り振られた数値を16進数で表したときの 1桁目と2桁目で分類して表にしている。
左にある数値が1桁目で,上段にある数値は2桁目である。
たとえば,K という文字は 0B の行の 40 の列にあるので,0B+40=4B すなわち10進数でいうと 16*4+11=75 という数値が割り振られている。


ページ先頭に戻る

文字列

 C言語には文字列型という型はない。文字列は,char 型の配列で表される。ただし,単なる配列ではなく,「文字列の最後には終端を表す文字 '\0'(ヌル文字)がついている」という約束の下に取り扱われる文字配列である。文字列の最後に付けられたヌル文字を終端文字と呼ぶ。

たとえば,次のように宣言された文字配列 s があったとする。

  char s[10];

その中身が次のようになっていたとする。

s[0]s[1]s[2]s[3]s[4]s[5]s[6]s[7]s[8]s[9]
'K''y''o''s''a''n''d''a''i''\0'

このとき,この文字配列 s が表す文字列は "Kyosandai" である.

また,次のようになっているとき,

s[0]s[1]s[2]s[3]s[4]s[5]s[6]s[7]s[8]s[9]
'K''y''o''s''a''n''\0''a''b''c'

この場合,s"Kyosan" という文字列を表している。終端文字 '\0' の後ろの要素の値は,s が表す文字列には関係ない。

 "Kyosan" の文字数は6文字であるからといって, char s[6]; と宣言された配列に,次のように文字を入れただけでは,終端文字がないので "Kyosan" という文字列にはならない。

s[0]s[1]s[2]s[3]s[4]s[5]
'K''y''o''s''a''n'

したがって,"Kyosan" という長さが6の文字列を表すためには,最低でも長さ7の文字型配列が必要となる。

 長さ n の文字列を表すには,長さ n+1 の文字配列が必要 ということを,強調しておく。

ページ先頭に戻る

文字列リテラル

 プログラムソースコードの中に,ダブルクォーテーション「"」で括られた文字列がある場合, これを文字列リテラル(文字列定数)という(リテラルとは,書いてある文字そのものという意味)。

たとえば,printf("Kyosan\n"); と書いてあれば, この "Kyosan\n" が文字列リテラルである。 文字列リテラル内の文字は,「\」で始まる特殊文字を除いて,書いてある文字そのままの文字列を表す。

 プログラム実行時には,文字列リテラルは,メモリ上のどこかの領域に確保された文字配列に収納されている,終端文字 '\0' のついた文字列となる。さきほどの "Kyosan\n" の例の場合,それは次のような内容の文字配列である。

'K''y''o''s''a''n''\n''\0'

 文字列リテラルの実体は文字配列ではあるが,その要素の値を読み出すことはできても,要素に値を代入することはできない(できるかも知れないが,しない方がよい)。

ページ先頭に戻る

文字列の初期化

文字配列を宣言すると同時に 次のように初期値を並べて与えて,初期化することができる。

  char s[7] = {'K', 'y', 'o', 's', 'a', 'n', '\0'};

これと同様のことを,文字列リテラルを用いてより簡単に次のように書くこともできる。

  char s[7] = "Kyosan";

この初期化により,文字配列 s の要素が次のように初期化される。

s[0]s[1]s[2]s[3]s[4]s[5]s[6]
'K''y''o''s''a''n''\0'

 注意すべき点は,"Kyosan" という文字列は終端文字を含めて7個の文字で表されるので,次のようにしては配列の要素数が足りないということである。

  char s[6] = "Kyosan";

 初期値として与える文字列の長さを数えることが面倒なときには,配列の要素数を指定せず,次のようにすればよい。

  char s[] = "Kyosan";

コンパイラが "Kyosan" の文字数を数えて, それにふさわしい要素数の配列を宣言し初期化してくれる。すなわち, 上のものは次のものと同等である。

  char s[7] = "Kyosan";

配列のサイズよりも定義文字列の長さが少なくてもよいが, その場合は,残りの要素は初期化されない.

  char s[10] = "Kyosan";

s[0]s[1]s[2]s[3]s[4]s[5]s[6]s[7]s[8]s[9]
'K''y''o''s''a''n''\0'

ページ先頭に戻る

文字列の出力(画面表示)

文字列を出力(画面に表示)するには,標準ライブラリ関数 printf を用いる。 第1引数の書式指定文字列に文字列を指定する %s を書き,第2引数以降に表示したい文字列を与えればよい。

#include <stdio.h>

int main()
{
    char str1[] = "Kyoto";
    char str2[] = "Sangyo";
    char str3[] = "University";

    printf("%s %s %s\n", str1, str2, str3);
    return 0;
}
Kyoto Sangyo University

ページ先頭に戻る

サンプルプログラムと演習問題

 文字列を扱ういくつかのサンプルプログラムを作ってみよう。

サンプルプログラム1

 次は,文字列 str にある文字数(終端文字は含まない)を数えるプログラムである。

#include <stdio.h>

int main()
{
    char str[12] = "Kyosandai";
    int n;
    
    n = 0;
    while (str[n] != '\0') {  /* str[n] が終端文字(ヌル文字 '\0')でない間繰り返す */
        n++;
    }
    printf("Length of \"%s\" is %d.\n", str, n);
    return 0;
}
Length of "Kyosandai" is 9.

 プログラムは,まず始めに n の値を 0 に初期化して,次の while 文で,str[n] の値が終端文字(ヌル文字 '\0')でない間, n1 ずつ増やし続ける。したがって,この while 文は文字列 str の先頭から終端文字までに何文字あるかを変数 n に数えることになる。そして,while 文が終了したとき,n の値が文字列の文字数となっている。

また,この while 文の働きを次のように解釈することもできる。 「この while 文は str[n] != '\0' であるとき回り続ける。言い換えると,while 文が終了したとき str[n] == '\0' となっている。すなわち,この while 文は終了文字 '\0' が何番目の要素にあるかを探しているのである。」

 配列の添字が0から始まることに注意すると,配列 str の要素の値は次のようになっている。

s[0]s[1]s[2]s[3]s[4]s[5]s[6]s[7]s[8]s[9]s[10]s[11]
'K''y''o''s''a''n''d''a''i''\0'  

それを見ると,終端文字は要素 str[9] にあり,その添字 9 がこの文字列に含まれる終端文字以外の文字数 9 に等しいことが分かる。

サンプルプログラム2

 次のプログラムは, 文字配列 str1 にある文字列を,文字配列 str2 に複写する。

#include <stdio.h>

int main()
{
    char str1[30] = "Kyoto Sangyo University";
    char str2[30];
    int n;

    n = 0;
    while (str1[n] != '\0') {
        str2[n] = str1[n];
        n++;
    }
    str2[n] = '\0';
    printf("%s\n%s\n", str1, str2);
    return 0;
}
Kyoto Sangyo University
Kyoto Sangyo University

 プログラムは,まず始めに n の値を 0 に初期化して,次の while 文で,str[n] の値が終端文字(ヌル文字 '\0')でない間,str2n 番目の要素に str1n 番目の要素の値を代入してから n1 だけ増やす,という作業を行い続ける。この while 文で str1 にある文字列の全ての文字(終端文字を含まない)が文字配列 str2 に複写される。

 しかし,これでは str2 に終端文字がつけられていない。str2 に終端文字をつけるために,どこにつければ良いかを考えなければならいが,先ほどのサンプルプログラム1で見たとおり,この while 文が終了した時点で要素 str1[n] の値が終端文字 '\0' となっているので, str2 にも n 番目の要素 str2[n] に終端文字を入れれば良いことがわかる。それゆえ,while 文の終了後に str2[n] = '\0'; という代入文がある。

演習問題1

次のソースコードに書き加えて,文字配列 str2 にある文字列を,文字配列 str1 にある文字列の末尾に付け加えて,最後にstr1 を表示するプログラムを作れ。

#include <stdio.h>

int main()
{
    char str1[30] = "Kyoto San";
    char str2[20] = "gyo University";
 
    printf("%s\n", str1);
    return 0;
}

実行結果が次のようになればよい。

Kyoto Sangyo University

解答

ページ先頭に戻る

文字列のための標準ライブラリ関数

 標準ライブラリ関数には,文字列を扱うための関数が多数用意されている。 それらの中でよく使うものを載せておく. 授業中には全部は解説しないので,使用するときに読み返して自分で理解するように.

 その前に,これらの関数を使う際には,次のインクルード命令をプログラムの先頭部分に書いておく必要があることを憶えておこう。

#include <string.h>

strlen 文字列長

 strlen(str) は,文字列 str の長さ(文字数)を整数値で返す。

#include <string.h>

int main()
{
    char str[30] = "Kyoto Sangyo University";
    int len;
    
    len = strlen(str);
    printf("Length of \"%s\" is %d.\n", str, len);
}
Length of "Kyoto Sangyou University" is 23.

strcpy 文字列の複写

 strcpy(target, source) は,文字配列 target へ, 文字列 source の内容を複写し,最後に終端文字を付ける

#include <string.h>

int main()
{
    char str1[30];
    char str2[30] = "Kyoto Sangyo University";
    
    strcpy(str1, str2);
    printf("%s\n", str1);
}
Kyoto Sangyo University

strncpy 文字列の複写(文字数制限付き)

strncpy(target, source, n) は,文字配列 target へ, 文字列 source の内容を複写する。ただし,複写する文字数は最大 n 個であり,終端文字は複写されない。

#include <string.h>

int main()
{
    char str1[10];
    char str2[30] = "Kyoto Sangyo University";
    
    strncpy(str1, str2, 9);
    str1[9] = '\0';          /* 終端文字を付ける */
    printf("%s\n", str1);
}

 このプログラムは "Kyoto Sangyo University" の最初の9文字だけを複写する。9文字を収納するためには,終端文字を含めて文字配列の長さは 10 必要であるので, char str1[10]; と宣言されている。

Kyoto San

 source の文字数が n-1 以下だと,終端文字も複写される。しかし, strncpy を用いるときは,そのことは忘れて,常に終端文字を後から付けるようにしたほうがいい。

strcat 文字列の連結複写

 strcat(target, source) は文字列 target の最後(終端文字 '\0' のところから)に, 文字列 source の内容を複写して付け加えて,最後に終端文字を付加する。

#include <string.h>

int main()
{
    char str1[30] = "Kyoto San";
    char str2[30] = "gyo University";
    
    strcat(str1, str2);
    printf("%s\n", str1);
}
Kyoto Sangyo University

strncat 文字列の連結複写(文字数制限付き)

 strncat(target, source, n) は文字列 target の最後(終端文字 '\0' のところから)に, 文字列 source の内容を,最大 n 文字だけ複写して付け加えて,最後に終端文字を付加する。

#include <string.h>

int main()
{
    char str1[30] = "Kyoto San";
    char str2[30] = "gyo University";
    
    strncat(str1, str2, 8);
    printf("%s\n", str1);
}
Kyoto Sangyo Univ

strcmp 文字列の比較

 strcmp(str1, str2) は文字列 str1 と 文字列 str2 とを辞書式順序で比較して,その結果を整数値で返す。 文字列の内容が全く等しいときには 0 を, str1 が小さいときには負の値を, str1 が大きいときには正の値を返す。

#include <string.h>

int main()
{
    char str1[30] = "Kyosandai";
    char str2[30] = "Kyoto Sangyo Daigaku";
    int cmp;

    cmp = strcmp(str1, str2);
    printf("%d\n", cmp);
}
-1

 文字の比較は,それらに割り振られた数値 (アスキーコード表) の大きさで比較が行われる。 二つの文字列中の文字を先頭文字から比較していき, 最初に異なる文字に出会ったときのアスキーコードの大小が,文字列の大小となる. (片方の文字列が他方の文字列の先頭部分に含まれるときには,長い文字列の方が大きくなる). たとえば,つぎのようになる.
strcmp("abc", "abc") == 0
strcmp("abcd", "abdc") == -1
strcmp("abcd", "abcde") == -1

文字列の内容が一致しているかどうかの判定にも strcmp は用いられる.

  if (strcmp(str1, str2) == 0) {
    printf("一致");
  } else {
    printf("不一致");
  }

strchr 文字列中の文字の検索

 strchr(str, chr) は文字列 str の中で 文字 chr を探し,最初に見つかった位置をポインタで返す。

#include <string.h>

int main()
{
    char str[30] = "Kyoto Sangyo Daigaku";
    char * p;

    p = strchr(str, 'a');
    printf("%s\n", p);
}
angyo Daigaku

 ポインタについては,ポインタのページを参照すること。

strstr 文字列中の文字列の検索

 strstr(str, keyword) は文字列 str の中で 文字列 keyword に一致する部分を探し,最初に見つかった位置をポインタで返す。

#include <string.h>

int main()
{
    char str[30] = "Kyoto Sangyo Daigaku";
    char *p;

    p = strstr(str, "ai");
    printf("%s\n", p);
}
aigaku

 ポインタについては,ポインタのページを参照すること。

演習問題2

標準ライブラリ関数を用いて,キーボードから文字列を読み込み,各行の文字数を出力し, "end" が入力されたら終了するプログラムを作れ.

#include ???

int main()
{
  char str[1000];          十分大きな文字配列を用意
  int len;
  
  do {
    scanf("%s", str);      文字列を読み込んで str にしまう
    len = ??? ;            文字列長を求める(ライブラリ関数)
    printf("%d\n", len);
  } while ( ??? )          str が "end" に一致してなければ繰り返し(ライブラリ関数)

  return 0;
}
abcde
5
Kyosandai
9
end
3

ヒント:文字列内容の一致の判定には strcmp を用いる.

解答

ページ先頭に戻る

レポート課題

次のソースコードに書き加えて,文字配列 str1 にある文字列を逆順にした文字列を,文字配列 str2 に複写して,最後に文字列 str2 を表示するプログラムを作れ。

#include <stdio.h>

int main()
{
    char str1[20] = "Kyosandai";
    char str2[20];

    printf("%s\n", str2);
    return 0;
}

実行結果が次のようになればよい。

iadnasoyK

ページ先頭に戻る