13.文字列処理関数

文字処理は,既製の文字処理関数を使って処理するのが一般的です.

 13.1 基本的な文字処理関数
 これまで,printf()関数や,sin(),log()などの数学関数を使ってきました.これらは,C言語のライブラリーファイルに最初から用意された関数でライブラリ−関数と呼ばれます.
ライブラリ関数には文字列処理のための関数も用意されています.
通常,前章で作成したような関数は自作する必要はなく,ライブラリ関数を使えば良いのです.
次の表に基本的な文字処理関数を紹介します.これらの他にも多数の文字処理関連の関数があります.一度,書籍で調べてみてください.


注)表中のp,p1,p2は,いずれも文字列を指すポインターを表します.
また,これらの文字処理関数を使用するときは,プログラムのヘッダー部に<string.h>を含める必要があります.

文字処理関数  説 明
int strlen(const char *p) ポインターpで指定される文字列の長さを求める.戻り値は文字列長.
char *strcpy(const char *p1, const char *p2) p2の文字列をp1にコピーする.戻り値はp1.
char *strcat(const char *p1, const char *p2) p1の指す文字列にp2の文字列を連結する.戻り値はp1.
char *strchr(const char *p, int c) 文字列pの中から文字cを検索する.戻り値はp中で最初に見つかったcへのポインタ(アドレス). 文字が見つからないと NULLポインタを返す.引数の文字がint型になっているが,char型のc='x'でも同じこと.
char *strstr(const char *p1, const char *p2) p1の文字列中にp2の指す文字列(部分文字列)を検索する.戻り値は最初に見つかった部分文字列への ポインタ(アドレス),見つからなければNULLポインタを返す.
int strcmp(const char *p1, const char *p2) 文字列p1とp2とを比較し,一致すれば0を返し,一致しなければ正または負の値を返す.
char *strncpy(const char *p1, const char *p2) p2の最初のn個の文字列をp1にコピーする.戻り値はp1.
char *strncat(const char *p1, const char *p2) p1の指す文字列にp2の先頭からn個の文字を連結する.戻り値はp1.
int strncmp(const char *p1, const char *p2) 文字列p1とp2の先頭からn個の文字を比較し,一致すれば0を返し,一致しなければ正または負の値を返す.



 13.2 文字処理関数を使うための基礎

 文字処理関数は,定義をみれば,一見,単純な機能しか持たないようにみえますが,工夫すれば,いろんな処理が可能です.ここでは,strcpy()関数を例に,文字処理関数の使い方について考えてみましょう.

strcpy()関数の定義は次のようなものでした.
char *strcpy(const char *p1, const char *p2) p2の文字列をp1にコピーする.戻り値はp1.
 この定義を単純に受け取れば,すぐに思いつくのは,プログラム例13.2.1のような処理でしょう.
書き方は,色々ありますがプログラム中の1),2),3)のいずれも,文字配列bの内容をそっくり配列aにコピーします.


 プログラム例 13.2.1  実行結果
#include <stdio.h>
#include <string.h>

void main(void){
   char a[21];
   char b[11] ={"program"};
   char *ap=a, *bp=b;
   
   strcpy(a, b);        /*書き方 1*/
   printf("1)a: %s\n".a);
   
   strcpy(&a[0], &b[0]);/*書き方 2*/
   printf("2)a: %s\n".a);
   
   strcpy(ap, bp);      /*書き方 3*/
   printf("3)a: %s\n".a);
   
   /*書き方 4*/
   printf("4)a: %s\n".strcpy(a, b));
}
1)a: program
2)a: program
3)a: program
4)a: program


 strcpy()の引数は,2つともポインター変数(アドレス)と定義されています.上のプログラム例では,引数として陽にポインター変数を書き込みましたが,次のようにダブルクオーツで囲んだ文字列定数を引数として使うこともできます.
   strcpy(a, "program")
この表現は,文字型配列を初期化するのによく用いられます.
もちろん,"program"と書くことによって,文字列"program"の先頭アドレスがstrcpy()関数に渡されることになります.
プログラム例13.2.2に使い方の例を示します.

 プログラム例 13.2.2  実行結果
#include <stdio.h>
#include <string.h>

