● 文字列の操作

プログラムは数字を計算するだけではなく文字を操作することも できます。この章では文字列の管理や表示の方法について解説します。


■ 文字型変数 char

数字の値を記憶する型の変数があると同様に文字の値を記憶する型の 変数があります。それがcharです。ここで「文字の値」とは各文字に あらかじめ決められた1byteの値です。現在はこの値は ASCIIによって 定められたコードである ASCII codeであるのが普通です。 例えば、文字 A の ASCII code の値は10進数では65です。 16進数では41です。プログラム中では 'A' とすることでこの ASCII code を表すことができます。

簡単な例を示します。

#include<stdio.h>
	
int main(void)
{
  char c;
  c = 'A' ;
  printf("%d %x %c\n", c, c );

  return(0);
}
このプログラムの出力結果は 65 41 A です。 printf関数の%cは文字形式で出力することを意味します。 つまり与えられた変数の数値をASCII codeとして対応する文字にして 変換して表示するのです。


■ 文字列を格納する変数

char型変数では1文字しか扱えません。複数の文字が連なったもの、 つまり文字列を扱うには配列変数を使います。char型の配列を用意して、 そこに順番に文字を格納していくのです。
配列の初期化を思い出してください。

int data[5] = {0,10,20,30,40};
これでint型配列dataが、data[0]に0が、data[4]に40が、という 具合に初期化されます。

これと同様に文字型配列も初期化できます。

char string[5] = {'a','b','c','d','e'};
これで配列 string は abcde になったわけです。

しかしこれは記述が面倒です。次のように簡単に記述することもできます。

char string[6] = "abcde";
"abcde"の文字列はプログラマの見えない別な 所にあらかじめ保管されていて、それがここで内部コピー関数に よって配列にコピーされるのです。 この時、配列stringの6文字目に'\0'という特殊な文字が自動的に代入 されます。これはnull terminater と呼ばれる文字列の終端を表す文字 なのです。

次のように文字列を代入することはできません。

char string[6];
string = "abcde";
しかし次のようにすると"代入"できます。
char* string;
string = "abcde";
前者のstringが配列変数の先頭ポインタであるのに対して 後者のそれは自由なポインタです。"abcde"の文字列の先頭アドレスを ポインタに代入することで結果的に配列に文字列が格納されたことと 同様になるのです。ただしこの文字列を書き換えたり追加したり することは危険です。期待通りに行われる保証は全くありません。

文字列をprintfで出力するときにはこうします。

printf("%s\n", string );
配列の名前そのものは、その配列の先頭アドレスを表すことを 思い出してください。%sとすると、先頭アドレスから順に文字を 表示していって'\0'は表示しないで終了します。このためにも null terminater '\0' が必要なのです。

2次元配列を使うことにより複数の文字列を一挙に扱うことができます。

char strings[5][10] = {"Red","Blue","Green","Yellow","Pink"};
strings[][]の1番目の数字は文字列の数であり、2番目の数字は 個々の文字列の長さの最大値です。この文字列を表示するには 次のようにします。
int i;
for( i=0 ; i<5 ; i++ ){
  printf("%s\n", strings[i] );
}

ポインタ配列を利用して複数の文字列を管理することができます。

char* ptr[5] = {"Red","Blue","Green","Yellow","Pink"};
2次元配列にコピーしたものと全く同様に扱うことができます。 ただしこれらの文字列を書き換えたり追加したり することは危険です。期待通りに行われる保証は全くありません。


■ 文字列操作関数の紹介

