第 3 回 文字列

本日の内容


このドキュメントは http://edu.net.c.dendai.ac.jp/ 上で公開されています。

3-1. 前回の演習の復習

合計

与えられた配列変数全ての和を求めるには、一つ一つの配列変数の値を 足していく必要があります。 配列変数 a[i] の値を合計するとし、 goukei という変数を用意したとします。 今、 i-1 番目までの合計がこの変数に合計されていたとすると、i 番目の値 a[i] だけこの goukei 変数を増やして上げれば i 番目の値までの合計が求ま ることになります。 つまり goukei = goukei + a[i] とすれば良いことになります(C 言語では goukei += a[i] と書くことができます)。 これを i = 0 から配列の終りまでやれば合計が求まることになります。


#include <stdio.h>
main(){
  int a[]={5, 2, 6, 3, 1, -1};
  int i,goukei;
  goukei=0;
  for(i=0;a[i]!=-1;i++){
   goukei += a[i];
  }
  printf("合計 %d\n",goukei);
}

キャスト

整数の値の平均を求めることを考えます。 整数の値の合計は整数ですし、個数も整数です。 しかし、(平均)=(合計)/(個数)は一般には整数の値ではなく、小数点付きの値 が求められます。 ところが、 C 言語では、(整数)/(整数) の値は小数点以下を切り捨てた整数 になります。 従って、次のプログラムでは正しく答が求められないことになります。


#include <stdio.h>
main(){
  int a[]={5, 2, 6, 3, 1, -1};
  int i,goukei,k;
  goukei=0;
  k=0;
  for(i=0;a[i]!=-1;i++){
   goukei += a[i];
   k++;
  }
  printf("平均 %f\n",goukei/k);
}

そこで、(整数)/(整数) が整数でないようにします。 それには(浮動小数点)/(整数) となるようにすれば良いです。 つまり、式において整数型の変数を浮動小数点型に読み替えられれば、目的の 計算が出来ます。 このように、変数や式の値を別の型に指定することをキャストと呼び、 次のように式の前に () で括った型の名前を書きます。


(型)式

上のプログラムの場合、 goukei/k の goukei を float に指定したいので、 (float) goukei/ k と書きます。 書き直したプログラムは次のようになります。


#include <stdio.h>
main(){
  int a[]={5, 2, 6, 3, 1, -1};
  int i,goukei,k;
  goukei=0;
  k=0;
  for(i=0;a[i]!=-1;i++){
   goukei += a[i];
   k++;
  }
  printf("平均 %f\n",(float) goukei/k);
}

3-2. 文字列

C 言語では文字列は文字の配列として扱います。 また、その文字配列の一番最後に番兵として特殊な文字 '\0' という要素を必 ず入れます。 例えば、 abc という文字列を配列 x[4] に入れる時は、次のようになります。


char x[4]={'a','b','c','\0'};

但し、この {'a','b','c','\0'} は単純に "abc" と書くことができます。従っ て、次のように宣言することができます。


char x[]="abc";

このように宣言すると x[0]='a', x[1]='b', x[2]='c', x[3]='\0' となりま す。

一文字を printf で表示するには %c を使いますが、配列変数に入っている文 字列全部を出力するには %s を使用します。 文字列全部と、それを左から一文字ずつ印刷するには次のようにします。


#include <stdio.h>
main(){
  char x[]="abc";
  int i;
  for(i=0;x[i]!='\0';i++){
    printf("文字列 %s の %d 番目は %c\n",x,i,x[i]);
  }
}

3-3. 文字コード

コンピュータでは文字を取り扱うため、数字を割り当てています。 これを文字コードと言います。 C 言語では文字はシングルクォーテーションマーク ' ' で囲みますが、これは コンピュータの内部処理では囲まれた文字のコードを示しています。 実は C 言語には文字(char)と整数(int)の明確な違いはありません。単に文字型は -128 から 127 までの値しか取れないだけです。 したがって、文字型の変数に対して、 printf 中で %d により表示すると、文 字コードを示す数字が表示されます。


#include <stdio.h>
main(){
  char x[]="abc";
  int i;
  for(i=0;x[i]!='\0';i++){
    printf("文字列 %s の %d 番目は %c で文字コードは %d\n",x,i,x[i],x[i]);
  }
}

C 言語では文字も整数とほぼ同様に扱いますので、加減乗除などの演算も可能 です。 さらに、文字コードで重要な性質として、現在のコンピュータではアルファベッ トの文字コードは連続しています (紙カードの時代の文字コードは違いました)。 そのため、例えば、'A'+1 は 'B' になります。 次のプログラムは実際正常に B を出力します。


