*:文字とは、'a'や'B'など1文字を示します。また、文字列とは"KINDAI'や"RIKOUGAKUBU"など、複数の文字が連なったものを指します。
後で説明しますが、文字の場合はクオーテーションマーク(')で、文字列の場合はダブルクオーテーションマーク(")で、文字や文字列を囲みます。
文字を扱うには、数値型変数(int,floatなど)と同じように、まず文字型変数宣言子(char)を使って変数宣言をします。次のように宣言します。
char a;
char b;
char c;
宣言した文字型変数に文字を格納するには、クオーテーションマーク(')を使い、以下のようにします。
a = 'A';
b = 'b'; c = a; |
文字型変数aに、文字Aを代入します。
文字型変数bに、文字bを代入します。 文字型変数cに、文字型変数aをコピーします。 |
文字型変数の内容を表示するには、%cを使い次のようにします。
|
|
printf("文字型変数aは、%c\n",a);
printf("文字型変数bは、%c\n",b); printf("文字型変数cは、%c\n",c); |
文字型変数aは、A
文字型変数bは、b 文字型変数cは、A |
キーボードから文字を入力する
前回では、プログラム内で文字型変数に代入しました。今回は、キーボードから文字を入力してみましょう。
といっても、今まで数値型変数に値を入力する方法と全く同じです。以下の例をみてください。
|
|
#include <stdio.h> void main(void) { char input_a,input_b,input_c; printf("3文字入力してください。\n"); scanf("%c %c %c",&input_a,&input_b,&input_c); //文字型変数への入力は%cを使う printf("あなたが入力した文字は、%cと%cと%cです。\n",input_a,input_b,input_c); } |
3つの文字型変数(input_a,input_b,input_c)を宣言し、キーボードよりその3文字に文字を入力しています。
数値型変数にキーボード入力させる時と同じ方法であるため、新しく覚えることはありません。 ただ、scanf関数で文字入力をさせる場合、書式を文字型%cにする必要があります。 関数説明:scanf関数 |
実行結果(キーボードよりx
Y zと入力)
あなたが入力した文字は、xとYとzです。 |
文字配列を使う
上の例は1文字に1つの文字型変数を用意し、代入・出力をしてきました。しかし、これでは、文字数が多くなればなるほどプログラムが長くなり、読み書きが面倒になります。この問題を解決するには、数値型変数でも使った配列を利用します。以下の例を見てください。
|
|
#include <stdio.h> void main(void) { char myName[ ]={'Y','a','m','a','d','a',' ','K','o','u','j','i'}; int nameSize=12; //名前の文字数 int i; for(i = 0;i < nameSize;i++) putchar(myName[ i ]); } |
数値型配列と同じ方法で、文字型配列を宣言します。
例では、宣言と同時に{'Y','a','m','a','d','a',' ','K','o','u','j','i'}で初期化しています。 初期化せず、宣言後に1文字ずつ代入することも可能です。 その後、繰り返し文(for文)を使い文字型配列[0]〜[11]の1文字ずつをputchar関数で出力しています。 空白も1文字であることに注意してください。 注1.配列に関して詳しい説明は、前節を参考にしてください。
関数説明:putchar関数 |
実行結果
Yamada Kouji |
繰り返し文(for文やwhile文)を使えば、文字配列に一連の単語や文をキーボードから入力させることも可能です。しかしそのような場合、次に説明する文字列を使ったほうが便利で、見た目にもきれいなプログラムになります。
よって、1文字を入力・出力させたい場合は文字を使い、一連の単語や文を入力・出力させたい場合は、文字列を使うようにしましょう。
では、文字列の説明に入ります。
以下のプログラムは、前回(9-2のプログラム例)を文字列を用いて書きなおしたものです。まずは、文字と文字列とでは、プログラムの書き方でどのような違いがあるのか見てみましょう。
|
9-3 9-2を文字列で書きなおしたプログラム |
#include <stdio.h> void main(void) { char myName[ ]={'Y','a','m','a','d','a',' ','K','o','u','j','i'}; int nameSize=12; //名前の文字数 int i; for(i = 0;i < nameSize;i++) putchar(myName[ i ]); } |
#include <stdio.h> void main(void) { char myName[ ]={"Yamada Kouji"}; printf("%s",myName); } |
実行結果(9-2,9-3とも同じ結果)
Yamada Kouji |
4行目: 6行目: 補足 : |
文字列のプログラムでは、"Yamada Kouji"となっています。これは、文字配列での'Y','a',' m','a','d','a',' ','K','o','u','j','i','\0'(自動的に\0が付加される)と同じ意味を持ち、 最後の1文字(\0)はヌル文字という文字列の終了を表す文字です。文字列操作では,必ずヌル文 字が必要となります。また\0(見た目は2文字ですが)で、1文字として考えます。ですから、実 際には格納しようとする文字数+1文字(ヌル文字)分の配列要素を用意する必要があります。例 のプログラムでは、9-2が12文字分,9-3では13文字分の配列要素が使われていることになり ます。 後で説明しますが、文字列の場合、myName ="Yamada Kouji"といった代入は使用できません。今回のプログラム では宣言時に初期化していますが、それ以外の格納方法は後に説明することにします。 文字列のプログラムには、繰り返し文(for文)がありません。文字列の場合 scanf関数の書式 に%sを使い 表示したい文字配列の先頭アドレスを指定すると、指定したアドレスに格納されて いる文字からヌル文字まで、1文字ずつ自動的的に表示していきます。ですから、文字数を意識 することなく(繰り返し文を使うことなく)文字列を表示させることができるのです。 では、ヌル文字がなければどうなるのでしょうか?おそらく、ヌル文字がでてくるまで(確保 した配列数を超えてでも)永遠に表示しつづけたり、プログラムの実行を途中で中止したりする と思います。もし、時間と興味があるのなら、故意に間違ったプログラム(*)を書いて、どうい うエラーが出るのか、確かめても面白いかもしれません。 (*)自己の責任で実行してください。どのような問題が発生しても、責任は負いかねます。 "A"とすると、末尾にヌル文字が自動的に追加され、A\0の2文字になります。 よって、 char a={"A"};//1文字を格納するスペースに2文字を格納しようとしている とすることは間違いです。 正しくは、 char a={'a'}; となります。 |
キーボードから文字列を入力する
文字列を初期化以外で格納する方法として、キーボードから文字列を入力するという方法があります。キーボード入力させる関数はいくつかありますが、ここでは、良く使う2つの関数を使って説明します。
まずは、いままで使ってきたscanf関数を使った方法です。
以下の例は、31文字格納できる文字配列を宣言した後、キーボードからユーザーの名前を入力させて、出力するプログラムです。
|
|
#include <stdio.h> void main(void) { char myName[31]; //最大文字数+1(ヌル文字) int maxString = 30; //最大文字数 printf("あなたの名前を%d文字以内で入力してください。>>",maxString); scanf("%s",myName); printf("\n"); printf("あなたの名前は、%sです。\n",myName); } |
30文字までの文字列を扱いたい時は、ヌル文字を合わせて、31文字分の配列要素を用意しておきます。実際の格納は、9行目のscanf関数で行われています。文字列入力時のscanf関数の使い方は、出力の時と同じように書式には%sを使い、文字配列の先頭アドレスを指定します。入力終了後、文字列の末尾に自動的にヌル文字が追加されます。
ここで気を付けなければいけないことは、30文字を超える文字列を入力してしまった時、配列内に正しくヌル文字が追加されないということです。この問題を解決するには、「少し余裕を持たせて配列要素を確保する」とか「指定文字数を超えるとエラーメッセージを出し、再度入力させる」と言った方法があります。 関数説明:scanf関数 |
実行結果(YamadaKoujiと入力した時)
あなたの名前を30文字以内で入力してください。>>YamadaKouji あなたの名前は、YamadaKoujiです。 |
しかしscanf関数には欠点があります。それは、空白(スペース)文字を区切り文字として認識してしまうことです。前回のプログラム9-4で、キーボードより"Yamada
Kouji"と入力したとすると、途中の空白(スペース)で区切られその後の文字は無視されます。よって、最終の出力では、"あなたの名前は、Yamadaです。"となってしまいます。
そこで、空白(スペース)文字を区切りではなく、1文字として扱いたい時にはgets関数を使います。以下にgets関数を使った例を示します。
|
|
#include <stdio.h> void main(void) { char myName[31]; //最大文字数+1(ヌル文字) int maxString = 30; //最大文字数 printf("あなたの名前を%d文字以内で入力してください。>>",maxString); gets(myName); //gets関数で文字列をキーボードから入力させている printf("\n"); printf("あなたの名前は、%sです。\n",myName); } |
プログラム9-4の入力部(scanf関数)の部分をgets関数に置き換えました。gets関数への引数は、文字列を格納したい文字配列の先頭アドレスです。実行結果をみても分かるように、gets関数は、scnaf関数とは違い空白(スペース)を1文字として扱います。ただし、gets関数は文字列専用入力関数である為、数値変数(int型やfloat型)への格納はできません。
関数説明:gets関数 |
実行結果(Yamada Koujiと入力した時)
あなたの名前を30文字以内で入力してください。>>Yamada Kouji あなたの名前は、Yamada Koujiです。 |
プログラム9-4,9-5では、30文字以上の文字列が入力された場合の対応策がありません。そこで、指定文字数以上の文字列が入力された時に、エラーメッセージを返し再度入力させるという、少し実用的なプログラムを紹介します。
以下のプログラムは、10文字以内でユーザーの名前を入力させ出力するプログラムです。今までと違う点は、10文字を超えて名前が入力された場合、エラーメッセージを返し再度入力させるという、文字数監視の機能を付け加えているところです。
|
|
#include <stdio.h> /*プロトタイプ宣言部*/ int input(char *,int); //文字列を入力させる関数 /*メイン関数*/ void main(void) { char myName[11]; int maxString = 10; printf("あなたの名前を%d文字以内で入力してください。>>",maxString); while(input(myName,maxString) != 0) { printf("エラー:入力文字数が%d文字を超えました。\n",maxString); printf("もう一度あなたの名前を%d文字以内で入力してください。>>",maxString); } printf("あなたの名前は、%sです。\n",myName); } /********************************************************************* input(char *,int) 文字列入力の手順をまとめた関数。 引数 char *:文字列を格納する文字配列の先頭アドレス 引数 int :ヌル文字を省く、最大文字数 文字列入力が成功すると整数値0を失敗すると整数値-1を返す。 /*********************************************************************/ int input(char sp[],int maxString) { int st_count = 0; while(st_count < maxString+1) { scanf("%c",&sp[st_count]); if(sp[st_count] == '\n') //printfでの改行(\n)文字、ここでは入力終了を意味する { sp[st_count] = '\0'; //\nをヌル文字(\0)に置きかえる return 0; //正常入力 } else st_count++; } rewind(stdin); //stdinバッファの内容をクリアーする return -1; //入力文字数が指定文字数をオーバーしました } |
実際に文字入力と文字数の監視の役割をしている関数が、input関数になります。文字列の取得には、scanf関数を使っていますが、書式は%s(文字列型)ではなく%c(文字型)を使っています。
実は、scnaf関数やgets関数の場合、キー入力された文字がそのまま文字変数に格納されるわけではなく、一度stdinというバッファに保存されます。そして、リターンキーを押すと、1文字ずつ各変数に格納されていくのです。 このプログラムでは、一度stdinバッファに蓄えられた文字列を繰り返し文を使うことで、ある文字数(ここでは、10文字)を超えるかリターンキーが押されたことを示す改行文字(\n)が見つかるまで、1文字ずつ文字配列に格納していくように組まれています。もし、文字数がオーバーすると、繰り返し文から抜け出し、rewind関数によりstdinバッファをクリアー(バッファに全く文字が保存されていない状態)にして、失敗を示す整数値-1を返します。また、指定文字数以内に改行文字(\n)が見つかると、その文字をヌル文字(\0)に上書きし、成功を示す整数値0を返します。gets関数や書式に%sを使っていた時には、自動的にヌル文字が追加されていましが、1文字単位で処理をする場合は、プログラマー側でヌル文字(\0)をつけなけばいけません。 関数説明:rewind関数 |
実行結果
あなたの名前を10文字以内で入力してください。>>Yamada Kouji エラー:入力文字数が10文字を超えました。 もう一度あなたの名前を10文字以内で入力してください。>>YamadaKoji あなたの名前は、YamadaKojiです。 |
文字列処理1:文字列コピー(strcpy関数)
ここまで、文字配列に文字列を格納する2つの方法(初期化,キーボード入力)を紹介しました。次は、strcpy関数を利用した文字列の格納方法を紹介します。文字列処理を行う関数(strcpyだけに限らず)を使うときは、プログラムの最初に"string.h"というヘッダーファイルをインクルードする必要があります。
次の例をみてください。
|
|
#include <stdio.h> #include <string.h> void main(void) { char myName[15]; strcpy(myName,"Yamada Kouji"); //文字配列に文字列"Yamada Kouji"を格納する strcpy(copyName,myName); //文字配列(copyName)に文字配列(myName)の内容をコピーする printf("%s\n",myName); printf("%s\n",copyNmae); } |
いつもの通り、文字列を格納する為に十分なスペースをもった文字配列を宣言します。その後、strcpy関数で、文字列を格納しています。
strcpy関数の引数は、1番目の引数が、文字列のコピー先配列の先頭アドレス、2番目の引数がコピーする文字列の先頭アドレスです。例では、2番目の引数が"Yamada Kouji"となっています。これって先頭アドレスなの?と疑問に思う人もいると思いますが、ダブルクオーテーションマーク(")の役割の1つは、文字列の末尾にヌル文字(\0)を追加すること、そしてもう1つが、それで囲まれた文字列の先頭アドレスを返すことです。ですから、2番目の引数に"Yamada Kouji"とかくことで、最初の文字'Y'のアドレスを与えていることになります。 関数説明:strcpy関数 |
実行結果
Yamada Kouji Yamada Kouji |
ここまでの文字列操作で、何度も文字列や文字配列の先頭アドレスといった言葉を使ってきました。C言語の文字と文字列の大きな違いは、文字列のどのような操作(文字列の格納・入力・出力など)においてもポインターという概念を使っているところです。また、数値や文字のように=を使って代入できない点も違います。
例えば前プログラムの一部(格納部)を次のように変えてみます。
myName = "Yamada Kouji";
これは、一見正しいように見えます。しかしじっくり考えてみると、左辺は文字配列(文字だけを格納する変数)なので、ポインターの形で左辺に使うことは許されません。
また、
myName[0] = "Yamada Kouji";
の記述が間違っていることは、いうまでもありません。(文字列の基本を参考)
以上の理由で、文字配列に文字列を格納するには、strcpy関数などを使わなければいけないのです。
文字列処理2:文字列連結 (strcat関数)
ここからは、より実用的な文字列処理の方法を紹介していきます。
まず最初に、strcat関数を紹介します。この関数は、文字列を連結してくれます。
以下の例をみてください。
|
|
#include <stdio.h> #include <string.h> void main(void) { char myNumber[10]; char myName[15]; char outputString[25]; strcpy(myNumber,"96131028"); strcpy(myName,"Yamada Kouji"); strcpy(outputString,myNumber); strcat(outputString,myName); puts(outputString); } |
3つの文字配列を宣言し、myNumber配列には学籍番号を、myNameには名前をstrcpy関数で格納しています。その後、outputString配列にmyNumber配列の文字列をstrcpy関数で格納した後、strcat関数でmyName配列の文字列を連結しています。
strcat関数の引数は、1番目には連結先文字配列の先頭アドレス、2番目には連結する文字配列の先頭アドレスです。 また、今回は出力にputs関数を使っています。引数は、表示させたい文字配列の先頭アドレスです。 関数説明:strcat関数 |
実行結果
96131028Yamada Kouji |
文字列処理3:文字列比較 (strcmp関数)
次に紹介するのは、文字列の比較です。早速以下の例をみてください。
|
|
#include <stdio.h> #include <string.h> #define ST_LENGTH 15 void main(void) { char inputSt_a[ST_LENGTH]; char inputSt_b[ST_LENGTH]; printf("文字列を入力しください>>"); gets(inputSt_a); printf("文字列を入力しください>>"); gets(inputSt_b); if(strcmp(inputSt_a,inputSt_b) == 0) //文字列比較:2つの文字列が一致する printf("入力された2つの文字列は一致します。\n"); else //文字列比較:2つの文字列が一致しない printf("入力された2つの文字列は一致しません。\n"); } |
このプログラムでは、2つの文字列を入力させ比較しています。
比較は、strcmp関数で行います。引数として、比較したい2つの文字列(s1,s2)が格納されている文字配列の先頭アドレスを渡します。 2つの文字列が一致する時、0を返します。 2つの文字列の関係がs1 > s2の時、正の数を返します。 2つの文字列の関係がs1 < s2の時、負の数を返します。 関数説明:strcmp関数 |
実行結果(inputSt_a:Yamada
inputSt_b:Yamada)
文字列を入力しください>>Yamada 文字列を入力しください>>Yamada 入力された2つの文字列は一致します。 実行結果(inputSt_a:Yamada inputSt_b:kindai)
|
必要なヘッダーファイル 戻り値 関数名(引数1[,引数2]) |
戻り値 |
引数 |
関数説明 |
|
|||
#include <stdlib.h> int atoi(str); |
変換された整数値 |
char *str:変換する文字列 例:atoi→"123" atol→"-123456" atof→"1.2e3" |
文字列strをそれぞれの型の数値に変換します。先頭のホワイトスペースは無視されますが、途中にあってはいけません。先頭の±は符号として扱われます。変換できなかったときは0を返します。 |
#include <stdlib.h> long atol(str); |
変換されたlong型整数値 |
||
#include <stdlib.h> or <math.h> double atof(str); |
変換されたdouble型実数値 |
||
#include <stdlib.h> int strtod(str,endptr); |
変換されたdouble型実数値 |
char *str:変換する文字列 例:"1.2e3" char **endptr:検索終了文字へのpointer |
文字列strをdouble型実数値に変換します。先頭の空白は無視され、±は符号として扱われます。
実数値ではない文字が現れた時には、その文字へのポインターをendptrに返します。endptrがNULLなら正常に変換できたことを示します。 |
#include <stdlib.h> long strtol(str,endptr,radix); |
変換されたlong型整数値 |
char *str:変換する文字列 char **endptr:strtodと同様 int radix:基数 |
文字列strをlong/unsigned long型整数値に変換します。文字列には、8進数(0)16進数(0x)を含めることができ、先頭の空白は無視されます。endptrは、strtodのときと同じです。
radixは文字列を変換する時の基数で、2〜36進が指定できます。 |
#include <stdlib.h> unsigned long strtoul(str,endptr,radix); |
変換された unsigned long型実数値 |
||
|
|||
#include <stdlib.h> char *itoa(ival,str,radix); |
strへのpointer |
int ival,long lval, unsigned long uval:変換する数値 char *str:変換文字列を格納するバッファ int radix:基数 |
int/long/unsigned logn型データをそれぞれの文字列に変換してstrに格納します。radixは基数です。 |
#include <stdlib.h> long ltoa(lval,str,radix); |
|||
#include <stdlib.h> double ultoa(uval,str,radix); |
|||
|
|||
#include <stdio.h> int getchar(); |
入力された文字 ^Z入力で、EOF(-1)を返す |
なし |
stdin(キーボード)より入力された文字を返します。 |
#include <stdio.h> char *gets(str); |
正常:strへのpointer EOFまたはエラー:NULL |
char *str:文字列を格納するバッファ |
stdin(キーボード)より入力された文字列をstrに格納します。入力の最後は'\n'ですが、'\n'は格納されず最後に'\0'が付加されます。^ZでNULLを返します。 |
#include <stdio.h> int scanf(format,arg1,arg2..); |
正しく入力された項目数。 (書式"format")に合わな いデータが入力される と、その時点でscanfか ら戻ってきます。 |
format:%[*][w][修飾子]変換文字 []内は省略可 %:書式制御文字列の開始 *:代入禁止 w:この幅でデータを区切ります 修飾子 l:longを意味します h:shortを意味します N:ニア・ポインター F:ファー・ポインター 変換文字 c:1文字 s:文字列 |
本節で説明している為、特になし。 |
|
|||
#include <stdio.h> int putchar(c); |
正常:出力した文字 EOFまたはエラー:EOF(-1) |
int c:出力する文字 |
文字cをstdout(CRT)に出力します。cにASCIIコードを指定することもできます。 |
#include <stdio.h> int puts(str); |
正常:0 エラー:非0 |
char *str:出力する文字列バッファ |
strでしめされるバッファの文字列を'\0'がくるまでstdout(CRT)へ出力します。'\0'は出力されず変わりに'\n'が出力されます。 |
#include <stdio.h> int printf(format[,arg1,arg2...]); |
出力した文字数 |
char *format:書式制御文字列へのpointer
arg1,arg2...:書式制御文字応じた引数 |
formatで指定する書式に従って、引数の内容をstdout(CRT)に出力します。
書式制御文字列と型が一致しない時や数が足りない時は、出力は保証されません。逆に数が多い場合は単純に捨てられます。詳しくは、本節で基本的使い方をのせているので参考にしてください。 |
|
|||
#include <string.h> char *strcat(s1,s2); |
s1へのポインター |
char *s1:文字列の連結先配列 char s2:連結する文字列 unsigned n:連結する文字列の長さ |
s1にs2を連結します。strncat関数では、s2の先頭からn文字だけ連結します。 |
#include <string.h> char *strncat(s1,s2,n); |
|||
#include <string.h> char *strchr(s,c); |
指定文字が見つかった位置, みつからない時は、NULL。 |
char *s:サーチされる文字 int c:キー文字 |
文字列sの中からキー文字cを探し、最初に見つかった位置へのポインターを返します。
|
#include <string.h> char *strrchr(s,c); |
|||
#include <string.h> int strcmp(s1,s2); |
比較結果(正/0/負) |
char *s1,*s2:比較する文字列 unsigned n:比較する長さ |
s1,s2で示される文字列を比較し、
|
#include <string.h> int strcmpi(s1,s2); |
|||
#include <string.h> int strncmp(s1,s2,n); |
|||
#include <string.h> int stricmp(s1,s2); |
|||
#include <string.h> int strnicmp(s1,s2,n); |
|||
#include <string.h> char *strcpy(s1,s2); |
s1へのpointer |
char *s1:文字列のコピー先配列 *s2:コピーする文字列 unsigned n:コピーする長さ |
配列s1に文字列s2をコピーします。strncpyは先頭から最大n文字までをコピーします。s1はコピー文字列の長さ+1('\0'分)のサイズ以上確保しておきます。 |
#include <string.h> char *strncpy(s1,s2,n); |
|||
#include <string.h> unsigned strcspn(s1,s2); |
最初に見つかった位置 |
char *s1:サーチされる文字列 *s2:キーリスト |
strcspnはs2のキーリスト中のどれか1文字が、s1の中で最初に見つかった位置を返します。
|
#include <string.h> unsigned strspn(s1,s2); |
|||
#include <string.h> unsigned strlen(str); |
文字列の長さ |
char *str:文字列 |
文字列strの長さ('\0'は含まない)を返します。 |
|
|||
#include <stdio.h> void rewind(fp); |
なし |
FILE *fp:ファイルpointer |
ファイルfpの現在位置をファイル先頭に戻します。rewindを行うとエンド・オブ・ファイルやエラー・フラグをクリアーします。
|
今回取り扱ったのは、文字と文字列に関するほんとうに極わずかな部分にすぎず、もっと説明したいことがいっぱいあります。しかし、時間的な理由から今回はこれだけになってしまいました。もちろん、良いページにしていきたいという気持ちはあります。ですから、「ここがわかりにくい!」とか「こういうことをしたいんだけど、どうすればいいのか?」などの疑問・質問・苦情をメールしてください。
最後に、説明不足や分かりにくい表現等があるかとは思いますが、この節を最後まで読んでいただきありがとうございました。(経済性工学研究室:山田 宏治)