void main(void){
   char a[21];
   char *ap=a;
   
   strcpy(a, "program"); /*書き方 1*/
   printf("a: %s\n",a);

   strcpy(ap, "program");/*書き方 2*/
   printf("a: %s\n",ap);

   /*書き方 3*/
   printf("a: %s\n",strcpy(a, "program"));
}
a: program
a: program
a: program


 ここまでの説明を読めば,strcpy()関数は,コピー元の文字列をそっくりそのままコピー先の配列へコピーし,全く同じ文字列データを2組作成作るための関数とのイメージが与えたかも知れませんが,次のような使い方もできます.
   strcpy(&a[4], b)
   strcpy(a, &b[5])
   strcpy(&a[4], &b[5])

このことを応用すれば,次のような処理を行うこともできます.

 プログラム例 13.2.3  実行結果
  #include <stdio.h>
  #include <string.h>

  void main(void){
      char a[101]={"Yamamoto Hanako"};
      char b[41] = {"TARO"};

      printf("a:%s\n", a);
      strcpy(&a[9], b);
      printf("a:%s\n", a);
  }
a:Yamamoto Hanako
a:Yamamoto TARO



 しかし,プログラム例13.2.3は,a[9]などの固定された表現があって,汎用性がありません.そこで,strchr()関数を使って,もうちょっとスマートに書き換えたのがプログラム例13.2.4です.これだと,姓と名前の間に半角のスペースを置くというルールにしたがうデータすべてに処理を適用できます.
もちろん,strchr()関数を使って半角スペース文字の位置(アドレス)がわかったら,その次の文字の位置から配列bの内容のコピーをすることになります.

 プログラム例 13.2.4  実行結果
  #include <stdio.h>
  #include <string.h>

  void main(void){
      char a[101]={"Yamamoto Hanako"};
      char b[41] = {"TARO"};
      char *cp;

      printf("a:%s\n", a);

      /*1)スペースの位置をみつける*/
      cp = strchr(a, ' ');

      /*2)コピー開始位置へcpを進める*/
      cp++;
               
      /*3)cpの位置へ文字列をコピー*/
      strcpy(cp, b);
      printf("a:%s\n", a);
  }
a:Yamamoto Hanako
a:Yamamoto TARO




 13.3 文字列の検索

 こんどは,文字列の中から,特定の部分文字列を検索するプログラムです.文字列処理関数としては,strstr()関数を使ってみます.
char *strstr(const char *p1, const char *p2)  p1の文字列中にp2の指す文字列(部分文字列)を検索する.戻り値は最初に見つかった部分文字列へのポインタ(アドレス),見つからなければNULLポインタを返す.

 プログラムの機能は,4件の文字列のなかから"Tanaka"という部分文字列を含む文字列をすべて見つけだし,それらの文字列全体を表示することとします.簡単のため,部分文字列"Tanaka"を含む文字列が見つからなければ何も表示しないことにします.
 strstr()数は,文字列が見つからなければNULL(もちろん,NULLは'\0'のこと)を返しますので,if()文の条件式は次のよう書くことができます.もちろん,プログラム中の変数fcountは,部分文字列の見つかった文字列の数をカウントする変数です.


 プログラム例 13.3.1  実行結果
#include <stdio.h>
#include <string.h>

void main(void){
   int nrec=4; 
   char *recp[4]={{"02,001,Suzuki Taro"},
                  {"02,002,Tanaka Jiro"},
                  {"02,003,Yamamoto Ichiro"},
                  {"02,004,Tanaka Goro"}};
   int fcount;
   int i;

   fcount = 0;
   for(i = 0; i < nrec; i++){
      if(strstr(recp[i], "Tanaka")){
         fcount++;
         printf("rec.%3d)%s\n",i, recp[i]);
      }
   }
   printf("%d件の文字列が見つかりました.",fcount);
}
rec.  1)02,002,Tanaka Jiro
rec.  3)02,004,Tanaka Goro
2件の文字列が見つかりました.


 検索処理部分を関数化すれば,プログラム例13.3.2のように書くことができます.


 プログラム例 13.3.2  実行結果
#include <stdio.h>
#include <string.h>
void search_rec(int nrec, char **recpp);

void main(void){
   int nrec = 4; 
   char *recp[4]={{"02,001,Suzuki Taro"},
                  {"02,002,Tanaka Jiro"},
                  {"02,003,Yamamoto Ichiro"},
                  {"02,004,Tanaka Goro"}};

   search_rec(nrec, recp);
}

