プログラミング I Report#4 提出日 :2012 年 6 月 14 日 (木) 所 属 :工学部情報工学科 学籍番号:125716B 氏 名 :伊波 立樹 目次 1 問題.1 1 2 関数 3 3 ctype.h について 6 4 問題.2 8 5 問題 3 12 6 感想 16 i 問題.1 1 標準ライブラリ関数 islower(),toupper() を使い、下記の trlowup プログラムを書き換えて、新規に trupper プロ グラムを作成せよ。 ソース 1: trlowup.c 1 /* Program Sttudent -ID Author UpDate Comments 2 3 4 5 6 7 : : : : : trlowup .c 125716 B IHA , Tatsuki 2012/06/10( Sun) translate lower case characters into upper case ones. */ 8 9 10 # include <stdio .h> # include <ctype .h> 11 12 char trlowup (char ); /* 関数 t r l o w u p の プ ロ ト タ イ プ 宣 言 */ 13 14 15 int main (){ char c; /* 文字型変数の宣言 */ 16 while ( (c= getchar ()) != EOF ) /* E O F じ ゃ な け れ ば 実 行 */ putchar ( trlowup (c) ); /* 関数 t r l o w u p の 戻 り 値 を 出 力 */ return (0); 17 18 19 20 } 21 22 23 24 25 26 27 char trlowup ( char c ){ /* 関数 trlowup */ if ( ’a’ <= c && c <= ’z’ ) /* 小英文字ならば */ return ( c-’a’+’A’ ); else /* それ以外 */ return ( c ); } ✓ 出力結果 ✏ abcDEF ABCDEF xyzXyYyZz XYZXYYYZZ ˆC ✒ ✑ 解説 このプログラムはキーポードからデータを入力し、英小文字ならば英大文字に変換するプログラムである。 12 行目で関数 trlowup のプロトタイプ宣言をしている。 16 行目の while 文で getchar() 関数 (getchar は 1 文字標準入力) で入力した文字が EOF(エンドオブファイルと呼 ばれる、キーボードで入力する場合 mac では ctrl+d が EOF であり、それが入力されるまで入力を待ち続ける) ではな ければこのループを処理する。 18 行目は putrchar() 関数で 12 行目で宣言した trlowup の返り値を出力する。関数 trlowup の引数は変数 c である。 22 行目からは関数 trlowup の処理を記述している。 23 行目の if 文で 16 行目の getchar() 関数で入力した文字が小英文字ならば 24 行目の return 文で小文字を大文字 に変換した値を main 関数に返す。 25 行目の else 文で入力した文字が小英文字以外の文字ならば何も処理せずそのままの値を main 関数に返す。 1 ソース 2: trupper.c 1 2 # include <stdio .h> # include <ctype .h> 3 4 char trlowup (char ); /* 関数のプロトタイプ宣言 */ 5 6 7 int main (){ char c; 8 while ( (c= getchar ()) != EOF ) /* E O F じ ゃ な け れ ば 実 行 */ putchar ( trlowup (c)); /* 関数 t r l o w u p の 戻 り 値 を 出 力 */ return (0); 9 10 11 12 } 13 14 15 16 17 18 19 char trlowup ( char c ){ /* 関数 trlowup */ if ( islower (c)) /* 小英文字なら実行 */ return ( toupper (c)); else /* それ以外 */ return ( c ); } ※ 出力結果は trlowup.c と同じなので省略 解説 このプログラムは trlowup.c のプログラムの条件式、計算式を標準ライブラリ関数、islower()、toupper() に書き 換えたプログラムである。 15 行目の if 文で islower() を使用し、真が返されたら 16 行目の toupper() で小文字を大文字に変換した値を main 関数に返す。 2 関数 2 関数とはプログラムの処理の集まりである。今まで main 関数の中でプログラムを記述子てきたが、問題 1 で使用し た trlowup.c と trupper.c は新たに関数を作成者が定義した。このようにプログラム作成者が定義する関数はユーザ 関数と呼ばれる、ここではユーザ関数について解説、考察する。 ソース 3: function1.c 1 # include <stdio .h> 2 3 4 5 6 7 8 9 10 11 int wa(int ,int ); /* 関数のプロトタイプ宣言 */ int main (){ /* main () 関 数 */ int a,b,sum; a = 4; b = 7; sum = wa(a,b); /* 関数 w a を 呼 び 出 す */ printf ("%d + %d = %d\n",a,b,sum ); return (0); } 12 13 14 15 int wa(int x,int y){ /* w a 関 数 */ int z; z = x+y; 16 return (z); /* 変数 z を main () 関 数 に 返 す */ 17 18 } ✓ 出力結果 ✏ 4 + 7 = 11 ✒ ✑ 解説 このプログラムは関数を使用し、2 つの値の加算を行うプログラムである。 3 行目で関数 wa のプロトタイプ宣言をしている。プロトタイプ宣言とはコンパイラにこの関数の情報を与える宣言で ある。 8 行目で関数 wa を呼び出している。変数 sum には関数 wa の戻り値が代入される。 14 行目からは関数 wa の処理が記述されている。int wa(int x,int y) の変数 x,y には main 関数で定義した a,b の 値が代入される。変数 a,b のような値を引数と呼ぶ。wa 関数の変数 x,y は int 型なので、引数も int 型であるのが望 ましい。 15 行目は int 型で変数を宣言している。宣言した変数は宣言した関数内でしか使用しできない。 16 行目で引数の値を加算し、その値を宣言した変数 z に代入している。 17 行目で return 文を使い wa 関数に変数 z を返す。そして main 関数に戻り、wa 関数の戻り値を main 関数で宣言し た sum に代入される。 考察 ・関数を定義するときは 型 関数名 (受け取る引数の型 変数名) で定義する。 ・型で設定した型で retrun 文を記述したほうが良い。(つまり関数の型が char 型の場合、return 文で返す値は文 字型) ・関数の引数に配列を使用するにはポインタを使う。そのため、このレポートでは解説しない。 3 ソース 4: function2.c 1 # include <stdio .h> 2 3 4 5 6 7 int func (); /* 関数のプロトタイプ宣言 */ int main(void ){ /* main () 関 数 */ int sum; sum = func (); /* 関数 f u n c を 呼 び 出 す */ printf ("sum = %d\n",sum ); 8 return (0); 9 10 } 11 12 13 14 15 16 17 int func (){ /* f u n c 関 数 */ int x,y; x = 10; y = 15; return (x*y); /*x* y を main () 関 数 に 返 す */ } ✓ 出力結果 ✏ sum = 150 ✒ ✑ 解説 このプログラムは関数を使用し、2 つの値の乗算を行うプログラムである。 先程の function1.c と違い、引数をとらず、ユーザー関数内で変数を宣言し値を main 関数に返している。 考察 ・関数で引数を取らない場合、関数名の後の () 内に何も記述せずに関数を定義する。または () 内に void と記述する。 ・void とは「なにもない」という意味で使われる。 ・retrun 文で演算子を使用できる。 ソース 5: function3.c 1 # include <stdio .h> 2 3 4 5 6 7 8 9 void func(int ,int ); /* 関数のプロトタイプ宣言 */ int main (){ /* main () 関 数 */ int a,b; a = 30; b = 15; func(a,b); /* 関数 f u n c を 呼 び 出 す */ } 10 11 12 13 14 15 void func(int x,int y){ /* f u n c 関 数 */ int z; z = x/y; printf ("%d / %d = %d\n",x,y,z); } ✓ 出力結果 ✏ 30 / 15 = 2 ✒ ✑ 4 解説 このプログラムは関数を使用し、2 つの値の乗算を行うプログラムである。 このプログラムは引数を取らない function2.c と違い戻り値の処理がない。つまりプログラムの処理自体は main 関 数で func 関数を呼び出した後、14 行目の printf() 関数で終了している。 考察 ・関数の型に void を指定すると戻り値を関数に返さない。という意味になる。 ・プログラムの処理がユーザー関数内で終了しているので、main 関数内には return 文の必要はない。 ・main 関数でも int main() ではなく void main() にすると、return 文も必要がなくなる。(ただし警告が発生 した。) 5 ctype.h について 3 問題 1 では ctype.h を読み込んでいる。ここでは ctype.h 内の関数について解説、考察する。 ソース 6: ctype1.c 1 2 # include <stdio .h> # include <ctype .h> /* ctype. h の 読 み 込 み */ 3 4 5 6 7 8 9 10 11 12 13 int main (){ char a; printf (" 英文字を 1 文 字 入 力 し て く だ さ い 。 \ n"); a = getchar (); if ( isalpha (a)){ /* 英文字なら実行 */ if ( islower (a)) /* 英小文字なら実行 */ printf (" 小文字です。 \n"); else if ( isupper (a)) /* 英小文字ではなくかつ英大文字なら実行 */ printf (" 大文字です。 \n"); } 14 return (0); 15 16 } ✓ 出力結果 ✏ 英文字を 1 文字入力してください。 a 小文字です。 英文字を 1 文字入力してください。 A 大文字です。 ✒ ✑ 解説 このプログラムは ctype.h 内の関数 isalpha(),islower()、isupper を使い、英小文字、英大文字を判断するプロ グラムである。 8 行目の if 文で isalpha() の結果が偽 (0) ならば 14 行目の return 文で main 関数を終了させる。 9 行目と 11 行目の if 文の条件式で islower()、isupper() の結果が真ならば、それぞれの処理を行う。 8 行目と 10 行目 考察 ・関数 isalpha() は引数が英文字ならば、真 (0 以外) を戻り値とする。それ以外だったら偽 (0) が戻り値になる。 例 isalpha(c) 変数 c に代入されているデータが英文字なら真 (0 以外) が返される。 ・関数 islower() は引数が英小文字ならば、真 (0 以外) を戻り値とする。それ以外だったら偽 (0) が戻り値になる。 例 islower(c) 変数 c に代入されているデータが英小文字なら真 (0 以外) が返される。 ・関数 isupper() は引数が英大文字ならば、真 (0 以外) を戻り値とする。それ以外だったら偽 (0) が戻り値になる。 例 isupper(c) 変数 c に代入されているデータが英大文字なら真 (0 以外) が返される。 6 ソース 7: ctype2.c 1 2 # include <stdio .h> # include <ctype .h> /* ctype. h の 読 み 込 み */ 3 4 5 6 7 8 9 10 11 12 13 14 int main (){ char a; printf (" 英文字を 1 文 字 入 力 し て く だ さ い 。 \ n"); a = getchar (); if ( isalpha (a)){ /* 英文字なら実行 */ if ( islower (a)){ /* 英小文字なら実行 */ printf ("%c\n",toupper (a)); /* 英大文字を出力 */ }else if( isupper (a)){ /* 英小文字ではなくかつ英大文字なら実行 */ printf ("%c\n",tolower (a)); /* 英小文字を出力 */ } } 15 return (0); 16 17 } ✓ 出力結果 ✏ 英文字を 1 文字入力してください。 a A 英文字を 1 文字入力してください。 A a ✒ ✑ 解説 このプログラムは関数 tolower()、toupper() を使い、変数に入力された文字が英小文字なら英大文字、英大文字な ら英小文字に変換し、出力するプログラムである。 printf() 関数以外の処理は先程の ctype1.c と同じである。 10 行目 12 行目の printf() 関数内の変換指定子%c にはそれぞれ関数 toupper()、tolower() の戻り値が出力される。 考察 ・関数 toupper() は引数が英小文字ならば、その文字の英大文字を戻り値とする。 例 toupper(’a’) 戻り値は’A’ となる。 ・関数 tolower() は引数が英大文字ならば、その文字の英小文字を戻り値とする。 例 toupper(’A’) 戻り値は’a’ となる。 7 問題.2 4 trupper プログラムを書き換えて、rot13 暗号化・復号プログラムを作成せよ。 ソース 8: rot13.c 1 2 # include <stdio .h> # include <ctype .h> 3 4 char rot13 (char ); /* 関数のプロトタイプ宣言 */ 5 6 7 int main (){ char c; 8 while ( (c = getchar ()) != EOF ){ putchar ( rot13(c)); /* 関数の呼び出し */ } 9 10 11 12 return (0); 13 14 } 15 16 17 18 19 20 21 22 23 24 25 char rot13( char c ){ if ( isalpha (c)){ /* 英字なら実行 */ if(’a’ <= c && c <= ’m’ || ’A’ <= c && c <= ’M’) return ( c + 13 ); /* m a i n 関 数 に 返 す */ else /* n ∼ z ま た は N ∼ Z な ら 実 行 */ return ( c - 13); /* m a i n 関 数 に 返 す */ } else /* 英字以外なら実行 */ return ( c ); } /* a ∼ m ま た は A ∼ M な ら 実 行 */ ✓ 出力結果 abcdefghijklmnopqrstuvwxyz ✏ /*暗号化*/ nopqrstuvwxyzabcdefghijklm nopqrstuvwxyzabcdefghijklm /*復号化*/ abcdefghijklmnopqrstuvwxyz ✒ ✑ 解説 このプログラムは rot13 という暗号化方式の暗号化と復号化を行えるプログラムである。 rot13 とは英字を 13 文字ずらす暗号化方式である。つまり暗号化した文字を再び 13 文字ずらすと復号化される。(英 字は 26 種類のため。) よって同じプログラムで暗号化も復号化もできる。 rot13 は 16 行目からの関数 rot13 で処理している。 18 行目の if 文で変数 c に代入されている値が’a’∼’m’ または’A’∼’M’ なら 19 行目の return 文で変数+13 を戻り 値として main 関数に返す。 20 行目の else 文で’n’∼’z’ または’N’∼’Z’ なら変数-’n’+’a’ を戻り値として main 関数に返す。 23 行目の else 文は英字以外の値だったら、そのままの値を戻り値として main 関数に返す。 考察 ・ASCII コードの英字は順番通り並んでいるため、暗号化前が’a’∼’m’ または’A’∼’M’ なら単純に 13 を加算する だけで良い。 ・暗号化前が’n’∼’z’ または’N’∼’Z’ なら単純に 13 を加算すると記号などの文字になってしまう。(’n’ を 13 文字 ずらすと、一周回って’a’ に戻るため。) その問題を解決するには逆に 13 を減算すれば良い。 ・暗号化方式 rot13 は英字を 13 文字ずらす単純な暗号化なため、13 文字ずらす作業を 2 回行うことで復号化される。 よって、セキュリティーを求める場面では余り使われない。 8 ソース 9: rot13.1.c 1 2 # include <stdio .h> # include <ctype .h> 3 4 5 # define FALSE 0 /* F A L S E を 定 義 */ # define TRUE ! FALSE /* T R U E を 定 義 */ 6 7 void rot13 (char *,int ); /* 関数のプロトタイプ宣言 */ 8 9 10 11 12 13 int main (){ int i,type; /* 整数型変数宣言 */ char y[128] , str [128]; /* 文字型変数、配列の宣言 */ while (TRUE ){ /* 無限ループ */ printf (" モードを選択してください。 0: 終 了 1: 新 規 入 力 "); 14 if (type == 2){ printf (" 2: 前 回 入 力 し た 文 字 を 復 号 化 \ n"); } else{ printf ("\n"); } 15 16 17 18 19 20 21 fgets(y, sizeof (y), stdin ); /* モードの入力 */ sscanf (y,"%d" ,&i); 22 23 24 if (i == 0){ break; /* 入力が 0 の 場 合 プ ロ グ ラ ム を 終 了 す る */ }else if(i == 1){ /* 入力が 1 の 場 合 文 字 を 入 力 さ せ 、 暗 号 化 */ puts(" 文字を入力してください。 "); fgets (str , sizeof (str),stdin ); /* 文字を入力 */ puts(" 暗号化 "); rot13 (str ,i); /* 関数の呼び出し */ }else if(i == 2){ puts(" 復号前 "); printf ("%s",str ); puts(" 復号化 "); rot13 (str ,i); /* 関数の呼び出し */ } type = 2; 25 26 27 28 29 30 31 32 33 34 35 36 37 38 } puts(" プログラムを終了します。 "); 39 40 41 return (0); 42 43 } 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 void rot13(char *c,int i){ /* 関数 rot13 */ int y; /* カウント */ for(y=0;c[y +1]!= ’\0 ’;y++){ /* 文字列が ’\0’( 文 字 列 の 終 わ り ) に な る ま で ル ー プ */ if( isalpha (c[y])){ /* 英字なら実行 */ if(’a’ <= c[y] && c[y] <= ’m’ || ’A’ <= c[y] && c[y] <= ’M’){ /* a ∼ m ま た は A ∼ M な ら 実 行 */ c[y] = c[y] + 13; } else /* n ∼ z ま た は N ∼ Z な ら 実 行 */ { c[y] = c[y] - 13; } printf ("%c",c[y]); /* 暗号化 o r 復 号 化 し た 結 果 を 出 力 */ } else{ /* 暗号化 o r 復 号 化 で き な い 場 合 */ if (i == 1){ /* 暗号化できない場合 */ puts(" 暗号化できません。 "); break ; }else if(i == 2){ /* 復号化できない場合 */ puts(" 復号化できません。 "); break ; } } } printf ("\n"); } 9 ✓ 出力結果 ✏ モードを選択してください。0:終了 1:新規入力 1 文字を入力してください。 TatsukiIHA 暗号化 GngfhxvVUN モードを選択してください。0:終了 1:新規入力 2:前回入力した文字を復号化 2 復号前 GngfhxvVUN 復号化 TatsukiIHA モードを選択してください。0:終了 1:新規入力 2:前回入力した文字を復号化 1 文字を入力してください # 暗号化 暗号化できません。 モードを選択してください。0:終了 1:新規入力 2:前回入力した文字を復号化 2 復号前 # 復号化 復号化できません。 モードを選択してください。0:終了 1:新規入力 2:前回入力した文字を復号化 0 プログラムを終了します。 ✒ ✑ 解説 このプログラムは rot13.c のプログラムを改良したプログラムである。 先ほどの rot13.c の変更点は過去の入力 1 回分の暗号化した文字列を記憶し、その文字列を復号化できる。 暗号化方式 rot13 は暗号文を暗号化と同じ処理をすることによって文字列が復号化される。よって、暗号化、復号化 の処理は先程の rot13.c と変わらない。 モードの処理は 21∼22 行目でモードの値を入力し、24,26,31 行目の if、else if 文で各モードの処理を行う。 新規に文字を入力して暗号化する場合の処理は 26 行目からの処理になる。まず 28 行目で文字列を入力する。入力の 処理は fgets() 関数を使用している。30 行目で配列 str の先頭アドレスと変数 i を引数にして関数 rot13 を呼び出し ている。 新規に入力せず暗号化した過去の入力を復号化したい場合の処理は 31 行目からの処理になる。まず 33 行目の printf() 関数で復号前の文字列を表示する。配列 str は何も代入されないので過去の入力 1 回分のデータが残っている。35 行目 で配列 str の先頭アドレスと変数 i を引数にして関数 rot13 を呼び出している。 関数 rot13 の変更点は文字列を引数にしているため、配列の要素 1 つ 1 つを変更している。47 行目の for 分で文字 型配列の内容が’\0’ までカウンタをインクリメントしながらループしている。50 行目でカウントの配列の要素に対応 10 した文字を+13 をしている。53 行目では配列の要素に対応した文字を-13 をしている。 考察 ・過去の暗号化した文字を記憶することによって、1 度暗号化したものを復号化する場合 rot13.c のように暗号化した 文字を入力する手間が省ける。 ・モードを実装、その他のメッセージを puts() 関数などで表示することによって、出力結果が見やすくなった。 ・main 関数のみでプログラムを書くと複雑な処理になるに連れてプログラムが見づらくなる。処理によって関数を定 義することによって処理の手順が追いやすくなる。 ・文字型の配列を rot13 関数で使いたかったためポインタを使用している。rot13 関数内のポインタの扱い方は通常 の配列と変わらない。 11 5 問題 3 オリジナルの暗号化・復号プログラムを作成せよ。 ソース 10: original1.c 1 2 # include <stdio .h> # include <ctype .h> 3 4 5 # define FALSE 0 /* F A L S E を 定 義 */ # define TRUE ! FALSE /* T R U E を 定 義 */ 6 7 void angou (char *,int ); /* 関数のプロトタイプ宣言 */ 8 9 10 11 12 13 int main (){ int i,type; /* 整数型変数宣言 */ char y[128] , str [128]; /* 文字型変数、配列の宣言 */ while (TRUE ){ /* 無限ループ */ printf (" モードを選択してください。 0: 終 了 1: 新 規 入 力 "); 14 if (type == 2){ printf (" 2: 前 回 入 力 し た 文 字 を 復 号 化 \ n"); } else{ printf ("\n"); } 15 16 17 18 19 20 21 fgets(y, sizeof (y), stdin ); /* モードの入力 */ sscanf (y,"%d" ,&i); 22 23 24 if (i == 0){ break; /* 入力が 0 の 場 合 プ ロ グ ラ ム を 終 了 す る */ }else if(i == 1){ /* 入力が 1 の 場 合 文 字 を 入 力 さ せ 、 暗 号 化 */ puts(" 文字を入力してください。 "); fgets (str , sizeof (str),stdin ); /* 文字を入力 */ puts(" 暗号化 "); angou (str ,i); /* 関数の呼び出し */ }else if(i == 2){ puts(" 復号前 "); printf ("%s",str ); puts(" 復号化 "); angou (str ,i); /* 関数の呼び出し */ } 25 26 27 28 29 30 31 32 33 34 35 36 37 38 type = 2; } puts(" プログラムを終了します。 "); 39 40 41 42 return (0); 43 44 } 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 void angou(char *c,int i){ /* 関数 angou */ int y; /* カウント */ for(y=0;c[y +1]!= ’\0 ’;y++){ /* 文字列が ’\0’( 文 字 列 の 終 わ り ) に な る ま で ル ー プ */ if (0 x20 <= c[y] && c[y] <= 0x7e ){ /* 出力可能な文字の場合 */ if (0 x21 <= c[y] && c[y] <= 0x4f ){ /*0 x 2 1 ∼ 0 x 4 f の 間 の 場 合 */ c[y] = c[y] + 47; }else if(0 x50 <= c[y] && c[y] <= 0x7e ){ /*0 x 5 0 ∼ 0 x 7 e の 間 の 場 合 */ c[y] = c[y] - 47; }else if(c[y] == 0x20 ){ /* 文字が SP( ス ペ ー ス ) の 場 合 */ c[y] = c[y]; } printf ("%c",c[y]); /* 暗号化 o r 復 号 化 し た 結 果 を 出 力 */ } else{ /* 暗号化 o r 復 号 化 で き な い 場 合 */ if (i == 1){ /* 暗号化できない場合 */ puts(" 暗号化できません。 "); break ; }else if(i == 2){ /* 復号化できない場合 */ puts(" 復号化できません。 "); break ; } } } printf ("\n"); } 12 ✓ 出力結果 ✏ モードを選択してください。0:終了 1:新規入力 1 文字を入力してください。 !"#$%&’()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNO 暗号化 PQRSTUVWXYZ[\]ˆ_‘abcdefghijklmnopqrstuvwxyz{|}˜ モードを選択してください。0:終了 1:新規入力 2:前回入力した文字を復号化 2 復号前 PQRSTUVWXYZ[\]ˆ_‘abcdefghijklmnopqrstuvwxyz{|}˜ 復号化 !"#$%&’()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNO モードを選択してください。0:終了 1:新規入力 2:前回入力した文字を復号化 0 プログラムを終了します。 ✒ ✑ 考察 このプログラムは英字のみの暗号化の rot13 を改良し、ASCII コードで出力できる文字、記号を暗号化できるように したプログラムである。 暗号化の処理は関数 angou 内の 49 行目、51 行目の if、else if 文で処理している。ASCII コードで出力できる文 字、記号は SP(スペース) を除くと 94 種類なのでコードを 47 文字ずらすと rot13 のように 2 回暗号化処理を行うと復 号化をすることができる。 53 行目の else if 文で配列の要素の文字が SP(スペース) の場合は、暗号化、復号化しても SP(スペース) という処 理を行った。 考察 ・rot13 を応用すれば、ASCII コードで出力できる文字の暗号化もできる。 ・rot13 と同じく 2 回暗号化処理を行うと復号化される。 13 ソース 11: original2.c 1 2 3 # include <stdio .h> # include <stdlib .h> # include <ctype .h> 4 5 6 # define FALSE 0 /* F A L S E を 定 義 */ # define TRUE ! FALSE /* T R U E を 定 義 */ 7 8 9 void angou (char *,int ); /* 関数のプロトタイプ宣言 */ void hukugou (char *,int ); /* 関数のプロトタイプ宣言 */ 10 11 12 13 14 15 int main (){ int i,ransu ,type; /* 整数型変数の宣言 */ char x[128] ,y[128] , str [128]; /* 文字型変数、配列の宣言 */ while (TRUE ){ /* 無限ループ */ printf (" モードを選択してください。 0: 終 了 1: 新 規 入 力 "); 16 if (type == 2){ printf (" 2: 前 回 入 力 し た 文 字 を 復 号 化 \ n"); } else{ printf ("\n"); } 17 18 19 20 21 22 23 fgets(y, sizeof (y), stdin ); /* モードの入力 */ sscanf (y,"%d" ,&i); 24 25 26 if (i == 0){ break; /* 入力が 0 の 場 合 プ ロ グ ラ ム を 終 了 す る */ }else if(i == 1){ /* 入力が 1 の 場 合 文 字 を 入 力 さ せ 、 暗 号 化 */ ransu = rand ()%10; /*9 以 下 の 乱 数 を 取 得 */ printf (" 乱数値 %d\n",ransu ); puts(" 文字を入力してください。 "); fgets (str , sizeof (str),stdin ); /* 文字を入力 */ puts(" 暗号化 "); angou (str , ransu ); /* 関数の呼び出し */ }else if(i == 2){ puts(" 暗号化時の乱数値を入力してください。 "); fgets (x, sizeof (x), stdin ); /* 乱数値の入力 */ sscanf (x,"%d" ,&ransu ); puts(" 復号前 "); printf ("%s",str ); puts(" 復号化 "); hukugou (str ,ransu ); /* 関数の呼び出し */ } 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 type = 2; } puts(" プログラムを終了します。 "); 46 47 48 49 return (0); 50 51 } 52 53 54 55 56 57 58 59 60 void angou(char *c,int ransu ){ /* 関数 angou */ int y; /* カウント */ for(y=0;c[y +1]!= ’\0 ’;y++){ /* 文字列が ’\0’( 文 字 列 の 終 わ り ) に な る ま で ル ー プ */ c[y] = c[y] + ransu; if (c[y] >= 0x7f ){ /* 乱数分ずらした結果、表示可能領域を超えてしまった場合の処理 */ c[y] = 0x32 + (c[y] - 0x7e ); } 61 if (0 x20 <= c[y] && c[y] <= 0x7e ){ /* 出力可能な文字の場合 */ if (0 x21 <= c[y] && c[y] <= 0x4f ){ /*0 x 2 1 ∼ 0 x 4 f の 間 の 場 合 */ c[y] = c[y] + 47; }else if(0 x50 <= c[y] && c[y] <= 0x7e ){ /*0 x 5 0 ∼ 0 x 7 e の 間 の 場 合 */ c[y] = c[y] - 47; }else if(c[y] == 0x20 ){ /* 文字が SP( ス ペ ー ス ) の 場 合 */ c[y] = c[y]; } 62 63 64 65 66 67 68 69 70 printf ("%c",c[y]); /* 暗号化した結果を出力 */ } else{ /* 暗号化できない場合 */ puts(" 暗号化できません。 "); break; } 71 72 73 74 75 76 } printf ("\n"); 77 78 79 } 80 81 82 83 84 85 86 87 void hukugou (char *c,int ransu ){ /* 関数 rot13 */ int y; /* カウント */ for(y=0;c[y +1]!= ’\0 ’;y++){ /* 文字列が ’\0’( 文 字 列 の 終 わ り ) に な る ま で ル ー プ */ if (0 x20 <= c[y] && c[y] <= 0x7e ){ /* 出力可能な文字の場合 */ if (0 x21 <= c[y] && c[y] <= 0x4f ){ /*0 x 2 1 ∼ 0 x 4 f の 間 の 場 合 */ c[y] = c[y] + 47; }else if(0 x50 <= c[y] && c[y] <= 0x7e ){ /*0 x 5 0 ∼ 0 x 7 e の 間 の 場 合 */ 14 c[y] = c[y] - 47; }else if(c[y] == 0x20 ){ /* 文字が SP( ス ペ ー ス ) の 場 合 */ c[y] = c[y]; } 88 89 90 91 92 c[y] = c[y] - ransu; if (c[y] <= 0x1f ){ /* 乱数分ずらした結果、表示可能領域を下回ってしまった場合 */ c[y] = 0x7e - (0 x1f - c[y]); } printf ("%c",c[y]); /* 復号化した結果を出力 */ 93 94 95 96 97 } else{ /* 復号化できない場合 */ puts(" 復号化できません。 "); break; } 98 99 100 101 102 } printf ("\n"); 103 104 105 } ✓ 出力結果 ✏ モードを選択してください。0:終了 1:新規入力 1 乱数値 7 文字を入力してください。 !"#$%&’()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNO 暗号化 WXYZ[\]ˆ_‘abcdefghijklmnopqrstuvwxyz{|}˜!"#$%&’ モードを選択してください。0:終了 1:新規入力 2:前回入力した文字を復号化 2 暗号化時の乱数値を入力してください。 7 復号前 WXYZ[\]ˆ_‘abcdefghijklmnopqrstuvwxyz{|}˜!"#$%&’ 復号化 !"#$%&’()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNO モードを選択してください。0:終了 1:新規入力 2:前回入力した文字を復号化 1 乱数値 9 文字を入力してください。 Tatsuki IHA 暗号化 .;NMOECX#"y モードを選択してください。0:終了 1:新規入力 2:前回入力した文字を復号化 2 暗号化時の乱数値を入力してください。 7 復号前 .;NMOECX#"y 復号化 Vcvuwmk"KJC モードを選択してください。0:終了 1:新規入力 2:前回入力した文字を復号化 0 プログラムを終了します。 ✒ ✑ 15 解説 このプログラムは擬似乱数を使い、ASCII コードの出力できる文字、記号の暗号化、復号化プログラムである。 先程の 47 文字ずらすだけの暗号化では暗号化した後に暗号化と同じ処理を行うと復号化されてしまう。その問題を解 決するために使用する値が乱数である。乱数とはその名の通りランダムな数のことである。 コンピュータは正確な機械であるため本当にランダムに数を作ったりはできないが、計算によってランダムのように 見える数を得ることができる。その手法を擬似乱数と呼ぶ。 擬似乱数の処理を行うには stdlib.h の rand() 関数を使う。このプログラムは 2 行目で stdlib.h を読み込んでいる。 rand() 関数は 0∼RAND MAX の間からランダムの値を返す。30 行目でそのランダムの値と 10 の剰余算を行なってい る。つまり 0∼9 までの数値のなかでランダムで変数 ransu に代入される。 暗号化の場合は 57 行目の処理で乱数分文字をずらす。その後にさらに 47 文字ずらす。 復号化の場合は乱数の入力を求め、まず 47 文字ずらした後、乱数分文字をずらす。乱数の入力するとき暗号化された 乱数でない場合、デタラメに復号化される。 考察 ・擬似乱数を使用する場合は rand() 関数を使う。rand() 関数は 0∼RAND MAX の間の値を返す。 ・RAND MAX は定義によって異なる。(ちなみに私の mac は 231 − 1(約 21 億) だった。) ・乱数を使うことによって簡単に復号化できなくなった。 ・今回は 0∼9 までの乱数しか使ってないが、乱数の幅を広げると更に復号化が難しくなる。 6 感想 今回暗号ということで、関数 islower()、toupper() など初めて使う関数が出てきて少し戸惑ったが、オリジナルの 暗号化、復号化プログラムを作成するときには乱数まで使えてよかった。 16
© Copyright 2024 ExpyDoc