Unix C プログラミング入門資料(高崎金久)   7.文字列(テキスト)の処理 目次 1.C言語における文字と文字列 2.プログラム例(文字列の照合) 3.文字および文字列処理関数 -------------------------------------------------------------------------- 1.C言語における文字と文字列 C言語では文字列という特別なデータ型がありません.文字列は文字型(char)配列 として実現されています.ただし,実際の文字列に加えて,文字列の終りを示す Ox00という1バイトコードが最後に付きます.したがって,文字列を格納する配列 としては文字列そのものよりも一つ長いものを用意しなければなりません. [例] char message[] = "Hello, world!"; /* 文字型配列の宣言と初期化 */ printf("%s\n", message); /* 文字型配列を引数とする関数 */ この例ではmessage は長さが 14 + 1 の配列になります. C言語の文字型は char で表わされます.これは1バイトで表わされる符号なし整 数(16進で00からFFまで)として実現されています.特に00から7Fまで の128個には「アスキーコード表」にしたがって英数字記号または改行コードな ど特別の機能をもつ制御コードが1対1に対応しています.残りの部分の利用法は 機種やシステムによって違います.(なお,日本語,中国語,韓国語などの文字は 2バイトで1文字を表わす体系になっています.) [文字型] char ------ 0x00 ... 0x7f アスキーコード |----- 0x80 ... 0xff  機械・処理系によって異なる 制御コードはいわば歴史的なもので,呼び名の通りに使われているとは限りません. 例えば 0x0d は改行(Carrige Return),0x0aは復帰(Line Feed)コードと呼ばれ, いずれもテキストの改行に関わるものですが,使われ方はシステムによって異なり ます.(テキストの改行は Unix では LF, DOS や Windows では CR+LF, MacOS で は CR です.) C言語では1文字を表現するためにいくつかの表記方法が用意されています. 1.普通の英数字は1重引用符で囲む ' '(空白), '0', '1', ..., '9', 'A', 'B', ..., 'a', ... など 2.制御コードおよび特殊文字 表記 記号 意味 表記 記号 意味 '\a' BEL ベル '\b' BS バックスペース '\f' FF 改ページ '\r' CR 復帰 '\n' LF 改行 '\t' HT 水平タブ '\v' VT 垂直タブ '\\' \ バックスラッシュ '\'' ' 一重引用符 '\"' " 二重引用符 '\?' ? 疑問符 3.対応する8進数や16進数を1重引用符で囲む '\ooo' ooo 8進数(o のところに0,1,...,7 の数が入る)(*) '\xhh' hh 16進(h のところに0,1,...,f の数が入る)(**) (*) ooo は8進で3桁以内.だから '\0' '\00', '\000' は同じこと. (**) hh は16進で2桁以内.たとえば 'x0d' と 'xd' は同じこと. 4.直接10進数や16進数で表す 0x00 ... 0xff (16進数) 0 ... 255 (10進数) 2.プログラム例(文字列の照合) 1: /* 標準入力から1行ずつ読み込み, コマンド引数として渡した文字列 2: がそこに何個含まれているかを表示する.^D で入力を打ち切ると, 3: プログラムを終了する. */ 4: #include 5: #include 6: #define patternMax 100 7: #define textMax 1000 8: int match(char [], char []); /* 関数プロトタイプ宣言 */ 9: 10: int main(int argc, char *argv[]) 11: { 12: char pattern[patternMax], text[textMax]; 13: 14: /* コマンド引数がないか, あるいは長さが0のときには 15: 使い方を表示して終了する */ 16: if ((1 == argc) || (0 == strlen(argv[1]))){ 17: printf("Usage: %s pattern\n\n", argv[0]); 18: exit(1); 19: } 20: /* 入力行がある限り繰り返し照合を行なう */ 21: while (NULL != fgets(text, textMax, stdin)){ 22: /* 入力行が改行のみのとき何もしない */ 23: if (strcmp(text,"\n") == 0) continue; 24: /* 照合関数を呼び出して結果を表示する */ 25: printf("pattern matched at %d places.\n", 26: match(argv[1],text) ); 27: } 28: return 0; 29: } 30: 31: int match(char pattern[], char text[]) 32: { 33: int patternLen, textLen, i, j, num; 34: patternLen = strlen(pattern); 35: textLen = strlen(text); 36: if (patternLen > textLen) return 0; 37: i = 0; j = 0; num = 0; 38: while(j < textLen){ 39: if (pattern[i] == text[j]){ 40: i++; j++; 41: if (i == patternLen){ 42: num++; i = 0; 43: } 44: }else{ 45: j = j - i + 1; i = 0; 46: } 47: } 48: return num; 49: } プログラムの解説 1)main 関数の引数 プログラムの起動パラメータ(例えば "cp file1 file2" における file1, file2) は main 関数の引数として利用できます(10行目).引数は2つで,次のような 意味をもっています. * argc は整数 (int) 型引数で,コマンド引数の個数をあらわす.ただし コマンド名自体も1個と勘定する. * argv は文字型2次元配列(これは実は正しくないが,当座はそのように 思っていても差し支えない),argv[i] は文字列で argv[0] -- コマンド名そのもの argv[1] -- 第1パラメータ argv[2] -- 第2パラメータ ... ... をあらわす. プログラムの main 部分ではコマンド引数を利用する処理を行なっています.また, strlen は文字列の長さを返す関数です. 2)文字列入力とパターン照合関数の呼び出し 21行から27行では,標準入力からテキストを1行ずつ入力して照合文字列を探 し,見つかった個数を表示する,という処理を繰り返しています.21行では文字 列の入力を fgets 関数で行ない,その結果が NULL という特別な値(^D で入力を 中止するとき,この値が返る)であるかどうかを調べています. * fgets 関数 fgets(s, n, file) s -- 文字配列変数(char [] 型) n -- 整数(int) file -- ファイルポインタ(FILE * 型) ファイルから文字配列変数に最大 n-1 文字(バイト)を読み込む 23行は空行(改行のみの行)が入力された場合にはそれ以上何もしないで while ループを続行することを指示するものです.空行であるかどうかは文字列比較関数 strcmp を使って判断しています. * continue による繰り返しの続行 while (...) { ... if (...) continue; ... } for 文の場合も同様 * strcmp(s1,s2) s1, s2 はともに定数文字列または文字配列変数 s1 と s2 が一致すれば 0 を返す 3)文字列の照合 31行ー49行は照合を行なう関数の定義です.照合パターンと対象テキストをそ れぞれ配列として受け取り,照合結果(見つかったパターンの個数)を返します. 照合のアルゴリズムは素朴なもので,照合パターンと対象テキストを整数変数 i,j で指しながら一文字ずつ一致を調べるだけです.41行ー42行は最終的に照合に 成功した場合の処理です. 3.文字および文字列処理関数 文字や文字列の処理は決まりきったものが多く,しかも頻繁に利用されるものなの で,C言語の標準ライブラリとしてさまざまな処理関数が用意されています.自分 で処理関数を書いてみることはそれなりに勉強になりますが,普通はむしろ標準で 用意されている関数を利用するほうがよいでしょう. 1)1文字処理函数 以下はC言語の標準ライブラリに用意されている1文字処理函数で,いずれも引数 と返戻値ともに int 型です.ただし"is..." という名前の函数は返戻値として !0 (真), 0 (偽)の2つのみをとります.こういう関数は条件式を構成するために 利用されます. 使用するためにはファイルの先頭に #include という一文をおきます. isalnum(c) --- c が英字あるいは数字のとき !0 isalpha(c) --- c が英字のとき !0 iscntrl(c) --- c が制御文字のとき !0 isdigit(c) --- c が数字のとき !0 isgraph(c) --- c が空白以外の印字可能な文字のとき !0 isprint(c) --- c が空白も含め印字可能な文字のとき !0 ispunct(c) --- c が空白,英字,数字以外の印字可能文字のとき !0 isspace(c) --- c が空白,タブ,改行,復帰,改めべーじのとき !0 isupper(c) --- c が英大文字のとき !0 isxdigit(c) --- c が16進数字のとき !0 tolower(c) --- 英字 c を小文字に変換して返す toupper(c) --- 英字 c を大文字に変換して返す 2)文字列処理関数 以下はC言語の標準ライブラリに用意されている文字列処理関数です.使用するた めにはファイルの先頭に #include という一文をおきます. 【代表的なもの】 ★ strlen(s) 引数: 定数文字列または文字配列変数 機能: s の文字数 (int) を返す ★ strcpy(s1,s2) 引数: s1 は文字配列変数,s2 は定数文字列または文字配列変数 機能: s2 を s1 にコピーし,s1 のアドレスを返す ★ strcat(s1,s2) 引数: s1 は文字配列変数,s2 は定数文字列または文字配列変数 機能: s2 を s1 に連結し,s1 のアドレスを返す ★ strcmp(s1,s2) 引数: s1, s2 はともに定数文字列または文字配列変数 機能: s1 と s2 をアスキーコード表に従う辞書式順序でで比較し, s1 > s2 ならば正整数 (int),s1 == s2 ならば0,s1 < s2 ならば負整数 (int) を返す.(値は文字列によって変わる. どういう値になるかは実験して調べてみればわかる.) 【それらの変形】 strcpy, strcat は渡された配列の長さを考慮しないので,用意された配列の領域 を越えて書き込んでしまう恐れがあります.これを避けるには以下に示すような, 書き込む文字数を指定できる関数を使います. ★ strncpy(s1,s2,n) 引数: s1, s2 は strcpy と同じ,n は整数 (int) 機能: s2 から最大 n 文字を s1 にコピーし,s1 のアドレスを返す ★ strncat(s1,s2,n) 引数: s1, s2 は strcat と同じ,n は整数 (int) 機能: s2 から最大 n 文字を s1 に連結し,s1 をのアドレスを返す ★ strncmp(s1,s2,n) 引数: s1, s2 は strcmp と同じ,n は整数 (int) 機能: s1 と s2 から最大 n 文字を切り出し,strcmp と同様に比較 【その他】 次の関数は入出力先が文字列変数であることを別にすれば printf や scanf と同じ 機能を持ちます.空白などで区切られた単語や数値データを扱うときに使います. ★ sprintf(s,format,...) 引数: s は文字列変数,format 以下は printf と同じ 機能: printf と同様の書式で文字列を生成し,それを s に書き込む. ★ sscanf(s,format,...) 引数: s は文字列変数,format 以下は scanf と同じ 機能: scanf と同様の書式で s からデータを読みとり,後続のアドレス (の変数)に書き込む. 【注意】 ◆strcpy, strcat, strncpy, strncat の第1引数は文字配列変数として 用意されなければならない.そのとき,コピーや連結の結果できる文字列 ('\0'も含めた)の長さが用意された配列の長さを越えてはならない. ◆strcpy, strcat, strncpy, strncat の返戻値は利用しなくてもよい. たとえば strcpy(s,"Hello,world"); strcat(s,"!\n"); という具合に,手続きとして呼び出してよい.