void search_rec(int nrec, char **recpp){
   int i;
   int fcount=0;
   for(i = 0; i < nrec; i++){
      if(strstr(*recpp, "Tanaka")){
         fcount++;
         printf("rec.%3d)%s\n",i, *recpp);
      }
      recpp++;
   }
   printf("%d件の文字列が見つかりました.\n",fcount);
}
rec.  1)02,002,Tanaka Jiro
rec.  3)02,004,Tanaka Goro
2件の文字列が見つかりました.



 13.4 文字列を切りとる

 特に,文字列を扱う場合,文字列の一部を切り取る処理が必要となります.
いろんな書き方ができると思いますが,1例をプログラム例13.4.1に示します.一見,面倒な処理のように見えますが,理解を試みてください.考え方は単純です.ポイントは,カンマ','を'\0'で置き換えていることです.
プログラム例13.4.1の文字列データ"02,001,Suzuki Taro"を例に処理の手順を書いておきます.プログラムと比べながら理解を試みてください.

     (recpで指定される)元のデータの内容を壊さないために作業用変数workにコピーする
     headpとtailpをworkの先頭アドレスに設定する.

     部分文字列間の','の位置をstrchr()関数で見つけ,tailpに記憶させる.
     tailpで指す','を'\0'で置換する.この処理で"02'\0'001,Suzuki Taro"となる.
     strcpy(gakunen,headp)で変数gakunenに文字列"02"がコピーされる.

     tailpをインクリメントすることで"001,Suzuki Taro""の先頭の文字位置へ進める.
     headpにtailpをコピーする.
     部分文字列間の','の位置をstrcpy()関数で見つけ,tailpに記憶させる.
     tailpで指す','を'\0'で置換する.この処理で"02'\0'001'\0'Suzuki Taro"となる.
     strcpy(no,headp)で変数noに文字列"002"をコピーする.

     tailpをインクリメントすることで"Suzuki Taro"の先頭の文字位置へ進める.
     strcpy(simei,tailp)で変数simeiに"Suzuki Taro"をコピーする.

実は,このプログラムについては,説明すべき事がいろいろありますが,それは,次章の「ファイル」で説明します.

 プログラム例 13.4.1  実行結果
#include <stdio.h>
#include <string.h>

void main(void){
   int nrec = 4;
   char *recp[4]={{"02,001,Suzuki Taro"},
                  {"02,002,Tanaka Jiro"},
                  {"02,003,Yamamoto Ichiro"},
                  {"02,004,Tanaka Goro"}};
   char gakunen[4][3],no[4][4], simei[4][21]; 
   char work[51];
   char *headp, *tailp;
   int i;

   for(i=0; i<nrec; i++){
      strcpy(work, recp[i]);
      printf("%d)%s\n",i,work);
      headp=work; tailp=work;

      tailp=strchr(tailp, ',');  *tailp='\0';
      strcpy(&gakunen[i][0], headp);
      printf("%d)%s\n",i, &gakunen[i][0]);

      tailp++;   headp=tailp;
      tailp=strchr(headp, ',');  *tailp='\0';
      strcpy(&no[i][0],headp);
      printf("%d)%s\n",i, &no[i][0]);

      tailp++;
      strcpy(&simei[i][0],tailp);
      printf("%d)%s\n\n",i, &simei[i][0]);
   }
}
0)02,001,Suzuki Taro
0)02
0)001
0)Suzuki Taro

1)02,002,Tanaka Jiro
1)02
1)002
1)Tanaka Jiro

2)02,003,Yamamoto Ichiro
2)02
2)003
2)Yamamoto Ichiro

3)02,004,Tanaka Goro
3)02
3)004
3)Tanaka Goro



 13.5 キーボードから文字列を入力する
 ここまでのプログラムでは,文字列や数値などのデータを代入演算子を使って,直接変数に書き込んできました.
