1. 文字コードD変換 (シーザー暗号)

1. 文字コードの変換 (シーザー暗号)
1-1. 標準ライブラリ関数 islower() , toupper() を使い、下記の trlowup プログラムを書き換えて、
新規に trupper プログラムを作成せよ
1-1-1. サンプルコード (trlowup.c)
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
#include <ctype.h>
char trlowup(char);
int main()
{
char c;
while( (c=getchar()) != EOF )
putchar( trlowup(c) );
return(0);
}
char trlowup( char c )
{
if ( 'a' <= c && c <= 'z' )
return( c-'a'+'A' );
else
return( c );
}
サンプルコードは、個人的な観点から見やすいようにスタイルを書き換えた。
スタイルのみ(改行,空白など)の変更なので、実行結果には一切影響しない。
以降の、課題で提示されたサンプルコードも、スタイルを統一するために書き換えることにする。
1-1-2. 実行結果 (abcDEF , xyzXyYyZz , abcdEFGhijKLmNoPqRstuVwXyZ を入力)
01
02
03
04
05
06
07
abcDEF
ABCDEF
xyzXyYyZz
XYZXYYYZZ
abcdEFGhijKLmNoPqRstuVwXyZ
ABCDEFGHIJKLMNOPQRSTUVWXYZ
^C
※ “^C”の記号は、“control+C”をあらわしている。
1-1-3. 考察
(a) ここでは、サンプルコード(trlowup.c)の解析を行う。
(b) コード 4 行目で関数 trlowup()について宣言している。戻り値,引数ともに char 型である。
(c) 10 行目で、getchar()関数で入力された値を c に代入する。その値が EOF になるまで、
11 行目をループし続けるという記述だと考えられる。
また EOF とは、“End Of File”の略であり(参考サイト : wikipedia EOF)、“control+C”の
コマンドを表していると考える。
(d) 14 行目で main()関数は終わっているので、それ以降の記述は trlowup()に関わる定義で
あると、コードの文面などから推測できる。
(e) 16 行目以降より、getchar()関数で入力された値が英小文字の場合は、英大文字にしてから
出力されるということが分かる。これは前回の課題(report3)の演算を活かしたものである。
(f ) コードの 2 行目は islower() , toupper() を使用するための記述である。
講義で<ctype.h>が、これらのヘッダであることを学んだ。
(1)
1-1-4. ソースコード
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#include <ctype.h>
char trupper(char);
int main()
{
char mozi;
while( (mozi=getchar()) != EOF )
putchar( trupper(mozi) );
return(0);
}
char trupper(char aa)
{
if ( islower(aa) )
return( toupper(aa) );
else
return( aa );
}
1-1-5. 実行結果
(abcDEF , xyzXyYyZz , abcdEFGhijKLmNoPqRstuVwXyZ , qwertyuiopasdfghjklzxcvbnm1234567890!"#$%&'() を入力)
01
02
03
04
05
06
07
08
09
abcDEF
ABCDEF
xyzXyYyZz
XYZXYYYZZ
abcdEFGhijKLmNoPqRstuVwXyZ
ABCDEFGHIJKLMNOPQRSTUVWXYZ
qwertyuiopasdfghjklzxcvbnm1234567890!"#$%&'()
QWERTYUIOPASDFGHJKLZXCVBNM1234567890!"#$%&'()
^C
※ “^C”の記号は、“control+C”をあらわしている。
1-1-6. 考察
(a) islower() , toupper() を使って、同じ出力結果を得ることができた。
(b) 講義を通して、 islower()は指定した文字が英小文字なら真を返すもので、
toupper()は指定した文字を小文字から大文字にして返すものであるということは、既に
知っていたが、このプログラムの作成を通してさらに理解が深まった。
(c) つまり、if('a' <= aa && aa <= 'z')と if( islower(aa) )、
また return(aa-'a'+'A')と return( toupper(aa) )は、ほぼ同じものである。
(d ) この問題に取り組む際に、以下のように失敗した。
(1 行目から 16 行目までは同じ内容のため、割愛して記述することにする)
実行結果から分かる通り、小文字が出力されていない (if 文に対応した処理が無視されている)
このことから toupper()は、直接文字を出力するような機能を持っていないことが分かった。
失敗したコードの一部 (17~23 行目)
17
18
19
20
21
22
23
実行結果 (ABCDEFG,abcdefg,AbCdEfG を入力)
char trupper(char aa)
{
if ( islower(aa) )
toupper(aa);
else
return( aa );
}
ABCDEFG
ABCDEFG
abcdefg
AbCdEfG
ACEG
^C
(e) 今回のコードを main()関数 1 つで書き換えられないかと考え、取り組んだ。
以下(1-1-7 から 1-1-9)でその結果を記す。
(2)
1-1-7. 改良ソースコード (main 関数のみで書き換える)
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
#include <stdio.h>
#include <ctype.h>
int main()
{
char mozi;
while( (mozi=getchar()) != EOF )
{
if ( islower(mozi) )
putchar( toupper(mozi) );
else
putchar( mozi );
}
return(0);
}
1-1-8. 実行結果 (ASDF , asdf , AbCdEfGhIjKlMn を入力)
01
02
03
04
05
06
07
ASDF
ASDF
asdf
ASDF
AbCdEfGhIjKlMn
ABCDEFGHIJKLMN
^C
※ “^C”の記号は、“control+C”をあらわしている。
1-1-9. 考察
(a) 1-1-4 の 17 行目以降を main()関数内に納めることで、1 つで書き換えることができた。
コードも短くなったので良いと思う。
1-2. trupper プログラムを書き換えて、rot13 暗号化・復号プログラムを作成せよ
rot13 とは、次のようにアルファベットを 13 文字ずらす暗号化の方法である
A --> N
B --> O
C --> P
....
Z --> M
a --> n
b --> o
c --> p
....
z --> m
1-2-1. ソースコード (暗号化プログラム)
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
#include <stdio.h>
int main()
{
char mozi0;
while( (mozi0=getchar()) != EOF )
{
if
( ('a' <= mozi0 && mozi0 <= 'm') || ('A' <= mozi0 && mozi0 <= 'M'))
putchar( mozi0 + 13);
else if( ('n' <= mozi0 && mozi0 <= 'z') || ('N' <= mozi0 && mozi0 <= 'Z'))
putchar( mozi0 - 13 );
else if( mozi0 == 0x20 )
putchar( mozi0 );
}
return(0);
}
(3)
1-2-2. 実行結果 (abcdefg , WXYZ , Hello I am Yoshiki を入力)
01
02
03
04
05
06
07
abcdefg
nopqrst
WXYZ
JKLM
Hello I am Yoshiki
Uryyb V nz Lbfuvxv
^C
1-2-3. 考察
(a) rot13 暗号化プログラムを作成できた。
(b) 例題の通りにアルファベットをずらすことができた。
また、空白文字はそのまま空白として出力するようにした。
(c) また、テキスト P.417 からの演算子を参考にすることで、プログラムを組んだ。
(d) M(m)よりもあとのアルファベットは、13 文字後ろにずらすと、別の記号が出力される。
よって Z(z)の次を A(a)にするために、M(m)以降の文字は 13 文字前にずらすことで対応した。
(e) 次は、復号プログラムを作成する。
1-2-4. ソースコード (復号プログラム)
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
#include <stdio.h>
int main()
{
char mozi0;
while( (mozi0=getchar()) != EOF )
{
if
( ('a' <= mozi0 && mozi0 <= 'm') || ('A' <= mozi0 && mozi0 <= 'M'))
putchar( mozi0 + 13);
else if( ('n' <= mozi0 && mozi0 <= 'z') || ('N' <= mozi0 && mozi0 <= 'Z'))
putchar( mozi0 - 13 );
else if( mozi0 == 0x20 )
putchar( mozi0 );
}
return(0);
}
1-2-5. 実行結果 (nopqrst , JKLM , Uryyb V nz Lbfuvxv を入力)
01
02
03
04
05
06
07
nopqrst
abcdefg
JKLM
WXYZ
Uryyb V nz Lbfuvxv
Hello I am Yoshiki
^C
1-2-6. 考察
(a) rot13 復号プログラムを作成できた。
(b) 空白文字はそのまま空白として出力するようにした。
(c) 13 文字ずらす場合、アルファベットは全部で 26 文字であることから、A(a)と Z(z)が
つながっていると考えた場合、前にずらしても後ろにずらしても取る値は結局等しい。
(d) rot13 暗号化プログラムと rot13 復号プログラムは同じものを使用できた。
(4)
1-3. オリジナルの暗号化,復号プログラムを作成せよ
1-3-1. ソースコード
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
#include <stdio.h>
int main()
{
char code;
while( (code=getchar()) != EOF )
{
if
(0x20 <= code && code <= 0x5d)
putchar( code + 33 );
else if(0x5e <= code && code <= 0x7e)
putchar( code - 62 );
}
return(0);
}
1-3-2. 実行結果
入力した文字列
01
02
03
04
05
06
!"#$%
123456789
[¥]
^_`
(My name is Yoshiki 6/16)
Hello! Nice to meet you.
出力された文字列
ABCDEF
RSTUVWXYZ
|}~
!"
In;A0#/'A+5Az15*+-+AWPRWJ
i'..1BAo+%'A61A/''6A;17O
1-3-3. 考察
(a) オリジナルの暗号化プログラムを作成できた。作成したのは rot33 プログラムであり、
範囲をアルファベットから、ASCII コード表の表示可能文字全体にまで広げた。
(b) 0x5d(])は出力の際には 0x7e(~)を、0x5e(^)は出力の際には 0x20( )を表示する。
このように今回の暗号に対応する文字は、0x7e-0x20 の間でつながっている。
(c) しかし単純に文字をずらすだけでは、すぐに解読できてしまうだろうと考えた。
そこで、さらにもう一工夫した暗号化プログラムを考えたい。
1-3-4. 改良ソースコード 10-1 暗号 (暗号化プログラム)
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <ctype.h>
int main()
{
char code;
char gimic = -10;
printf("message! (only a small letter)\n");
while( ((code=getchar()) != EOF) && (0x61 <= code && code <= 0x7a) )
{
putchar( code + gimic );
gimic++;
if(gimic > -1)
gimic = -10;
}
return(0);
}
(5)
1-3-5. 実行結果
入力した文字列
01
02
03
04
05
06
aaaaaaaaaaaaaaaaaaaaaaa
gfedcba
qwertyuiop asdfghjkl
gameover
hello! nice to meet you.
hellonicetomeetyou
出力された文字列
WXYZ[\]^_`WXYZ[\]^_`WXY
]]]]]]]
gn]kntqfmo
]Xe^iqao
^\dei
^\deiie`csed]^ntkr
1-3-6. 考察
(a) 単純に文字をずらすのではなく、文字の桁数ごとにずらす数を変更する暗号化プログラムを
作成することができた。
(b) 入力の際に使用できる文字は英小文字のみとした。
それ以外の文字を入力すると、問題の文字の部分で暗号化は終了する(実行結果 3 行,5 行目)
(c) 入力した文字列が英小文字の場合、1 桁目は入力した文字に対して、ASCII コード表上で
10 個分、数が若いものを表示する(例として、k を入力すると a が出力される)。
同じ要領で、2 桁目は 9 個分若いもの、3 桁目は 8 個分...と遡って表示していく。
また、10 桁目で 1 個若い文字を表示させたら、11 桁目では 10 個若いものの表示に戻って、
同じような処理のループを文字列が終わるまで繰り返す。
(d) このプログラムでは実行結果からも分かる通り、出力の際には、1 つの文字に対して
全部で 10 種類の異なった文字が存在する。
これで、rot33 のプログラムよりは、解読は困難になるだろう。
(e) 今回の暗号化プログラムではスペースを入力できなくなった。
しかしスペースが無いことによって、暗号の難易度は高くなるので、そのままにする。
(f ) 続いて、復号プログラムを作成する。
1-3-7. ソースコード 10-1 暗号 (復号プログラム)
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <ctype.h>
int main()
{
char code;
char gimic = 10;
printf("code!\n");
while( ((code=getchar()) != EOF) && (0x57 <= code && code <= 0x79) )
{
putchar( code + gimic );
gimic--;
if(gimic < 1)
gimic = 10;
}
return(0);
}
(6)
1-3-8. 実行結果
入力した文字列
01
02
03
04
05
06
WXYZ[\]^_`WXYZ[\]^_`WXY
]]]]]]]
gn]kntqfmo
]Xe^iqao
^\dei
^\deiie`csed]^ntkr
出力された文字列
aaaaaaaaaaaaaaaaaaaaaaa
gfedcba
qwertyuiop
gameover
hello
hellonicetomeetyou
1-3-9. 考察
(a) 復号プログラムも作ることができた。
暗号化とは逆の処理を行うことで、きちんと復号を行うことができている。
2. 反省・感想
暗号についてのレポート。パズル好きの私にとって、非常に興味深い内容だった。
今回はオリジナルの暗号化,復号プログラムを作った。暗号の仕組みを思いつくまでは良かったのだが、
実際にコードを完成させるまでがとても大変で、多くの時間を要した。まだまだ改善の余地はありそう
なので、これからも個人的に取り組んでいきたいと思う。
だんだんとコードの書き方のルールのようなものを習得しつつあるような気がする。しかし、未だエ
ラーや、実行時の予期せぬ動作なども多いので、これからも講義や自主学習に励んでいきたい。
3. 参考文献
書籍
(1) C 実践プログラミング 第 3 版 (オライリー・ジャパン)
インターネットサイト
(2) wikipedia
EOF ( ja.m.wikipedia.org/wiki/EOF )
(7)