[前へ]   [目次へ]   [次へ]

文字の扱い(2)(C/C++)

今回は、様々な方法で文字を代入してみます。

まず、日本語は置いておいて、半角英数のみを扱ってみます。
と、その前に、文字コードは一般に16進数を用いて表現します。
C/C++言語では、数値の前に 0x という表記をすることで、16進数として扱わせることができます。
また、 '' (シングルクオーテーション)で文字を囲むと文字コードの数値に置き換えられます。
ついでに、数値の前に 0 のみを表記すると、8進数として扱わせられます。

様々なバリエーションで文字 7 (コード55,0x37)を入力、表示してみます。
<  1>
<  2>
<  3>
<  4>
<  5>
<  6>
<  7>
<  8>
<  9>
< 10>
< 11>
< 12>
< 13>
< 14>
< 15>
< 16>
< 17>
#include <stdio.h>

int main(void)
{
   
char buf[10];
   buf[0]=55;
//普通に代入
   
buf[1]=067;//8進数として代入
   
buf[2]=0x37;//16進数として代入
   
buf[3]='7';//文字として代入
   
buf[4]='0'+7;//文字 0 (コード48,0x30)に7を加算して文字 7 のコードを作成
   
buf[5]="0123456789"[7];//リテラル文字列が配列であることを利用して7番目の要素の文字コードを取得
   
buf[6]='\0';//ナル文字を付けておきます。
   
printf("%s\n",buf);//全部まとめて文字列として出力
   //終了待ち
   
getchar();
   
return 0;

実行結果:
777777

このように、全ての方法で文字 7 が生成されたのが分かります。
また、今回は最後に文字列として扱っています。
文字列の各文字が配列の各要素に格納されているということは、
逆もまた然り、各要素に格納した文字は文字列の一部として扱えるということです。

ただし、最後(12行)の時点でナル文字の付与を忘れると、意味のないゴミが後ろに表示されたり、
範囲外アクセスでプログラムが異常終了したりします。

前にも書いたようにナル文字は文字列の終端を意味するので、終端のない文字列は大変なことになります。
このような方法で文字列を生成しようとする場合はナル文字の存在を特に大事にしてください。


では各行の解説を行います。
まず、 buf[0]=55;//普通に代入 から。
これは今までも出てきたパターンで、文字コードを調べてそのコードを代入しています。
一々文字コードを調べなければならない上、一見して文字コードを代入しているようには見えないという弱点があります。
何故これが弱点かというと、そのコードの「意図」が見えないと、読む時に理解し辛いという点です。
変数名に意味のある名前が推奨されるのと同じ理由です。

次は、 buf[1]=067;//8進数として代入
数値の先頭に0を付与しているので、8進数の 67 を意味し、10進数ではこれは55に相当します。
それ以外は普通に代入した時と同じですが、このようなコードをC/C++ソース上で見ることはほぼないと思われます(笑)

次は、 buf[2]=0x37;//16進数として代入
数値の先頭に0xを付与しているので、16進数の 37 を意味し、10進数ではこれは55に相当します。
それ以外は普通に代入した時と同じです。
10進数の時よりは「何かのコード」っぽい感じがしますが、
結局「何かのコード」でしかないのでやっぱり一見して文字コードを代入しているようには見えません。

次は、 buf[3]='7';//文字として代入
文字 7 を '' で囲っているので文字 7 の文字コードの数値として扱われます。
なお、この時の型はC言語は int 、C++言語では char ですが、通常は気にする必要はないはずです(笑)
これは文字を代入しようとしているのが明らかなので、このような操作の時はとても見やすいです。
また、文字コードを調べなくてもいいため、書く時も読む時も楽です。

ここからは複合技です、 buf[4]='0'+7;//文字 0 (コード48,0x30)に7を加算して文字 7 のコードを作成
ようは注釈に書いたとおりで、文字 0 の文字コードに7を加算しています。
数字の文字コードは連続しているので、このような手段が使えます。
この方法は printf 関数に頼らずに数値を文字列化する時に使えます。
また、足して数値を文字にできるなら、引けば文字から数値になる訳で、逆方向の変換も可能です。
文字から数値への変換は数字文字列の中から特定の桁の値だけ取り出したい時に重宝します。(↓)
<  1>
<  2>
<  3>
<  4>
<  5>
<  6>
<  7>
<  8>
<  9>
< 10>
< 11>
< 12>
< 13>
< 14>
< 15>
< 16>
< 17>
< 18>
< 19>
< 20>
< 21>
< 22>
#include <stdio.h>

int main(void)
{
   
char str[10]="53976";//適当な数字文字列。
   //            01234 ←配列添字。1文字目が0番であることに注意。
   
int n=0;
   
//sscanfを使って3桁目の '9' を取り出してみる
   
sscanf(str,"%d",&n);//まず数値として解釈して・・・(終了時 n==53976)
   
n/=100;//100で割って下2桁を消します。(終了時 n==539)
   
n%=10;//10の剰余(あまり)を求めて上位桁を消します。(終了時 n==9)
   
printf("sscanf()=%d\n",n);
   
   n=0;
//次の正当性証明のため nの中身を消しておきます。
   
   //上記の減算法で3桁目の '9' を取り出してみる
   
n=str[2]-'0';//3文字目の文字コードから文字 0 の文字コードを減算。
   
printf("減算法=%d\n",n);//一行になりました。ただし数字じゃなかった場合は妙なことになります。
   //終了待ち
   
getchar();
   
return 0;

実行結果:
sscanf()=9
減算法=9

見事スッキリしました。
ついでに桁数が多くて int 型の保持限界を超えるような大きな数でも減算法なら確実に取得できます。

最後です、 buf[5]="0123456789"[7];//リテラル文字列が配列であることを利用して7番の文字コードを取得
これは変則的な操作なのであまり推奨できない気がしますが・・・
前にも書いたようにリテラル文字列(文字列定数)の型は配列なので、配列添字参照演算子を適用することが可能です。
あまり大した使い道はありませんが、ちょっとした簡易暗号とか、
数値を16進数表現で文字に直す時に10以上の値をアルファベットに配置する用途ぐらいには使えます(笑)
<  1>
<  2>
<  3>
<  4>
<  5>
<  6>
<  7>
<  8>
<  9>
< 10>
< 11>
< 12>
< 13>
< 14>
< 15>
< 16>
< 17>
< 18>
< 19>
< 20>
< 21>
< 22>
< 23>
< 24>
< 25>
< 26>
< 27>
< 28>
< 29>
< 30>
#include <stdio.h>

int main(void)
{
   
char dig2hex[17]="0123456789ABCDEF";//10進数の数値と16進数文字の対応配列。結果的には下の直接参照と同じことになる。
   
printf("%c,%c\n",dig2hex[12],"0123456789ABCDEF"[12]);//10進の12を16進数一文字で出力してみる。
   
   
char encrypt[11]="7192806435";//簡易暗号のキー・・・数字をぐちゃぐちゃにしちゃいます。
   
char decrypt[11]="5138796042";//復号用のキー・・・上のキーの逆行キーです。
   
char str[6]="80619";//暗号化してみる数字列。
   
char encstr[6];//暗号化した数字列。
   
char decstr[6];//復号した数字列。
   
encstr[0]=encrypt[str[0]-'0'];//こんな感じに使います。難しいですか?
   
encstr[1]=encrypt[str[1]-'0'];//ちなみにループが出てくるとこのようなパターンは1行になります。
   
encstr[2]=encrypt[str[2]-'0'];
   encstr[3]=encrypt[str[3]-
'0'];
   encstr[4]=encrypt[str[4]-
'0'];
   encstr[5]=
'\0';//ナル文字忘れずに・・・
   
puts(encstr);//putsには配列変数に入れた文字列も渡せます。printf("%s\n",encstr);と同じ結果になります。
   
decstr[0]=decrypt[encstr[0]-'0'];//今度は復号です。
   
decstr[1]=decrypt[encstr[1]-'0'];
   decstr[2]=decrypt[encstr[2]-
'0'];
   decstr[3]=decrypt[encstr[3]-
'0'];
   decstr[4]=decrypt[encstr[4]-
'0'];
   decstr[5]=
'\0';//ナル文字忘れずに・・・
   
puts(decstr);
   
//終了待ち
   
getchar();
   
return 0;

実行結果:
C,C
37615
80619

一つ目は10進数の12と16進数のCが変換できています。
一度配列に代入した場合と、文字列定数に直接使った場合で結果が変化しないことが確認できます。

簡易暗号は80619->37615と一見ぐちゃぐちゃにした後、80619に正しく復号できています。
理論的には0〜9までの数字を重複しない別の数字に置き換える暗号で、
元に戻す時はその変更を元に戻します。例えば、最初0だと上のキーでは7に置き換えるため、
復号する時は7を0に置き換えるようにすれば復元できるという訳です。
これを各文字に対して行えば数字文字列の暗号化と復号ができます。

ただ、この方法、数字しか入れられないので大した使い道ないですねぇ(笑)
しかも古典的な換字式暗号なので結構簡単に破れます、あしからず(笑)


次回は、いよいよ文字の比較を扱います。

[前へ]   [目次へ]   [次へ]

プログラミング講座 総合目次

最終更新 2008/10/17