一般に配列のすべての要素の値を他の配列の要素に一度にコピーする ことはできません。要素一つ一つをコピーしていくしかありません。
文字列のコピーも同じです。そのため専用の文字列操作関数がいくつか string.hに用意されています。

  • strcpy(文字列コピー)
    char* strcpy( char* str1, const char* str2 );

    str1はコピー先の文字配列の先頭アドレスです。
    str2はコピー元の文字配列の先頭アドレスです。
    返り値はコピー先の文字配列の先頭アドレスです。

    使用例:
        char a[10],b[10],c[10]="abcde";
        
        strcpy( a, c );
        strcpy( b, "fghij" );
        

  • strcat(文字列連結)
    char* strcat( char* str1, const char* str2 );

    str1は連結先の文字配列の先頭アドレスです。
    str2は連結元の文字配列の先頭アドレスです。
    返り値は連結先の文字配列の先頭アドレスです。

    この関数はstr1の配列の'\0'のところからstr2の文字列を コピーしていきます。したがって、str1はstr2の文字列を 付け足せるのに十分な配列でなければなりません。

    使用例:
        char a[10];
        
        strcpy( a, "abc" );
        strcat( a, "def" );
        

  • strcmp(文字列比較)
    int strcmp( const char* str1, const char* str2 );

    str1,str2は比較する文字配列の先頭アドレスです。
    返り値は比較の結果です

    この関数は2つの文字列が辞書順にしてどちらが先かを調べます。 str1の方が後ならば正の値が返り、str1の方が前ならば負の値が 返ります。同じ文字列ならば 0 の値が返ります。その値は具体的には 最初の異なる文字のascii codeの引き算です。

    使用例:
        char a[10],b[10];
        
        strcpy( a, "abc" );
        strcpy( b, "acd" );
        if( strcmp( a, b ) == 0 ){
           printf("a is equal to b. \n");
        }
        

  • 指定文字数のみの操作
    上記3つの関数の拡張型として指定文字数のコピー、連結、比較を 行う関数が用意されています。
    char* strncpy( char* str1, const char* str2, int n );
    char* strncat( char* str1, const char* str2, int n );
    int strncmp( const char* str1, const char* str2, int n );

  • ■ 文字列入出力関数の紹介

    文字列を入出力する関数の代表格であるprintf関数とscanf関数には 実にさまざまな細かい設定があります。それらの紹介と他の 文字列入出力関数を紹介します。

  • printf(書式付き出力)
    int printf( const char* format [, arg1,arg2,... ] );

    formatは書式を制御する文字列です。 arg1,arg2,...は出力する変数の値です。 返り値は出力した文字数です。
    第1引き数である固定文字列が表示されます。

    使用例:
        printf("hello, world!\n");
        
    '%'以降の文字は書式制御文字として第2引き数以降の引き数に 対して特別な意味を持ちます。
        int a = 10;
        printf("a = %d \n", a );
        
    '%'の後ろの'd'の持つ意味は、第2引き数のaの値がsigned int型として 10進数でここに表示されるということです。この'd'のような 文字を型指定文字と呼びます。

  • 型指定文字の一覧です。
    変数を signed int 型として扱うもの
  • d …符号付き10進整数として表示することを指定する。
  • i …符号付き10進整数として表示
    変数を unsigned int 型として扱うもの。
  • u …符号なし10進整数として表示
  • o …符号なし 8進整数として表示
  • x …符号なし16進整数として小文字で表示
  • X …符号なし16進整数として大文字で表示
    使用例:
            unsigned int a = 10;
            printf("%d %u %o %x %X \n", a,a,a,a,a );
            結果:10 10 12 a A
            
    変数を float 型として扱うもの
  • f …浮動小数点形式で表示
  • e …指数形式で小文字で表示
  • E …指数形式で大文字で表示
  • g …短い値はf形式で、長い値はe形式で表示
  • G …短い値はF形式で、長い値はE形式で表示
    使用例:
            float f = 3.14;
            printf("%f %e %E %g %G \n", f,f,f,f,f );
    
            結果:3.14 3.14e+00 3.14E+00 3.14 3.14
            
    変数を char 型として扱うもの
  • c …ascii codeを対応する文字で表示
    変数を char 配列の先頭ポインタ型として扱うもの
  • s …ポインタが指す文字列を'\0'の手前まで表示
    変数をポインタ型として扱うもの
  • p …値をアドレス表記で表示
    使用例:
            int *p;
            printf("%p \n", p );
    
            結果:0000
            
    その他
  • n …次の%までに出力した文字数を対応した引き数に代入することを指定する。
  • 型指定文字に修飾文字を付けて機能を拡張/限定すること ができます。型指定文字の直前に次の修飾文字を付けます。
  • F … p に対して farポインタ指定に拡張(16bit環境でのみ有効)
  • N … p に対して nearポインタ指定に限定(16bit環境でのみ有効)
  • l … d,i,o,u,x,X に対し long int 指定、e,E,f,g,G に対し double 指定に拡張
  • h … d,i,o,u,x,X に対し short int 指定に限定
  • L … e,E,f,g,G に対し long double 指定に拡張
    使用例:
            double d = 3.14;
            printf("%lf\n", d );
    
            結果:3.140000
            
  • 型指定文字の前に整数値によるフィールド指定文字を付けることで 表示するデータが占める文字数を指定することができます。 これにより表示データが右端に揃えられます。 指定よりデータが長い場合は指定が無視されます。
    使用例:
        int a = 10, b= 100;
        float f = 3.14;
        char str[10] = "abc";
    
        printf("%5d %5d %5f %5s\n", a, b, f, str );
    
        結果:
        __10 __100 _3.14 __abc
        
    小数に対するこの指定の文字数には符号、小数点、e、指数部も 含まれます。

  • 小数フィールド指定文字を付けて、小数に対して何桁を表示 するかを指定できます。また文字列に対して、何文字を表示するかを 指定できます。フィールド指定文字の直後に.を付けて10進整数値を 付けて指定します。
    使用例:
        float f = 3.14159;
        char str[10] = "abcde";
    
        printf("%7.2f %7.3s\n", f, str );
    
        結果:
        ___3.14 ____abc
        

  • フィールド指定の所を * とすることで後続の引数でフィールド 指定できます。 使用例:
        int size = 5;
        int a = 10;
        float f = 3.14159;
        
        printf("%*d\n", size, a );
        printf("%*.*f\n", size, size, f );
        

  • %の直後にフラグ指定文字を付けて、出力の先頭の形状を 指定できます。
  • -  …フィールドの左端にそろえて出力するよう指定。
  • +  …数値の直前に+−の符号を付加
  • space…正の数の場合には符号位置に空白を付加
  • #  …型の指定によって意味が以下のように変わります。
  • o型:出力データの前部に0を付加
  • x,X型:出力データの前部に0x,0Xを付加
  • f,e,E,g,G型:どんな場合でも小数点を付加
  • 他の型の場合には意味を持ちません。
  • 使用例:
            int a = 10;
            printf("%-4d %+d % d \n", a,a,a );
    
            結果:10__ +10 _10
            
  • scanf(書式付き入力)
    int scanf( const char* format[, arg1,arg2,... ] );

    formatは書式を制御する文字列です。 arg1,arg2,...は入力する変数のアドレスです。 返り値は正常に読み込まれた項目数です。

    キーボードから入力した文字列がformatの書式に従って後続の引数に 格納されます。formatの書式はprintfとだいたい同じですが、 さまざまな規約があります。

    • 1文字入力の指定以外では、入力の際のスペース、リターンは読み 飛ばされます。
    • 2つのint型を読み込む場合には、入力の際にその2つの数字が区別で きなくてはなりません。この区切り記号はスペースまたはリターンで す。区切り記号が複数つながっていてもかまいません。
    • 型の大きく異なる2つのデータを読み込む場合には、区切り記号が無く ても区別でき、正常に読み込まれる場合もあります。
    • 例えば%d:%dのように、format内にスペース以外の文字を入れると、 この文字が区切り記号として扱われます。つまり、データの形式が より厳密になります。
    • %*dのように *を付けた項目は変数に格納されずに読み飛ばされます。
    • 入力した文字列がscanfによって実際に格納され始めるのは リターンキーを押してからです。なのでリターンを押す前なら、 入力した文字列をBS,DELキーによって修正することができます。
    • formatの書式に合わない入力があると、その入力データはそのまま 放置され、以後の入力データがすべて詰まって読み込めない状態 となってしまいます。なので scanfを使用した後にはその戻り値が 期待の値であるかをチェックして、もし、期待外れであれば、 rewind(stdin);を唱えて詰まった入力データを破棄して下さい。

    使用例:
        char str[10];
        scanf("%s", str );
    
        int a;
        if( scanf("%d", &a ) != 1 ){
          printf("data mismach!! \n");
          rewind(stdin);
        }
    	
        int a,b;
        if( scanf("%d %d", &a,&b ) != 2 ){
          printf("data mismach!! \n");
          rewind(stdin);
        }
        


  • ■ データファイルの変換プログラムを作る

    実践的な例を示します。物理などの実験では、計測器がファイルに 出力した大量のデータから必要なデータだけを取り出してそれを 別のファイルに格納する作業が必要になります。

    ファイルの操作には一般には次章で説明する特別な技術が 必要なのですがここではOSのリダイレクション機能を用いて それを簡単に行います。

    ある実行ファイル filter を次のように実行します

    filter < original.dat > result.dat
    こうするとデータファイル original.dat に格納されている内容が OSによって自動的にfilterプログラムに打ち込まれて、scanf()関数や gets()関数よってプログラムに読み込むことができます。またこの プログラムがprintf()関数などで出力する結果は画面に表示されずに result.dat という名前のファイルが作られてそこに格納されます。

    こうすることで original.dat の内容を読んで適切に処理して 結果を result.dat に格納することができます。

    例えば original.datの内容が次の様に一行に4つの小数がそれぞれ カンマで区切られて並んでいるとして、これの各行の第1成分と第2成分 のみを取り出してresult.dat に格納したいとしましょう。

    123.4,  23.5,  58.0, 589.0
    125.8,  21.0,  52.5, 623.1
    128.3,  19.6,  46.2, 656.2
    131.5,  16.2,  42.3, 685.6
    
    プログラム filter.c を次の様に作れば期待の処理ができます。

    #include <stdio.h>
    
    int main( void )
    {
      double d1,d2;
    
      while( scanf("%lf, %lf, %*lf, %*lf\n", &d1, &d2 ) != EOF ){
        printf("%4.1lf %4.1lf\n", d1, d2 );
      }
    
      return(0);
    }
    

    scanf()関数の第一引数の文字列の書式を original.dat の一行の 書式と同じにします。つまり4つの小数がカンマとスペースで区切られて 最後に改行があるという書式です。4つの項目のうち第3,第4項目については 値を変数に格納する必要がないので読み飛ばすことを指定する*を付けます。
    この各行ごとの操作をscanfが読み込みできなく なるまで続けます。これでできあがりです。


  • 次へ
  • 目次
    Copyright(C) by Naoki Watanabe. Oct 21st, 1995.
    This page was modified on Aug 3rd, 1998.
    渡辺尚貴 naoki@cms.phys.s.u-tokyo.ac.jp