こんどは,キーボードから文字列データを入力し,変数に代入するライブラリー関数を紹介します.
次のgets()関数は,文字列をキーボードから受け取り,変数に代入する関数です.
char *gets(const char *ptr) キーボード から 1 行を読み込み、文字列変数に格納します。ここで1行とは[Enter(改行)]が押されるまでを言い,改行記号の位置にはNULL(\0)が代入される。
また,文字列をディスプレィに表示するときは,printf()関数の代わりに,次に示すputs()関数を使うこともできます.
char *puts(const char *ptr) デスプレイに文字列を出力します.出力の際,文字列の終端 NULL 文字 ('\0') を改行文字 ('\n') に置き換えます。

これら2つの関数を使い方を理解するためのプログラムを例13.5.1に示します.gets(),puts()関数の扱えるデータは文字列と決まっているので%sなどの書式指定子は不要ということになります.
このようにコンピュータにデータを入力したりディスプレィに出力したりする関数のことを入出力関数と呼びます.もちろん,printf()関数も出力関数のひとつです.
入出力関数にはいろいろありますが,必要になった時点で紹介します.


 プログラム例 13.5.1  実行結果
#include <stdio.h>

void main(void){
   char string[101];
  
   printf("文字を入力後,改行キーを押して下さい\n");
   gets(string);
   puts(string);
}
文字を入力後,[Enter]キーを押してください
abc,#!12345hsdfgklhdfgjklgdf
abc,#!12345hsdfgklhdfgjklgdf



プログラム例13.3.1を書き換えて,検索する部分文字列をキーボードから指定できるように書き換えてみます

 プログラム例 13.5.2  実行結果
#include <stdio.h>
#include <string.h>

void main(void){
   int nrec=4; 
   char *recp[4]={{"02,001,Suzuki Taro"},
                  {"02,002,Tanaka Jiro"},
                  {"02,003,Yamamoto Ichiro"},
                  {"02,004,Tanaka Goro"}};
   int fcount;
   int i;
   char keyword[21];

   /*検索するkeyの入力*/
   printf("検索する文字列を入力して下さい\n");
   gets(keyword);

   fcount = 0;
   for(i = 0; i < nrec; i++){
      if(strstr(recp[i], keyword)){
         fcount++;
         printf("rec.%3d)%s\n",i, recp[i]);
      }
   }
   printf("%d件の文字列が見つかりました.",fcount);
}
検索する文字列を入力して下さい
Tanaka
rec.  1)02,002,Tanaka Jiro
rec.  3)02,004,Tanaka Goro
2件の文字列が見つかりました.




 13.6 文字(数字)から数値への変換

 文字型配列に記憶された数字は,単なる文字列で,演算に使用することはできません.プログラムを作成するとき,数字の並びとしての文字列を数値に変換する場合がしばしばあります.ここでは,文字列を数値に変換するC言語のライブラリー関数を紹介します.
 文字列を数値に変換するには,整数の場合と実数の場合とに分けて扱い,それぞれ,次のようなライブラリー関数を使用します.
     atoi(string)   (ascii to integer)
     atof(string)   (ascii to floating point num.)
 それぞれの関数は,次のように定義されています.インクルードファイルに注意.

int atoi(const char *ptr) 文字列として入力された整数を表す数字の列を整数値に変換する.戻り値は変換された整数値.stdlib.hが必要

double atof(const char *ptr) 文字列として入力された実数を表す数字の列を数値に変換する.戻り値は変換された実数値.stdlib.hとmath.hが必要.

関数の定義を読めば,使い方についての説明は,不必要かと思いますが,プログラム例13.6.1と13.6.2に簡単なプログラム例を示します.

 プログラム例 13.6.1  実行結果
#include <stdio.h>
#include <stdlib.h>

void main(void){
    char astr[16], bstr[16];
    int a, b;

    printf("2数の演算を行います.\n");
    printf("aの値を入力してください:");
    gets(astr);
    printf("bの値を入力してください:");
    gets(bstr);

    a = atoi(astr);
    b = atoi(bstr);
    printf("a = %d, b = %d\n", a, b);
    printf("a + b = %d\n", a + b);
    printf("a - b = %d\n", a - b);
    printf("a * b = %d\n", a * b);
}
2数の演算を行います.
aの値を入力してください:234
bの値を入力してください:15
a = 234, b = 15
a + b = 249
a - b = 219
a * b = 3510




 プログラム例 13.6.2  実行結果
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

