C言語入門 第9回 文字・文字列の使い方
2014年10月より個人の方を対象に、Study C無料提供を開始しました。
C言語を勉強中の方は、学習・教育に最適なC言語インタープリタのStudy Cを使ってみてください(個人の方は無料です)。
大学・高専・高校などの教育機関での採用実績も多数あるロングセラー商品Study Cが、個人向けに無料提供を始めました。
インタープリタの手軽さに加え、ゲームや3Dタートルグラフィックで楽しく勉強したりと、C言語の学習を強力にサポートします。
また、このようなボタンの用意されているページでは、掲載しているプログラムをStudy Cに直接ロードし実行したりすることができます。
Study C無料利用についての詳細は、このページを参照してください。
コンピュータは0と1の組合せのデータや機械語しか処理することができません。intで宣言した変数の内容や10進数の定数などは、すべて0と1の組合せである2進数としてコンピュータ内に蓄えられています。10進数や16進数も2進数に変換されて格納しています。 たとえば、10進数で17は次のように16進数や2進数で表現できます。
10進数 16進数 2進数 17 11 10001
コンピュータがこのうちのどの形式で数を記憶していようと、値はみな同じものになります。 しかし、コンピュータでは文字も2進数で表現しなければ取り扱うことができません。 このため、英文字や記号に通し番号を付け、数値と同じように扱うことにしました。 この通し番号の付け方がコンピュータによって異なっていると不便なことが多いので、ASCII(アスキー)と呼ばれるコード体系が作られました。 このコード体系では16進数の0x20から0x7Eまでの値に(10進数では32から126)英文字や記号を割り当てています。
***参考***
C言語では16進数の数を0x~という形式で表現します。また、16進数では1つの位が0~15まであるので10~15をA~Fで表現します。
たとえば、0x7Eは7×16+14で10進数の126に相当します。
実際のアスキーコードと対応する文字の関係を表に示します。
10進 16進 文字10進 16進 文字10進 16進 文字 ------------------------------------------------ 32 0x20 64 0x40 @ 96 0x60 ` 33 0x21 ! 65 0x41 A 97 0x61 a 34 0x22 " 66 0x42 B 98 0x62 b 35 0x23 # 67 0x43 C 99 0x63 c 36 0x24 $ 68 0x44 D 100 0x64 d 37 0x25 % 69 0x45 E 101 0x65 e 38 0x26 & 70 0x46 F 102 0x66 f 39 0x27 ' 71 0x47 G 103 0x67 g 40 0x28 ( 72 0x48 H 104 0x68 h 41 0x29 ) 73 0x49 I 105 0x69 i 42 0x2a * 74 0x4a J 106 0x6a j 43 0x2b + 75 0x4b K 107 0x6b k 44 0x2c , 76 0x4c L 108 0x6c l 45 0x2d - 77 0x4d M 109 0x6d m 46 0x2e . 78 0x4e N 110 0x6e n 47 0x2f / 79 0x4f O 111 0x6f o 48 0x30 0 80 0x50 P 112 0x70 p 49 0x31 1 81 0x51 Q 113 0x71 q 50 0x32 2 82 0x52 R 114 0x72 r 51 0x33 3 83 0x53 S 115 0x73 s 52 0x34 4 84 0x54 T 116 0x74 t 53 0x35 5 85 0x55 U 117 0x75 u 54 0x36 6 86 0x56 V 118 0x76 v 55 0x37 7 87 0x57 W 119 0x77 w 56 0x38 8 88 0x58 X 120 0x78 x 57 0x39 9 89 0x59 Y 121 0x79 y 58 0x3a : 90 0x5a Z 122 0x7a z 59 0x3b ; 91 0x5b [ 123 0x7b { 60 0x3c < 92 0x5c \ 124 0x7c | 61 0x3d = 93 0x5d ] 125 0x7d } 62 0x3e > 94 0x5e ^ 126 0x7e ~ 63 0x3f ? 95 0x5f _ 127 0x7f
各文字に対する値(アスキーコード)を覚える必要はありません。しかし、AからZに連続した値が割り当てられていてZのほうが大きな値である、という程度は知っていたほうがよいでしょう(a~z、0~9についても同様)。
次に示すプログラムを実行してみましょう。
main() { int i; i = 65; printf("%d\n", i); printf("%c\n", i); }
65 A
このプログラムでは2番目のprintf内に%cという新しい表現が含まれています。
%dは数を表示しますが、%cは指定した数(アスキーコード)に対応する文字(characterの頭文字のCという意味)を表示します。
1番目のprintfは当然65と表示します。また、2番目のprintfはアスキーコードの65に相当する文字であるAを表示します。このプログラムの i = 65;を i = 66;にすればAのかわりにBと表示されるようになります。
ここで、今までprintfでよく使ってきた%dについてもう一度考えてみましょう。
printf("%d", 123);を実行すると、画面には123と数が表示されますが、123はすべてアスキーコード表にある文字の一部に相当しています。実は、%dは指定した数を画面に
表示できるように何文字かの文字(アスキーコード)に変換して、その文字を画面に表示しています。
つまり123という数はprintf内部でアスキーコード49、50、51に変換され、それらの文字が画面に表示されます。
123 ↓ printfがアスキーコードに変換 49、50、51 ↓ 3つの文字を画面に表示
アスキーコードを画面に表示するためにprintfを使ってきましたが、前に述べたようにprintf("%c", アスキーコード);とする必要があります。
C言語では、%cと指定しなくてもアスキーコードを文字として表示する関数putchar()が用意されています。この関数は次の形式で使用します。
putchar(アスキーコード);
putchar関数を使った、次のプログラムを実行してみましょう。
main() { putchar(97); putchar(98); putchar(99); }
abc
このプログラムを実行すると、小文字のabcが画面に表示されます。
今まで文字のアスキーコードを表現するために10進数を使ってきましたが、Cのプログラムではアスキーコードを数字で指定しなくても 'a' 'A' '0'などと記述することができます。
'a'、'b'などを使えば、いちいちアスキーコード表を見てプログラムをつくる必要がなくなります。
main() { int i; i = 'A'; printf("%d\n", i); printf("%c\n", i); }
65 A
iに65を代入している前のプログラムと同じ実行結果となります。
今までの変数の宣言にはint i;のようにint型を使ってきました。しかし、文字データのためにはchar型変数を使用するのが一般的です。
int型は整数値を保持するために使用しますが、前の例では文字(アスキーコード)データを保持するために使用しています。
文字(アスキーコード)も同じ整数なのでint型変数に代入することが可能なため、int型変数を使用してきました。
int型とchar型の違いは保持できる数の大きさにあります。int型の変数は2進数で32桁(一部のコンパイラでは16桁)、char型は2進数で8桁分の数値を記憶することができます。
***参考***
2進数の桁数をビットと呼びます。したがって2進数で8桁であるchar型は8ビットとなります。また、2進数8桁を1バイトと呼びます。2進数で32桁のint型は4バイトに相当します。
2進数での桁数 ビット バイト char 8 8 1 int 32 32 4
これをワインのボトルにたとえてみましょう。charは1/4 bottle(int = 16桁の時、書いたのでそのときはhalf bottleでした)なので場所はとらないが中に入れられる物の量は少なくなります。 それに比べてintはfull bottleなので、場所はとるが中に入れられる物の量は多くなります。
half bottle +-+ 文字データは | | char型変数に + + ちょうど良い | | 大きさ。 | | +---+ char型変数 full bottle +-+ | | + + ←int型変数を文字データ | | として使っていると | | この部分はいつも空の | | ままになってしまう。 | | | | | | | | | | | | | | | | ←文字データはこの程度 | | の量しか必要としない。 +---+ int型変数
int型の変数で文字データを保持していても問題はありませんが、コンピュータが記憶できる量は無限ではありません。
不必要に大きなサイズの変数を使うことはメモリ(記憶する部品)の無駄使いでしかありません。
それでは、実際にchar型変数を使ってみましょう。
char型変数はint型と同じように次の形式で宣言します。
char 変数名;
前のプログラムをintからcharに書き換えて実行してみましょう。
main() { char i; i = 'A'; printf("%d\n", i); printf("%c\n", i); }
65 A
このように、変数をintからchar型に変えても同じように動作します。
文字列はいくつかの文字が集まったものを指します。たとえば、3個の文字定数'a''b''c'が連続して存在していればabcという文字列になります。
C言語では文字列を"..."で囲んで表現します。
今までのプログラム例でprintfのカッコ内で使っていたものが文字列(定数)に相当します。たとえばprintf("Hello");の"Hello"の部分が文字列定数です。
文字列でも定数(値を変化させることができないもの)があれば変数(値を変化させることのできるもの)もあるのではないかと考えられますが、C言語には文字列変数は存在しません(BASICなど、他の言語には文字列変数が扱えるものがあります)。
しかし、プログラムを作っていると文字列を変数に格納して一部を変更したりする必要がでてきます。
このため、C言語では文字列変数のかわりに文字型の配列を使用します。文字列のようにデータが連続して格納されている状態は配列と類似しているため、文字型の配列は文字列用の変数として利用されます。
文字列定数と文字型配列と比較してみましょう。
文字列定数"abcd"に相当する文字型配列は、次のように文字型配列に文字を代入することにより作ることができます。
main() { char string[5]; string[0] = 'a'; string[1] = 'b'; string[2] = 'c'; string[3] = 'd'; string[4] = 0; }
最後に数値の0(アスキー文字の'0'ではない)が代入されていることに注意してください。
C言語では、文字列の最後に0をセットしておくことで文字列の最後を認識します。
***参考***
変数 定数 整数 int i; 10 文字 char c; 'x' 文字列 存在しない "xyz"
文字列変数は存在しないため、char s[10];などの文字型配列で代用します。
第8回C言語入門では配列宣言を次のように行っていました。
int data[10];
このように、int型で宣言した配列は整数値を格納するために用いられる整数型配列となります。 一方、文字を格納するための文字型配列はintのかわりにcharを用い、次のように宣言を行います。
char string[10];
この例では、最大9文字(最後の0が必要なので)の文字列を蓄えることができます。 文字列定数"abcd"と前の例の文字型配列stringを例にとって、いろいろ比較してみましょう。
①格納されている内容をprintfで表示させるには 文字列定数 printf("abcd"); 文字型配列 printf(string); 実行結果 : どちらもabcdと表示します。 ②何番目かの文字をputchar(アスキーコードを表示する関数)で表示させるには 文字列定数 putchar("abcd"[2]); 文字型配列 putchar(string[2]); 実行結果 : どちらもcと表示します。 ③どちらも最後には数値の0がセットされています。 文字列定数 printf("%d", "abcd"[4]); 文字型配列 printf("%d", string[4]); 実行結果 : どちらも0と表示します。
このように、文字列定数と文字型配列の使用方法は極めて類似しています。両者の最も大きな違いは、内容を書き換えることができるかどうかという点にあります。 次の2つの例を見てください。
文字列定数 "abcd"[1] = 'B'; 文字型配列 string[1] = 'B';
配列への代入は当然可能ですが、文字列定数への代入はエラーとなります。
***参考***
C言語の古い規格では文字列定数への代入も可能でした。しかし、最近の規格では文字列定数への代入は禁止されているので、STUDY Cもエラーになるように作られています。
C言語では、文字列変数が存在しないため、文字列を操作するための関数が用意されています。よく使用される関数には次のようなものがあります。
文字列の長さを数える strlen(文字列) 2つの文字列を比較する strcmp(文字列1, 文字列2) 文字列のコピー strcpy(コピー先文字列, コピー元文字列) 2つの文字列を結合する strcat(加えられる文字列, 加える文字列)
・strlen()
この関数は文字列の長さを数え、その値を返します。
main() { printf("%d\n", strlen("abcdefg")); printf("%d\n", strlen("123456789")); }
7 9
"abcdefg"は7文字、"123456789"は9文字という結果が返ってきます。
他の関数にもいえることですが、文字列定数として"abcd"などを使用する(前例)かわりに 文字型配列を使用する(次の例)ことも可能です。
main() { char str[10]; str[0] = 'a'; str[1] = 0; printf(str); printf(" = %d\n", strlen(str)); str[1] = 'b'; str[2] = 'c'; str[3] = 0; printf(str); printf(" = %d\n", strlen(str)); }
a = 1 abc = 3
この例では、文字列と文字の長さを表示するために2個のprintfを使用していますが、これは1個にまとめることができます。
printfで使用する%dや%cのかわりに%sを使用します。
%sは、その位置に対応する文字列を埋込み表示します。たとえば、printf("[%s]", "abcd");の表示結果は[abcd]となります。
%sを使用して前の例を書き換えると、次のようになります。
main() { char str[10]; str[0] = 'a'; str[1] = 0; printf("%s = %d\n", str, strlen(str)); str[1] = 'b'; str[2] = 'c'; str[3] = 0; printf("%s = %d", str, strlen(str)); }
a = 1 abc = 3
また、printfと同様にscanfでも%sが使用でき、文字型の配列に入力した文字列を格納することができます。
このとき、数を入力するときに必ず付けていた'&'は不要です。
また、配列のサイズより長い文字列も入力できてしまい、その場合は正常に動作しないので注意してください。
scanfの使用例は次のようになります。
main() { char str[80]; <- scanf使用時はこのサイズを大きめに設定する scanf("%s", str); printf("[%s]", str); }
abc <- ここで何か文字列を入力する [abc]
・strcmp()
この関数は指定した2つの文字列を比較します。strcmp(文字列1, 文字列2)とすると次の結果が返されます。
①文字列1と文字列2がまったく同じ場合は0が返されます。
②文字列1が文字列2より大きい場合は1以上の正の値(実際に返される値は指定した文字列によって異なる)が返されます。
C言語で文字列の大小というのは、1文字目から順番に1文字ずつ比較していって最初の一致しない文字をアスキーコードとして大小比較した結果を指します。
たとえば、"abcd"と"abce"では4文字目のdとeが異なっており、アスキーコードとしてはeのほうが大きいため"abce"のほうが大きい文字列となります。
③文字列1が文字列2より小さい場合は、1以下の負の数が返されます。
main() { char str1[80], str2[80]; printf("Input(1): "); scanf("%s", str1); printf("Input(2): "); scanf("%s", str2); if (strcmp(str1, str2) == 0) { printf("\n%s = %s\n", str1, str2); } if (strcmp(str1, str2) > 0) { printf("\n%s > %s\n", str1, str2); } if (strcmp(str1, str2) < 0) { printf("\n%s < %s\n", str1, str2); } }
Input(1):test1 ここで1つ目の文字列を入力。これ例ではtest1と入力 Input(2):test2 ここで2つ目の文字列を入力。この例ではtest2と入力 test1 < test2
このプログラムはscanf()で2つの文字列を入力し、strcmp()で比較します。
・strcpy()
この関数は、ある文字列を他の文字型配列にコピーするために使用します。
strcpyもstrcmpと同様に2つの文字列を指定しますが、コピー先には文字列定数を指定することはできません。
コピー元の文字列は、当然文字列定数を指定することができます。
strcpyの機能はコピー元の内容をすべてコピー先に書き込むという単純なものですが、この関数はコピー先が何文字分受け入れられるかをまったくチェックしません。
たとえば、char str[5];で4文字分(最後に0をセットする必要があるので)の文字型配列を宣言し、strcpy(str, "abcde");で5文字分のコピーを行ったとします。
STUDY Cでは、strcpyを実行するときにエラーが発生しますが、普通のCコンパイラを使用する場合はエラーを発生せずに無理矢理コピーを行ってしまいます。
そして、あふれた部分のコピーで関係ないデータなどを破壊してしまい、正常に動作しないことがあります。
このため、strcpy()を使用する場合は文字があふれないかをstrlen()などを使って、チェックする必要があります。
main() { char str[20]; str[0] = 'a'; str[1] = 0; printf("1...%s\n", str); strcpy(str, "strcpy"); printf("2...%s\n", str); }
1...a 2...strcpy
この例では最初に文字型配列strへ、"a"を代入してprintf()で表示し、次にstrcpy()を使い"strcpy"を代入しprintf()で表示させています。
・strcat()
この関数は、2つの文字列の結合を行います。
結合した文字列は、最初に指定する文字列へ格納されます。したがってstrcpy()と同様に、格納先の文字列は文字列定数ではなく文字型配列でなければなりません。また、結合した結果が格納できるかもstrlen()を使うなどして調べる必要があることも同じです。
main() { char str[20]; strcpy(str, "str"); printf("1...%s\n", str); strcat(str, "cat"); printf("2...%s\n", str); }
1...str 2...strcat
この例では最初に文字型配列strにstrcpy()で"str"を代入しprintf()で表示させ、次にstrcat()で"str"に"cat"を結合し、その結果をprint()で表示させています。