文字列
授業中にカバーできなかったら、冬休みの宿題。期末テストに類似の問題、出します。
文字
シンボルをシングルクオートで囲って、文字を表す。
char c; c = 'a';
文字列は char の配列
文字が複数個繋がったもの、"hello" や "日本語" などを文字列という。 文字列の間にスペース文字や改行文字が含まれることもある。
C 言語では文字列を表現するのに要素の型が char の配列を使う。 そして、文字列の後ろにヌル文字という特別な文字でマーキングする。 ヌル文字は '\0' で表す。ヌル文字の実体はすべてのビットが 0 のデータ。
char str[10]; // 長めに配列を用意する。 str[0] = 'h'; // 配列要素に文字を代入していく。 str[1] = 'e'; str[2] = 'l'; str[3] = 'l'; str[4] = 'o'; str[5] = '\0'; // 文字列の終わりをマーク。str[5] = 0; でもよい。 str[6] = 'z'; // ヌル文字の後ろは文字列の一部とは認識されない。 printf("%s\n", str); // %s がデータを文字列と解釈する決まり。hello とプリントされる。そして改行。zは無視される。
文字と文字列を区別すること
文字データは、シンボルを シングルクオート ' ' で囲む。
-
'a', 'b', '0','!','\n','\0' はすべて文字。'\n'は改行、'\0' はヌル文字。
-
'abc' はエラー。
文字列は 0 個以上のシンボルの続きを ダブルクオート " " で囲む。
-
"a" ...先頭に 'a' が入った長さ1の文字列。配列の2番目にはヌル文字'\0'。
'a' '\0' -
"abc" ... char配列の先頭に'a', 2番目に 'b', 3番目に'c',4番目には '\0'。
'a' 'b' 'c' '\0' -
"a b c" ... 'a', ' ', 'b', ' ', 'c' の5文字からなる文字列。
'a' ' ' 'b' ' ' 'c' '\0' -
"abc\ndef\n" ... abcの後ろで改行、defの後ろで改行。
'a' 'b' 'c' '\n' 'd' 'e' 'f' '\n' '\0' -
"" ... 先頭に'\0'が入ったヌル文字列。空文字列。
文字列を初期化する
char s1[10] = "abc"; // s1[0] に 'a', s1[1] に 'b', s1[2] に 'c', s1[3] に '\0' が代入される。 // それより後ろはゴミ。 char s2[] = "abc"; // 'a', 'b', 'c', '\0' を格納するに必要十分な要素数 4 つの配列 s2 が用意され、 // 各文字が各場所に代入される。
文字列を scanf で読み込む
入力が予想される文字列の長さ以上の char 型配列を用意しておき、 scanf("%s", 配列の名前) で、 入力中に空白文字が現れるまで の文字列を配列に読み込む。
char str[10]; scanf("%s", str); // キーボードから abc def ghi と打つと変数 str には "abc" が読み込まれる。
準備した配列の長さ以上の文字列を scanf で読むと予期しないエラーになる。 コンピュータウィルスの侵入口のひとつ。
ここから以下はすべて個人課題。 文字列を操作する関数を作ってみることは C 言語のいいトレーニングになる。
できない問題はグループ、あるいはグループを超えて話し合ってよい。 個人課題の提出を済ますことより、自分の理解度にプライドをもて。
文字列の長さを求める
文字列の最後の後ろには必ずヌル文字 '\0' がある。 配列の先頭から数えて '\0' が何番目にあるかを求め、 その番号を戻り値とする関数を定義すればよい。
// 文字列 s の長さ(文字列中の文字数)を返す。 int str_len(char s[]) { int i; for (i = 0; _______; i++) { ; } return i; }
- 文字列 "1234567" の長さを str_len( ) で求めよ。
- 文字列 "I am a programmer." の長さを str_len( )で求めよ。
文字列に含まれる特定の文字を数える
// 戻り値は文字列 str 中の文字 c の出現回数。 int count_chars(char str[], char c) { int i; int count = 0; for (i = 0; str[i] != '\0'; i++) { if (str[i] == c) { count++; } } return _____; }
- 文字列 "sumomomo momo mo momo no uchi" に文字 'o' は何個含まれるか?
二つの文字列が等しいかどうかを判定する
文字列 s1 と s2 とが同じ文字列かどうかは == で判定できない。次のプログラムでそれを確かめろ。
#include <stdio.h> int main(void) { char s1[] = "abcdef"; char s2[] = "abcdef"; if (s1 == s2) { printf("equal\n"); } else { printf("not equal\n"); } }
イコールにならない理由は上記のコードは配列 s1 と配列 s2 のアドレスを比較するから。 アドレスわからなくてもプログラミングできます。 この授業ではアドレス、ポインタ、扱いません。
二つの文字列が等しいとは、文字列に含まれる文字が全て同じってこと。 二つの文字列が等しければ 1、等しくなければ 0 を返す関数 str_eql(s1, s2) を定義する。
// 文字列 s1 と s2 が等しければ 1、そうでなければ 0 を返す。 int str_eql(char s1[], char s2[]) { int i; for (i = 0; s1[i] != '\0'; i++) { if (s1[i] != s2[i]) { return 0; } } return ____________; }
- str_sql("abc", "abc") が 1 を返し、 str_eql("abcd", "abc") も str_eql("abc","abcd") も 0 を返すことを確認せよ。
- 端末から文字列を二つ読み込み、それらが文字列として等しければ、"same"、等しくなければ "not same" をプリントする関数を上で定義した str_eql( ) を下請け関数として作れ。
二つの文字列の先頭から n 文字が等しいかどうかを判定する
プログラマの繊細さが問われる。
// 文字列 s1 と s2 の先頭から n 文字を比較する。 int str_eql_n(char s1[], char s2[], int n) { int i; for (i = 0; i < n; i++) { if (s1[i] != s2[i]) { return 0; } if (__________________) { // 添字が範囲を超えてないかのチェック return 0; } } return 1; }
- 文字列 "powerfull" と文字列 "powerless" の先頭から 5 文字を比較し、 等しければ "OK"、等しくなければ "NG" をプリントせよ。
- str_eql_n("powerfull", "powerless",6) が 0 を返すことを確認せよ。
- str_eql_n("a","a",100) が 0 を返すことを確認せよ。
文字列 s1 を 文字列 s2 にコピーする
// 文字列 s2 の要素数は s1 の要素数よりも大きいこと。 void str_copy(char s1[], char s2[]) { int i; for (i = 0; s1[i] != '\0'; i++) { s2[i] = s1[i]; } ______________; }
コピーする文字数よりも s2 に確保している配列の要素数が短いと致命的なエラーになる。 上はまだ不十分。エラーを防ぐにはどうすれば?
- char s1[10] = "yoshino";
- char s2[10] = "kobayashi";
- 文字列 s1 を文字列 s2 にコピーしろ。
- 文字列 s1 と文字列 s2 は等しい文字列となったか?
文字列 s1 の後ろに 文字列 s2 をつなぐ
文字列 s1 は s2 をつなぐ十分な長さがあること (十分大きな配列として宣言されていること)が前提。 A を取る気の学生は以下の定義では、 str_append("abc", "xyz")がエラーになることを理解するまで考えること。 考えに考えてもわからないときは 214 室まで。
// 文字列 s1 の後ろに文字列 s2 を付け足す。 void str_append(char s1[], char s2[]) { int i; for (i = 0; s1[i] != '\0'; i++) { ; } int j; for (j = 0; s2[j] != '\0'; j++) { s1[i] = s2[j]; i++; } ___________; }
- 文字列 s1 を "nakajima" とせよ。
- 文字列 s2 を "love" とせよ。
- 文字列 s1 の後ろに文字列 s2 をつなげ。
- s1 の後ろにスペースを一つ入れ、結果が "nakajima love" となるようにするにはどうすれば?
文字列 s1 の n 文字目からの m 文字を s2 にコピーする。
コピーに成功したら 1, 失敗したら 0 を返そう。
int str_take(char s1[], int n, int m, char s2[]) { int i; int j = 0; for (i = n; i < n + m; i++) { // 不十分。s1 の長さよりも n が大きかったら? if (s1[i] == '\0') { return 0; } else { s2[j] = s1[i]; } } _________; return 1; }
- 文字列 "son of a bitch." の 7 文字めから 5 文字を取り出した文字列をプリントせよ。
文字列 s1 中に文字列 s2 が出現するかどうかを判定する
目的の文字を見つけたら s1 の何文字目から出現しているか、 その添え字を返そう。見つからなかった時は添字としてありえない -1 を返そう。
// 文字列 s1 中に文字列 s2 が見つかればその位置、ないときは -1 を返す。 int str_search(char s1[], char s2[]) { int i; for (i = 0; s[i] != '\0'; i++) { if (___________) { return i; } } __________; }
- 文字列 "fukushima, love and peace" 中に文字列 "love" は何文字目から出現するか。 また、"lovely" は出現するか?
文字列 s1 の n 文字目からの m 文字を削除する
ノーヒントで。
void str_remove(char s1[], int n, int m) { }
- str_remove("Ucchi does not love.", 7, 9); の結果が "Ucchi love." となることを確認せよ。
文字列 s1 中から文字列 s2 を削除する
今度は範囲ではなく、見つかった文字列を削除。
void str_remove_str(char s1[], char s2[]) { }
- str_remove_str("Yuki does not love every day.", "not "); の結果が "Yuki does love every day." となることを確認せよ。
文字列 s1 の n 文字目に文字列 s2 を挿入する
できるかな?
void str_insert(char s1[], int n, char s2[]) { }
- char s1[100]="He studies hard."; str_insert(s1, 11, "very ")); により、 s1 が "He studies very hard." となることを確認せよ。
文字列 s1 中に現れる文字列 s2 を文字列 s3 で置き換える
このくらいできれば配列、文字列は合格。C言語の理解もだいぶ深まったはず。
void str_subst(char s1[], char s2[], char s3[]) { }
- char s1[100]="All you need is XX."; とし、 str_subst(s1, "XX", "love") の呼び出しにより s1 が "All you need is love."となることを確認せよ。
- str_subst(s1, "YYY", "hate") の呼び出しで s1 が変化しないことを確認せよ。
できない問題はグループ、あるいはグループを超えて話し合ってよい。 個人課題の提出を済ますことより、自分の理解度にプライドをもて。
hkimura.
1.0.2