第13章 文字の取り扱い方 13.1 文字と文字型変数 13.2 文字列 13.3 文字型配列への文字列の代入 13.4 キーボードからの文字列の入力 13.5 文字型配列と関数
13.1 文字と文字型変数 これまでに扱ったのは数値型変数 プログラミングでは文字も扱う場合が多い 整数型 13.1 文字と文字型変数 これまでに扱ったのは数値型変数 整数型 int : -2147483648 〜 2147483647 浮動小数点型 float : 10-38 〜 1038 double : 10-308 〜 10308 プログラミングでは文字も扱う場合が多い 文字型 char を用いる これらの他に、short int, long int, long double, また、unsigned がつくものなどがある。 詳細はプログラミング演習Ⅰの資料を参照 http://www.ced.is.utsunomiya-u.ac.jp/lecture/2009/prog/p1/kadai1/variable.html
文字型変数に文字を代入 プログラム例 13.1.1 より抜粋 1文字 = 1バイトの数値 int main(void) { char x; x = 'H'; printf("%c¥n", x); return 0; } 文字型変数 x を宣言 代入文でOK 1文字はシングル クォート(')でくくる 1文字用の書式指定子 p.180, 表A.3 ASCIIコード 1文字 = 1バイトの数値 8ビット(0~255)
文字型変数に文字を代入 プログラム例 13.1.1 の改変 #include <stdio.h> int main(void) { char x, y, z, u, v, w; x = 'H'; y = 'a'; z = 'p'; u = 'p'; v = 'y'; w = '!'; printf("%c%c%c%c%c%c¥n", x, y, z, u, v, w); printf("%d %d %d %d %d %d¥n", x, y, z, u, v, w); printf("%x %x %x %x %x %x¥n", x, y, z, u, v, w); return 0; } character decimal %c 文字 %d 10進数 %x 16進数 hexadecimal
文字列 William Shakespeare を配列変数 x に代入 13.2 文字列 複数個の文字(文字列、string という)は、 「char 型の配列」として扱う。 文字列はダブルクォート(")でくくること。 文字列の最終文字の次に、文字列の最後を示す \0 が入る。 null文字 プログラム例 13.2.2 より抜粋 文字列 William Shakespeare を配列変数 x に代入 int main(void) { char x[20] = {"William Shakespeare"}; printf("%s¥n", &x[0]); return 0; } 19字だが、 20字分用意 ここで、配列変数 x の先頭番地を指定すると、 x のすべてを表示する。 ただし最後の ¥O は表示しない 文字列用の書式指定子
文字列は配列に格納される W int main(void) i 1000 x[0] { l 1001 char x[]={"William Shakespeare"}; printf("%s¥n", x); return 0; } 1000 1001 1002 ・ x[0] x[1] x[2] x[3] x[4] &x[0] は 単に x でよい e \0 x[18] 文字列の終わり x[19] 正体は1バイトの数字の0
13.2 文字列 プログラム例 13.2.2 の改変 配列の長さ < 文字列の長さ の場合にどうなるか、試してみよう 13.2 文字列 配列の長さ < 文字列の長さ の場合にどうなるか、試してみよう プログラム例 13.2.2 の改変 #include <stdio.h> int main(void) { char x[??] = {"William Shakespeare"}; printf("%s¥n", x); return 0; } ここの値を変えて、配列の 長さを1つずつ減らしていく
文字列の正体 プログラム例 13.2.2 の改変 #include <stdio.h> int main(void) { int i; char x[20] = {"William Shakespeare"}; printf("%s¥n", x); for (i = 0; i < 20; i++) printf(" %d", x[i]); printf("\n"); return 0; } この出力結果を p.180、表A.3 のASCIIコード と比べてみよう
13.2 文字列 プログラム例 13.2.4 複数の文字列を扱う場合 → 2次元配列を使う #include <stdio.h> 13.2 文字列 複数の文字列を扱う場合 → 2次元配列を使う プログラム例 13.2.4 #include <stdio.h> int main(void) { char name[3][20] = {"William Shakespeare", "William Clinton", "William Henry"}; int i; for (i = 0; i < 3; i++) printf("%s¥n", name[i]); printf("\n"); return 0; } 前の数字が文字列の数 後の数字は「文字数+1」以上に
13.3 文字列の代入 配列(文字列)に代入文は使えない 文字列処理関数 (string.h を include) 13.3 文字列の代入 配列(文字列)に代入文は使えない 文字列処理関数 (string.h を include) strcpy(char *dst, char *src) string copy (ストリング・コピー)の略 引数の順序は代入文 dst ← src に対応 引数はポインタ(文字型配列の名前)
a ← "William Shakespeare" 13.3 文字列の代入 プログラム例 13.3.2 #include <stdio.h> #include <string.h> int main(void) { char a[31], b[31]; strcpy(a, "William Shakespeare"); printf("%s\n", a); strcpy(b, a); printf("%s¥n", b); return 0; } 入れ物は2つ用意 a ← "William Shakespeare" b ← a 文字列の代入 a William Shakespeare 実際にコピー b William Shakespeare
a ← "William Shakespeare" 13.3 文字列の代入(もどき) プログラム例 13.3.2 の改変 #include <stdio.h> int main(void) { char a[] = "William Shakespeare", *b; printf("%s\n", a); b = a; printf("%s¥n", b); return 0; } 入れ物は1つ a ← "William Shakespeare" 文字型ポインタを用意 b ← a アドレスデータの代入 データは共用 a William Shakespeare b
13.4 キーボードからの文字列の入力 プログラム例 13.4.2 #include <stdio.h> 13.4 キーボードからの文字列の入力 プログラム例 13.4.2 101 は文章を読み込むのに 十分と考えられる適当な数。 特別な意味はない #include <stdio.h> int main(void) { char sentence[101]; printf("文章を入力して下さい"); gets(sentence); puts(sentence); return 0; } キーボードから文字列を受け取り、 の配列変数 sentence に代入する ディスプレイに表示 gets は get string、puts は put string と覚えれば良い ゲットエス プットエス 参考: C言語の読み方サイト http://bubuzuke.s7.xrea.com/ISO10646/c_reading.html
失敗例 プログラム例 13.4.1 と 13.4.2 の混合 #include <stdio.h> int main(void) { char surname[51], sentence[101]; printf("姓を入力してください :"); scanf("%s", surname); printf("姓は %s です.\n", surname); printf("文章を入力してください "); gets(sentence); puts(sentence); return 0; } gets, getc, getchar があるときは scanf は使わない 製品開発には挙動不審な scanf は使われない
解決例 プログラム例 13.4.1 と 13.4.2 の混合 #include <stdio.h> int main(void) { char s[101], surname[51], sentence[101]; printf("姓を入力してください :"); gets(s); sscanf(s, "%s", surname); printf("姓は %s です.\n", surname); printf("文章を入力してください "); gets(sentence); puts(sentence); return 0; } gets + sscanf なら OK
13.5 文字型配列と関数 関数に文字列を渡す → ポインタ変数(配列名)を引数に プログラム例 13.5.2 2次元配列のまま渡す場合 13.5 文字型配列と関数 関数に文字列を渡す → ポインタ変数(配列名)を引数に プログラム例 13.5.2 2次元配列のまま渡す場合 #include <stdio.h> #define NSIZE 40 #define N_Mem 3 void input_name(int n, char name[][NSIZE]); void print_name(int n, char name[][NSIZE]); int main(void) { char f_and_g[N_Mem][NSIZE]; input_name(N_Mem, f_and_g); print_name(N_Mem, f_and_g); return 0; } 1行あたり40文字で3行分 の2次元配列を宣言 ポインタ(その配列 の先頭番地を指す) を引数とする 文字列を 読込む関数 文字列を 表示する関数
13.5 文字型配列と関数 プログラム例 13.5.2 (つづき) void input_name(int n, char name[][NSIZE]) { int i; for (i = 0; i < n; i++) { printf("苗字と名前を入力してください :"); gets(name[i]); } void print_name(int n, char name[][NSIZE]) for (i = 0; i < n; i++) printf("あなたの苗字と名前は %s です.\n", name[i]);
13.5 文字型配列と関数 プログラム例 13.5.3 ポインタ配列として渡す場合 #include <stdio.h> 13.5 文字型配列と関数 プログラム例 13.5.3 ポインタ配列として渡す場合 #include <stdio.h> #define NSIZE 40 #define N_Mem 3 void input_name(int n, char *name[]); void print_name(int n, char *name[]); int main(void) { int i; char f_and_g[N_Mem][NSIZE], *name[N_Mem]; for (i = 0; i < N_Mem; i++) name[i] = f_and_g[i]; input_name(N_Mem, name); print_name(N_Mem, name); return 0; } NSIZEが不要に 文字列ポインタ配列を用意 行ごとに2次元配列を 文字列ポインタ配列に すり替え
13.5 文字型配列と関数 プログラム例 13.5.3 (つづき) void input_name(int n, char *name[]) 13.5 文字型配列と関数 プログラム例 13.5.3 (つづき) void input_name(int n, char *name[]) { int i; for (i = 0; i < n; i++) { printf("苗字と名前を入力してください :"); gets(name[i]); } void print_name(int n, char *name[]) for (i = 0; i < n; i++) printf("あなたの苗字と名前は %s です.\n", name[i]); NSIZEが不要に
スキルアップタイム 1 以下の動作をするプログラムを作成してみよう。 ディスプレイに「文章を入力して下さい」と表示する。 キーボードから入力された適当な英語の文章 (例えば Boys be ambitious! など)を読み取る。 その文章をディスプレイに表示する。 これを3回繰り返す。 (1、2、3回目は各々違う文章を入力してみる) プログラム例 13.4.2 を参照
スキルアップタイム 1 のプログラムの流れ main printf で「文章を入力して下さい」 gets で文章を読み取り、変数に入れる スキルアップタイム 1 のプログラムの流れ ヘッダファイル、関数、記号定数の宣言部 main 変数宣言(必要な変数と そのデータ構造を決める) 3つの文章を記憶せず、読み込んで表示するだけなら1つの文章用1次元配列で OK 3回繰り返し printf で「文章を入力して下さい」 gets で文章を読み取り、変数に入れる puts で文章を表示 文字型配列sentence 終了
3個の文章を記憶する必要有り → 文字列の2次元配列が必要 スキルアップタイム 2 作ったプログラムを以下のように変更してみよう。 最初にディスプレイに「3つの文章を入力して下さい」 と表示する。 ディスプレイに「文章 i:」(iは1、2、3と変化する)と表示し、キーボードから入力される英語の文章を読み取る。これを3回繰り返す(この時点では表示はしない)。 3個の文章をまとめてディスプレイに表示する。 3個の文章を記憶する必要有り → 文字列の2次元配列が必要
スキルアップタイム 2 のプログラムの流れ (その1) スキルアップタイム 2 のプログラムの流れ (その1) ヘッダファイル、関数、記号定数の宣言部 main 変数宣言(必要な変数と そのデータ構造を決める) main で全ての作業を行う タイプ(この程度の課題ならこれでも十分) printf で「文章を入力して下さい」 繰返し gets で文章を読み取り、配列変数に入れる 繰返し puts or printf で配列変数に入った文章を表示 終 了
スキルアップタイム 2 の プログラムの流れ (その2) プログラム例 13.5.2 を参照 関数名、変数名は自分の好きなように決めてください これは一例 ヘッダファイル、関数、などの宣言部 文章を読み取る関数 input_sentence main printf で「文章を入力して下さい」 繰返し 変数宣言(必要な変数と そのデータ構造を決める) gets で文章を読み取る 文章を読み取る関数 文章を表示する関数 print_sentence 文章を表示する関数 繰返し printf or puts で文章を表示 終 了 文章用の2次元配列(その先頭へのポインタ)を引数としてデータをやりとりする
スキルアップタイム3 すべてできた人は、演習問題の問題13.3、問題13.5 をやってみよう。 問題13.3 用のデータを考えるのが面倒な人は下記のデータを使ってください。 学籍番号 氏名 科目Aの得点 1001 Akanishi Hideo 55 1005 Ishihara Satoshi 64 1013 Kitano Keiko 71 1024 Sakaguchi Erika 85 1030 Tanaka Kazuya 68