文字列

    2012/06/11

    本章では下記の内容を学習します。

    • 文字の配列を作る: 文字列
    • 文字列を表示: printf 関数
    • 文字列の終端: NULL 文字
    • 文字列の初期化: ダブルクォーテーション
    • 自由な文字列の作成: swprintf 関数

    文字列の作成

    文字列の宣言と利用

    例1-文字列の宣言と利用
    #include <stdio.h>
    #include <locale.h>
    
    int     main( void )
    {
      // 日本語表示対応にする
      ::setlocale( 0, "JPN" );
    
      // 文字の配列を作成
      wchar_t str[4];
    
      // 文字列の各要素に文字を代入
      str[0]  = L'も';
      str[1]  = L'じ';
      str[2]  = L'れ';
      str[3]  = L'つ';
    
      // 文字の配列を1つずつ表示
      for( int i=0; i<4; i++ )
      {
          printf( "%C", str[i] );
      }
    
      return  0;
    }
    

    前回、変数に複数個の値を覚えさせる機構として配列を学習しました。1 つの変数の中に複数個分の値を覚えておく領域が作られて、それらを要素番号を通して操作することができるようになるというものでした。この配列の仕組みを上手く応用すると、文字列が作成できるようになります。

    文字列は、文字型の配列として宣言すると、作成できます。文字列とは文字が複数個並んで形作られているものですから、この作成方法は理にかなっています。

    文字を表すデータ型は、char, wchar_t の 2 つがあります。このうち私たちはwchar_t 型を積極的に利用していくことにします。wchar_t 型は 1 文字に 2 バイトの容量を使い、世界中の文字に対応する UNICODE 文字列を扱うことができる型です。

    wchar_t 型の文字を表す方法は、L'x' という形です。L は wchar_t 型を表現するためにつける先頭文字、シングルクォーテーションは挟まれている文字が文字型であることを表すためにつける記号です。挟み込む文字は基本的には何でも OK です。ただし、1 文字のみです。

    文字を配列に 1 つずつ収めていくと、文字列が形成できます。例では「もじれつ」という文字列が、4 つの要素によって形成されました。これを 1 文字ずつ printf 関数で表示しています。printf 関数で文字を表示するときは、wchar_t 型の場合は%C です。C は大文字ですから注意してください。

    練習問題: 文字列の作成
    1. 適当な 3 文字の文字列を作成し、表示してください
    2. 適当な 6 文字の文字列を作成し、表示してください

    終端文字:NULL

    例2-終端文字:NULL
    #include <stdio.h>
    #include <locale.h>
    
    int     main( void )
    {
      // 日本語表示対応にする
      ::setlocale( 0, "JPN" );
    
      // 文字の配列を作成
      wchar_t str[5];
    
      // 文字列の各要素に文字を代入
      str[0]  = L'も';
      str[1]  = L'じ';
      str[2]  = L'れ';
      str[3]  = L'つ';
      str[4]  = NULL;
    
      // 文字列を表示
      printf( "%S\n", str );
    
      return  0;
    }
    

    前回の例では、printf 関数を使って 1 文字ずつ表示を行っていましたが、これを毎回手動で行うのはなかなか大変です。そもそもこの方法では、文字列が何文字あるかを常に知っておかなければ対応できません。これらをうまく扱うための仕組みが NULL です。

    文字列では、文字の終端を表す特殊文字として、終端文字 NULL を定義しています。その中身は数値の 0です(文字の’0′ではありません)。0 という数値を見つけると終端であるとみなすという決まりをつけておくことで、便利な点がいくつか生まれます。

    あらゆる文字列操作は、NULL を区切りとして処理を終了することができます。文字列のコピーや連結などは、この NULL までを処理の区切りとして、配列操作を行えば良いということになります。また文字数を覚えておくための専用の変数を用意する必要もありません。2 つの変数を利用して 1 つの文字列を管理するのはとても手間ですから、スマートであると言えるでしょう。

    ただし、文字列に NULL という区切りを付け加えるということは、文字列は 1 文字余分に文字を記憶しなければならないということにもなります。これは仕方ない部分もありますね。

    皆さんに強く意識してほしいのは、文字列を作るときは、必ず NULL 文字を付加し、その分の領域も確保しなければならないということです。「もじれつ」なら 5 文字分必要だということです

    練習問題: 文字列の作成

    適当な 3 文字の文字列を作成し、printf( “%S\n”, str ); で表示してください

    応用問題: 文字列の自力表示

    while 文と printf( “%C”, … ); を使って、%S を使わず自力で文字列を表示してください

    文字列の操作

    文字列の初期化

    例3-文字列の初期化
    #include <stdio.h>
    #include <locale.h>
    
    int     main( void )
    {
      // 日本語表示対応にする
      ::setlocale( 0, "JPN" );
    
      // 文字の配列を作成
      wchar_t str1[5] = { L'M', L'o', L'j', L'i', NULL };
      wchar_t str2[5] = L"Moji";
      wchar_t str3[]  = L"Moji";
    
      // 文字列を表示
      printf( "%S\n", str1 );
      printf( "%S\n", str2 );
      printf( "%S\n", str3 );
    
      return  0;
    }
    

    文字列は配列同様、初期化指定子を利用することで配列要素を一括して初期化することができます。例 3 の一番上(str1)がその方法です。

    この他に、文字列には専用の初期化方法が規定されており、ダブルクォーテーションを利用することができます。ダブルクォーテーションを利用すると、挟み込んだ文字列が一括で文字配列に格納されます。例の真ん中(str2)がその方法です。なお、wchar_t 型ですので、頭に L をつけることを忘れないようにしてください。このとき、終端文字 NULL は、自動的に文字列の末尾に付け加えられます。便利ですね。

    さらに、配列には「要素数を書かなくても、初期化すれば自動的に決めてくれる」という機能がありました。これを最大限に利用した例が、例 3 の一番下(str3)になります。初期化時に文字数が 4 個の初期化を行いましたので、これで自動的に要素数が決まります。5 です。

    忘れないでほしいのが、文字列は NULL 文字も含めて文字列であるということです。このためダブルクォーテーションを使った場合でも、コンパイラが自動的に NULL を付加してくれます。配列の長さとしては「文字数+1」になりますね。

    練習問題: 文字列の初期化

    『文字列を表示』という日本語 6 文字を、上記 3 通りの方法で表示してください

    文字列の入力

    例4-文字列の入力
    #include <stdio.h>
    #include <locale.h>
    
    int main( void )
    {
      // 日本語表示対応にする
      ::setlocale( 0, "JPN" );
    
      // 文字の配列を作成
      wchar_t str[32];
    
      // 文字列を入力
      printf( "文字列を入力: " );
      fgetws( str, 32, stdin );
    
      // 文字列を表示
      printf( "入力結果:%S\n", str );
    
      return  0;
    }
    

    文字列も、プログラム実行時に入力させることができます。一般的に利用している scanf_s 関数でも可能なのですが、それよりも少しだけ便利な関数があるので、そちらを紹介します。

    fgetws 関数は、指定した文字列分だけを入力させることができる関数です。第 2 引数に必要な文字数を指定すると、それ以上を入力されても打ち切ってくれる便利な関数です。第 3 引数の stdin は、コンソール画面からという意味です。

    ちなみに、fgetws 関数の本職はファイルからの入力ですが、コンソールにも対応しているため、ここで利用しています

    例では 32 文字分の文字列を用意し、そこへ fgetws 関数で入力させています。32 文字を上回る文字列を入力したときは、それ以降の文字が自動的に切り捨てられます。

    fgetws 関数の欠点は、最後に打ちこんだ改行コードも文字として認識し、取りこんでしまう点です。ですから入力文字列の大半には、改行も含まれてしまいます。これを取り除くためには、最後の文字を NULL 文字で置き換えてやる必要があります。

    scanf_s 関数でこれを行いたい場合は…

    wscanf_s( L”%31s”, str, 32 ); と記述すると、同様のことが実行できます。

    ただし scanf_s 系の関数の共通の特徴として、スペースなどの空白系文字はすべて区切り文字として認識し、それ以降の文字を別のものとして切り離してしまうことがあります。上記の wscanf_s を利用して文字列を入力した場合、スペース以降は取り込めません。

    そういった用途の場合は、fgetws 関数を利用するか、スキャン集合を利用して wscanf_s( L”%31[^\n]“, str, 32 ); としてください。

    日本語を打ち込みたい場合は、コンソールの画面で『ALT+全角/半角』キーを押してください。日本語モードを切り替えることができるようになります。

    実践問題: 入力文字の検索

    ユーザから文字列を入力してもらい、その中に「T」という文字があるかないかを表示してください

    応用問題: fgetws 関数の改行文字を取り除く

    fgetws 関数は、ユーザが最後に入力した改行文字も取り込んでしまいます。この改行文字を取り除く処理を記述してください

    (ヒント:(1) 改行文字は L'\n' で表現できます。(2) 改行文字は末尾につきます。(3) 文字列は NULL 文字があればそこで終了と言う意味になります。)

    自由な文字列の作成

    swprintf 関数

    例5-自由な文字列の作成
    #include <stdio.h>
    #include <locale.h>
    
    int main( void )
    {
      ::setlocale( 0, "JPN" );
    
      wchar_t str[32];
    
      // 文字列の連結
      wchar_t str1[] = L"文字列の";
      wchar_t str2[] = L"連結テスト";
      swprintf( str, 32, L"%s%s", str1, str2 );
      printf( "%S\n", str );
    
      // 連番ファイル名の生成
      for( int i=0; i<5; i++ )
      {
          swprintf( str, 32, L"table%02d.txt", i );
          printf( "%S\n", str );
      }
    
      return  0;
    }
    

    プログラム上で、文字や数値を組み合わせて、自由な文字列を作製したいという要望は多数挙がってきます。たとえば、フォルダ名とファイル名を合成したり、連続したファイルの操作を行ったりなど、ファイル名を管理するときに使ったりします。これらの自由な文字列の作成と似たようなこととして、コンソール画面に文字や数値を出力する printf 関数をすでに学んでいますが、これを応用すると、文字列も自由に作成することができるようになります。

    swprintf 関数は、printf 関数の出力対象を文字列にした関数です。printf 関数で利用した %d%s などを使って、自由な文字列を作成することができるようになります。

    例では 2 つの処理を示しています。1 例目は複数の文字列の合成で、%s を 2 つ指定することにより、2 つの文字列を 1 つの文字列として合成しています。2 例目は連番ファイル名の作成で、table00.txt ~ table0#txt までを処理したいというときに、for 文を使ってそれらを合成している例です。どちらもファイル操作の時には頻繁に利用します。

    swprintf 関数が利用できるようになると、上記のファイル操作や、画面出力文字列の生成などに活用することができるようになります。この関数は後半になるほど頻繁に利用することになりますので、ぜひ頭に入れておいてください。

    実践問題: 数字の文字列を作成
    1. 1, 3, 5, 7, 9, (中略), 97, 99 という奇数一覧が書かれた文字列を作成してください
    2. 2, 4, 8, 16, (中略), 128, 256 という 2 のべき乗が書かれた文字列を作成してください