void main(void){
    char astr[16], bstr[16];
    double a, b;

    printf("2数の演算を行います.\n");
    printf("aの値を入力してください:");
    gets(astr);
    printf("bの値を入力してください:");
    gets(bstr);

    a = atof(astr);
    b = atof(bstr);
    printf("a = %f, b = %f\n", a, b);
    printf("a + b = %f\n", a + b);
    printf("a - b = %f\n", a - b);
    printf("a * b = %f\n", a * b);
}
2数の演算を行います.
aの値を入力してください:1.23
bの値を入力してください:2.5
a = 1.230000, b = 2.500000
a + b = 3.730000
a - b = -1.270000
a * b = 3.075000






 13.7 数値から文字列字への変換

 今度は,前節とは逆に数値を数字で構成される文字列へ変換する関数を紹介しましょう.これも,実用的なアプリを作成するときよく使われる処理です.
Microsoft社系のコンパイラー,VisualC++などには,そのような処理行うための独自のライブラリー関数がありますが,ここでは,一般的なsprintf()関数を紹介します.でも,この関数は,本来,数値を数字へ変換するための関数と言うよりprintf()関数の出力をデスプレィに表示する代わりに,出力内容そのままを文字列化するための関数です.
sprintf()関数の定義は次のとおりです.

int sprintf( char *buffer, const char *format [, argument] ... ) 書式指定指定される出力内容を文字列化し,文字列変数bufferに代入する.戻り値はbufferに出力された文字の数.stdlib.hが必要

引数部分が,わかりにくいと思いますが,printf()とsprintf()関数の書き方を比べれば,理解できると思います.
   printf("a=%5d, b=15.7f\n", a, b);
   sprintf(char *buffer, "a=%5d, b=15.7f\n", a, b);
 定義中のconst *formatは,print()関数やsprintf()関数のダブルクオーツ(””)で囲まれた書式(format)指定部分のことです.また,sprintf()の最初の引数char *bufferは,出力先の文字列変数のことです.
また,
  [, argument] ... の部分は,引数(argument)をカンマで区切っていくつでも並べられると言うことを意味します.

sprintf()関数を使って,数値を文字列に変換する例をプログラム例13.7.1に示します.

 プログラム例 13.7.1  実行結果
#include <stdio.h>
#include <string.h>

void main(void){
  char str[51];
  int a, b;

  a=23;

  /* 桁指定なしの場合 */
  printf("1)整数桁指定無しの場合\n");
  sprintf(str, "%d", a);
  printf("文字列長%d桁:%s\n",strlen(str), str);

  /* 桁指定をした場合 */
  printf("2)整数桁指定有りの場合\n");
  sprintf(str, "%5d", a);
  printf("文字列長%d桁:%s\n",strlen(str), str);
}
1)整数桁指定無しの場合
文字列長2桁:23
2)整数桁指定有りの場合
文字列長5桁:   23


桁指定子をつかうと,空白桁が出力されますが,atof()やftoa()は,このようにして得られた文字列としての数字をちゃんと数値に変換してくれます.
また,上記プログラム中のprintf()中の文字列長は,strlen()関数を使わなくても,sprintf()の戻り値から得ることができます.



 13.8 何でも文字列化してしまうsprintf()関数
 先ほども述べましたが,実は,sprintf()関数は,printf()の出力を文字型配列へ代入する関数なのです.
したがって,文字と数値の組み合わせを1個の文字列に変換してしまう目的で使用されます.
プログラム例13.8.1に例を示します.注意すべき点は,書式指定中に\nがあれば,変換後の文字列中にもそれが組み込まれ,print時にちゃんと機能するということです.


 プログラム例 13.8.1  実行結果
#include <stdio.h>
#include <string.h>

void main(void){
  char str[51];
  int a=2, b=5;

  printf("1)書式指定に改行記号を含まない場合\n");
  sprintf(str, "a=%d  b=%d", a, b);
  printf("文字列の表示\n");
  printf("%s\n", str);

  printf("\n\n");

  printf("2)書式指定に改行記号を含む場合\n");
  sprintf(str, "a=%d\nb=%d", a, b);
  printf("文字列の表示\n");
  printf("%s\n", str);
}
1)書式指定に改行記号を含まない場合
文字列の表示
a=2  b=5


2)書式指定に改行記号を含む場合
文字列の表示
a=2
b=5