#include <stdio.h>
main(){
  printf("%c",'A'+1);
}

従って、'A' と 'a' の文字コードを使うことで大文字小文字変換ができます。 例えば 'c' を大文字の'C' に変換することを考えます。 'c' から 'a' までの距離と 'C' から 'A' までの距離は等しいはずです。 つまり 'c'-'a'='C'-'A' となるはずです。これを移項すると 'c'-'a'+'A'='C' となります。従って入力が全て小文字の文字列を大文字にし て出力するには次のようにすれば良いです。


#include <stdio.h>
main(){
  char x[]="abc";
  int i;
  for(i=0;x[i]!='\0';i++){
    printf("%c",x[i]-'a'+'A');
  }
}

また、文字が数字や英小文字かどうかなどの判定に大小関係が使えます。


#include <stdio.h>
main(){
  char x[]="abc 123 ABC";
  int i;
  for(i=0;x[i]!='\0';i++){
    if((x[i]>='0')&&(x[i]<='9')){
      printf("%c は数字\n",x[i]);
    }else if((x[i]>='a')&&(x[i]<='z')){
      printf("%c は英小文字\n",x[i]);
    }else 
    if((x[i]>='A')&&(x[i]<='Z')){
      printf("%c は英大文字\n",x[i]);
    }else{
      printf("%c は英数字以外\n",x[i]);
    }
  }
}

3-4. 演習

標準問題

  1. char x[]="abc" と宣言されている時、この文字列の長さを出力するプロ グラムを書きなさい。
  2. char x[]="This is a pin." と宣言されている時、この文字列に含まれる i の数を出力するプログラムを書きなさい。
  3. char x[]="This is a pin." と宣言されている時、この文字列を全て大文 字で出力するプログラムを書きなさい (ヒント: 小文字だけを大文字にするようにします)。
  4. 二つの文字列 char x[]="abc", y[]="def" があり、 char z[50]; が宣言 されてたとします。この時、文字配列 z に文字列 x と y をつなげて一つの 文字列を作りなさい。
    
    #include <stdio.h>
    main(){
      char x[]="abc";
      char y[]="def";
      char z[50];
    
      /* 文字列をつなげる処理 */
      printf("文字列 %s と %s をつなげると %s になる\n",x,y,z);
    }
    
  5. 文字コードと文字の対応表を表示するプログラムを書きなさい。 表示範囲は 32 から 126 までで良い。 なお、 printf 中で数を 16 進数で表示したい場合は %x を使用する。二桁の 16 進数であれば、%2x とする。

発展問題

  1. bob のように左から読んでも、右から読んでも文字の配列が同じものを 「回文(palindrome)」と言います。文字配列に与えられた文字 列に対して、回文かどうかを判定するプログラムを書きなさい。 なお、英語の回文には次のようなものがあります。
    1. Madam, I'm Adam.
    2. Was it a cat I saw?
  2. 以下のプログラムを読んで、ひらがなをカタカナに直すプログラムを作りなさ い。
    
    #include <stdio.h>
    #include <locale.h>
    #include <wchar.h>
    main(){
      wchar_t c=L'ア';
      wchar_t s[]=L"あいうえお";
      setlocale(LC_CTYPE,"Japanese_Japan.932");
      wprintf(L"%ls %lc\n",s,c);
    }
    
  3. 「IBM」を「HAL」など、文字を一文字ずつ定められただけずらして作られる暗 号を、初めて使った人の名前をとり「シーザー暗号」と呼びます。 以下は暗号化プログラムの例です。
    
    #include <stdio.h>
    main(){
      char c,x[]="hello world";
      int i,key;
      key=3;
      for(i=0;x[i]!='\0';i++){
        if((x[i]>='a')&&(x[i]<='z')){
          c='a'+(x[i]-'a'+key)%('z'-'a'+1);
        }else{
          c=x[i];
        }
        printf("%c -> %c\n",x[i],c);
      }
    }
    
    シーザー暗号の鍵は高々 25 種類しかないことを利用して、シーザー暗号を 解読するプログラムを作りなさい。 また、以下の暗号文を解読しなさい。
    1. sedwhqjkbqjyedi
    2. dahhk sknhz
    3. jxyi yi jef iushuj.

坂本直志 <sakamoto@c.dendai.ac.jp>
東京電機大学工学部情報通信工学科