納得C言語!
文字列の扱い
1.変数のスコープとは
第4回でこのようなプログラム例を書きましたね。覚えていますか?
例)第4回で出てきた例題です
第4回では"おまじない"だと思ってもらった下線部のchar str[256];ですが、この回でようやく"おまじない"の正体について説明します。
1.文字列とは
文字列とは、名前の通り文字を複数並べた列の事をいいます。
C言語では文字型と呼ばれるデータ型(char)がありましたね。
char型の変数を複数並べることで文字列を表現することが出来ますが、どのようにすればよいのでしょうか?
正解は「char型の変数を配列として宣言する」です。
プログラム例でもchar型の変数strを256個の配列で宣言していますよね。
こうすることによって文字列をC言語で表現することが出来ます。
2.文字列の扱い方
(1)文字列の宣言
文字列を宣言するには次の方法があります。
このように宣言することによって、文字列の変数を宣言することができます。
文字列の長さは宣言した変数で扱うことができるメモリ量です。
文字列ではない普通のchar型の宣言は1バイト分のメモリを確保します。
256個のchar型の配列を宣言しているので、全部で256バイト分のメモリを確保できます。
その時は256バイト分のデータ(文字列)を代入することができますが、文字列の最後には「ここで文字列は終わりですよ」と言うのを知らせる「EOS」と言うものをつけなければなりません。
「EOS」とは「End Of String」の頭文字を取ったものです。
プログラム上で「EOS」を示すにはヌル文字「\0」という制御文字を使って表します。
配列1つ1つに文字を代入していく時に、最後に必ずヌル文字が必要となります。
ヌル文字がないと文字列の終了が判断できなくなり、プログラムの結果がおかしくなることがありますので気をつけてください。
その他にも以下の方法もあります。
こうすると、文字配列にあらかじめ初期値を与えることができます。
[ ]の中が空白になっているので、初期値の文字列の長さによって適切なメモリの量が与えられます。
この場合は自動的に文字列の最後にヌル文字が挿入されるので「\0」を記述する必要はありません。
例題1 文字列の表示
#include <stdio.h> // おまじない
int main()
{
//文字列型配列str[]を宣言し
//"ABCDE"を代入
char str[]="ABCDE";
//%sで文字列を表示
printf("%s\n",str);
return 0;
}
結果
例題1ではstr[]に"ABCDE"を代入しています。
それを図に表すとこのようになります。
A「上の図の最後の"\0"って?」
B「\0は"ヌル文字"って意味だよ。ここでは、文字列の終わりを表しているんだ」
A「必要あるの?」
B「例えば、str[256]で配列を256個用意したけど、全部使わないこともあるんだ。"\0"を使わないで表示したら全部表示されてしまうよね?」
A「あっ、だから"\0"で終わりを教える必要があるんだね!」
(2)文字列の入力
文字列を入力するにはscanf( )とgets( )を使います。
scanf( )を使う場合は指定文字列を「%s」にします。
変数のアドレスは文字列を格納したい文字列配列の変数を記述してください。
この場合、実数を扱う時みたいに変数の前に「&」を入力する必要はありません。
gets( )を使う場合は( )の中に文字列を格納したい文字列配列の変数を記述するだけで大丈夫です。
そういえば、scanf( )とgets( )の違いについて第4回の注意で軽く触れましたが、ここでも再度説明します。
scanf( )とgets( )の違いは・・・
と言うことで、復帰/改行以外の空白類文字を文字列として扱いたい場合はgets( )を使ってください。
scanf( )を使って「This is a pen」と入力しても「This」までしか出力されませんので注意しましょう。
空白を文字列として扱わない場合は両方とも使えますが、どちらかを使うかは皆さんのご自由です。
使いやすいと思ったものを使う方がいいでしょう。
(3)文字列の出力
文字列を出力させるにはprintf( )とputs( )を使います。
printf( )を使う場合は" "で囲まれている変換指定文字列で「%s」にしてください。
エスケープシーケンス(\nなど)は各自お好みで。
変数は表示したい文字列の変数を記述してください。
この部分は実数を扱う時と全く変わりありません。
puts( )を使う場合は( )の中に表示したい文字の変数を記述するだけです。
printf( )かputs( )を使うかは皆さんにお任せします。
使いやすいと思ったものを使う方がいいでしょう。
3.文字列を扱う関数
それでは文字列に文字を代入するにはどうすればいいのでしょうか?
char str[256];
str = "あいうえお";
とか
char str1[256] = "あいうえお";
char str2[256];
str2 = str1;
のようにすればいいと思っている方もいらっしゃるでしょうが、これは大きな間違いです。
データ型が実数のように「a = 3」や「a = b」という方法は文字列には使えないのです。
それではどうすればよいのでしょうか?
そんな時には文字列処理関数というものを使います。
C言語のコンパイラにはよく使われる機能はオブジェクトライブラリとして提供されています。
皆さんが何気なく使っているprintf( )やscanf( )もオブジェクトライブラリとして提供されているのです。
なお、文字列処理関数を使うには「#include <string.h>」を使います。
これも「#include <stdio.h>」と同じように、文字列処理関数を扱う際の"おまじない"と覚えてください。
strcat(a, b)
strcat( )関数は2つの文字列を連結する関数です。
strcat(a, b)は文字列aに文字列bを連結するという意味になります。
文字列bのところは" "でくくった文字列も記述できます。
例題2 strcat( )関数
#include <stdio.h>
#include <string.h>
int main()
{
//30個文の配列str1,str2,str3を宣言
//初期値としてAAAAAA,BBBBBB,CCCCCCを代入
char str1[30]="AAAAAA";
char str2[30]="BBBBBB";
char str3[30]="CCCCCC";
//str2の中身"BBBBBB"を
//str1の後ろに連結
strcat(str1,str2);
printf("%s\n",str1);
//str3の中身"CCCCCC"を
//str2の後ろに連結
strcat(str2,str3);
printf("%s\n",str2);
//str3の内容"CCCCCC"を
//str1の後ろに連結
//このときstr1は"AAAAAABBBBBB"となっている
strcat(str1,str3);
printf("%s\n",str1);
return 0;
}
結果
連結されて表示されましたね。
(注意)
文字列型配列の宣言のときstr[ ]="AAAAAA"と書くと、自動的にstr[7]="AAAAAA"となってしまいます。
この状態で連結を行おうとすると、おかしな動作をしてしまうので、宣言するときには大きめの配列を用意してあげましょう。
strcpy(a, b)
strcpy( )関数は文字列をコピーする関数です。
strcpy(a, b)は文字列bを文字列aにコピーするという意味になります。
念のため、文字列aは文字列bより大きめにメモリを確保してください。
文字列bのところは" "でくくった文字列も記述できます。
例題3 strcpy( )関数
#include <stdio.h>
#include <string.h>
int main()
{
//30個文の配列str1,str2,str3を宣言
//初期値としてAAAAAA,BBBBBB,CCCCCCを代入
char str1[30]="AAAAAA";
char str2[30]="BBBBBB";
char str3[30]="CCCCCC";
printf("%s\n",str1);
//str2の中身"BBBBBB"を
//str1へコピー
strcpy(str1,str2);
printf("%s\n",str1);
//str3の中身"CCCCCC"を
//str2へコピー
strcpy(str2,str3);
printf("%s\n",str2);
//str1の中身"BBBBBB"を
//str3へコピー
strcpy(str3,str1);
printf("%s\n",str3);
return 0;
}
結果
それぞれの文字列がコピーされてますね。
ここで気になるのは、3個目のstrcpy( )でstr1の中身をコピーしてるのにstr2の中身"BBBBBB"がコピーされてますね。
それは、1番目のstrcpy( )でstr1の内容を"BBBBBB"に書き換えているからです。
また、strcat( )と同様に配列の個数を大きめに取ってあげましょう。
今回はすべての配列が同じ個数だったので省いてみました。
strcmp(a, b)
strcmp( )関数は2つの文字列の大きさを比較する関数です。
strcmp(a, b)は文字列aと文字列bの大きさを比較するという意味になります。
大小関係は辞書列順で判断されます。例えば、「a」と「z」では「z」のほうが大きくなります。
返り値は
・ a>bの場合は正数
・ a=bの場合は0
・ a<bの場合は負数
となります。
文字列bのところは" "でくくった文字列も記述できます。
例題4 strcmp( )関数
#include <stdio.h>
#include <string.h>
int main()
{
//文字列型配列str1〜str7の宣言
//初期値として、それぞれ代入
char str1[]="ABCDE";
char str2[]="CDEFG";
char str3[]="FEHIJ";
char str4[]="A";
char str5[]="S";
char str6[]="ABCDF";
char str7[]="ABCDE";
//判定用int型変数iの宣言と初期化
int i=0;
//str1とstr1の比較を行い、iに代入
i = strcmp(str1, str1);
printf("結果(%5s,%5s)=%2d\n", str1, str1, i);
//str1とstr2の比較を行い、iに代入
i = strcmp(str1, str2);
printf("結果(%5s,%5s)=%2d\n", str1, str2, i);
//str2とstr3の比較を行い、iに代入
i = strcmp(str2, str3);
printf("結果(%5s,%5s)=%2d\n", str2, str3, i);
//str2とstr2の比較を行い、iに代入
i = strcmp(str2, str2);
printf("結果(%5s,%5s)=%2d\n", str2, str2, i);
//str4とstr2の比較を行い、iに代入
i = strcmp(str4, str2);
printf("結果(%5s,%5s)=%2d\n", str4, str2, i);
//str4とstr5の比較を行い、iに代入
printf("結果(%5s,%5s)=%2d\n", str4, str5, i);
//str5とstr4の比較を行い、iに代入
i = strcmp(str5, str4);
printf("結果(%5s,%5s)=%2d\n", str5, str4, i);
//str6とstr7の比較を行い、iに代入
i = strcmp(str6, str7);
printf("結果(%5s,%5s)=%2d\n", str6, str7, i);
//str7とstr6の比較を行い、iに代入
i = strcmp(str7, str6);
printf("結果(%5s,%5s)=%2d\n", str7, str6, i);
return 0;
}
結果
負数や正数が戻り値として出ていますが、実際に使う際は
・ 0なら一致している
・ 0以外なら一致していない
というふうに使います。
strlen(a)
strlen( )関数は文字列の長さを取得する関数です。
strlen(a)は文字列aの長さを取得するという意味になります。
文字列aのところは" "でくくった文字列も記述できます。
例題5 strlen( )関数
#include <stdio.h>
#include <string.h>
int main()
{
//文字列の長さを格納するint型変数lenを宣言
int len;
//文字列を格納する文字列型配列strを宣言
char str[256];
//strに文字を代入
printf("半角英数字を入力してください\n");
scanf("%s",str);
//strに格納されている文字列の長さを測り、lenに代入
len = strlen(str);
printf("入力文字[%s]は%d文字です\n",str,len);
return 0;
}
結果
文字列「tokei」は5文字なので、ちゃんと表示されてますね。
このとき日本語を入力してみてください。「Alt+半角/全角」で日本語入力モードになります。
おそらく文字数を倍にした数字が表示されると思います。
それは、半角英数字は1バイト、日本語は2バイトで1文字を表現しているからです。
4.練習問題
(1)文字列に自分の名前をローマ字で代入して表示してみよう。
(2)(1)で自分の名前の長さの文字列の長さを表示してみよう。