Python

プログラマの考えが・・身につく本より(Python):メッセージの復号

More than 3 years have passed since last update.

問題:メッセージの復号(例題の文字列をコードで示す)


あるメッセージがテキストストリームとして符号化されており、それを1文字ずつ読み込む。ストリームの中身はカンマで区切られた整数値の羅列で、個々の数値はC++のint型で扱える範囲の正の数である。しかし、その数値がどの文字を表しているのかは、現在の復号モードによって変わる。モードは3種類あり、大文字・小文字・記号のいずれかになる。

大文字モードでは、個々の数値がアルファベットの大文字を表す。数値を27で割った余りがアルファベットで割った余りは8で、アルファベットの8番目の文字はHだからである。

小文字モードの場合も同じだが、アルファベットの小文字を表している。数値を27で割った余りがアルファベットの文字に対応する(1をaとする)。たとえば、入力が56なら出力はbになる。57を27で割った余りは2で、アルファベットの2番目の文字はbだからである。

記号モードの場合は、数値を9で割った余りを使う。この余りを表2-3に従って変換する。たとえば、19は感嘆符になる。19を9で割った余りは1だからである。

メッセージが始まった時点の復号モードは、大文字モードである。剰余演算(モードによって、27あるいは9のいずれかを使う)の結果が0になるたびに、復号モードが切り替わる。現在のモードが大文字の場合は小文字モードへ、現在のモードが小文字の場合は記号モードへ、記号モードの場合は再び大文字モードに戻る。

表2-3 記号モードの復号

数字   記号

1     !

2     ?

3     ,

4     .

5     (スペース)

6     ;

7     "

8     \n(改行)  

例として以下画像に示している通り

処理手順は、大文字(U)から小文字(L)そして記号(P)へと切り替わっている。列(c)その時点のモードで使う除数を示す。列(d)は、列(あ)の入力を列(c)の除数で割った余りだ。処理結果を列(e)に示す。もし(d)が0なら、次のモードへ切り替わる。

例題文字列⇨18,12312,171,763,98423,1208,216,11,500,18,241,0,32,20620,27,10

2-3.png


さて、これをどうやったらPythonコードにできるかなといつものように小さな問題に切り分けろと指示がありましたので詳細を示します。


  • 行末に達するまで1文字ずつ読み込む (ここはPythonのinput()で解決)

  • 数字を表す文字列を整数に変換する。(ここもPythonのinput()で解決)

  • 1から26までの整数値をアルファベット大文字に変換する。

  • 1から26までの整数値をアルファベット小文字に変換する。

  • 1から8までの整数値を表2-3に従って記号に変換する。

  • 復号モードを記憶する。

以上の内容で進めていきたいと思います。

ですので、早速

1から26までの整数値をアルファベット大文字に変換する。

書籍の内容だとASCII文字コードと対応させて、入力1〜26までの整数値に何らかの文字コードを足せば、AからZまでの文字に変換できるのでは?と進めています。

前回同様、PythonではASCII文字コードを利用せず違う方法でやってみようと思いますが、Pythonならというか初歩的というかリストに入れてインデクスで取り出すということでいいのかな

これなら

- 1から26までの整数値をアルファベット大文字に変換する。

- 1から26までの整数値をアルファベット小文字に変換する。

- 1から8までの整数値を表2-3に従って記号に変換する。

対応できると思う.


test12.py

#!/usr/bin/env python

#coding:utf-8

from ConsoleOut import cout #pythonでcout<<を使う為のクラスを用意(教えて頂きました)
import enum
import math

UPPERCASE = [' ','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']
LOWERCASE = [' ','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']
PUNCTUATION = [' ','!','?',',','.',' ',';','"','\n']

modeType = UPPERCASE

while modeType in [UPPERCASE,LOWERCASE,PUNCTUATION]:
cout << "Enter some numbers ending with -1: "
digit = input()
if modeType == UPPERCASE:
cout << "Number read: " + digit + "\n"
digit = int(digit) % 27
cout << ". Modulo 27: " + UPPERCASE[digit] + "\n"
if digit == 0:
modeType = LOWERCASE
else:
continue

elif modeType == LOWERCASE:
cout << "Number read: " + digit + "\n"
digit = int(digit) % 27
cout << ". Modulo 27: " + LOWERCASE[digit] + "\n"
if digit == 0:
modeType = PUNCTUATION
else:
continue

elif modeType == PUNCTUATION:
cout << "Number read: " + digit + "\n"
digit = int(digit) % 9
cout << ". Modulo 27: " + PUNCTUATION[digit] + "\n"
if digit == 0:
modeType = UPPERCASE
else:
continue
・・・・(ターミナル実行)
>>> import test12
Enter some numbers ending with -1: 18
Number read: 18
. Modulo 27: R
Enter some numbers ending with -1: 12312
Number read: 12312
. Modulo 27:
Enter some numbers ending with -1: 171
Number read: 171
. Modulo 27: i
Enter some numbers ending with -1: 763
Number read: 763
. Modulo 27: g
Enter some numbers ending with -1: 98423
Number read: 98423
. Modulo 27: h
Enter some numbers ending with -1: 1208
Number read: 1208
. Modulo 27: t
Enter some numbers ending with -1: 216
Number read: 216
. Modulo 27:
Enter some numbers ending with -1: 11
Number read: 11
. Modulo 27: ?
Enter some numbers ending with -1: 500
Number read: 500
. Modulo 27:
Enter some numbers ending with -1: 18
Number read: 18
. Modulo 27:
Enter some numbers ending with -1: 241
Number read: 241
. Modulo 27: Y
Enter some numbers ending with -1: 0
Number read: 0
. Modulo 27:
Enter some numbers ending with -1: 32
Number read: 32
. Modulo 27: e
Enter some numbers ending with -1: 20620
Number read: 20620
. Modulo 27: s
Enter some numbers ending with -1: 27
Number read: 27
. Modulo 27:
Enter some numbers ending with -1: 10
Number read: 10
. Modulo 27: !
Enter some numbers ending with -1:


とりあえず無限ループで整数値からアルファベットへ変換とモード切り替えで記号までを出力できるようになりましたが、途中なぜか空白が余分にでる部分や無限ループを抜けるのどうしようなど微妙な点も多々ありますが、とりあえず動いた。

ちなみにC++の解答がこちら

char outputCharacter;

enum modeType {UPPERCASE,LOWERCASE,PUNCTUATION};
modeType mode = UPPERCASE;
char digitChar;
do {
digitChar = cin.get();
int number = (digitChar - '0');
digitChar = cin.get();
while ((digitChar != 10)&&(digitChar != ',')){
number = number * 10 + (digitChar - '0');
digitChar = cin.get();
}
switch (mode){
case UPPERCASE:
number = number % 27;
outputCharacter = number + 'A' - 1;
if(number == 0){
mode = LOWERCASE;
continue;
}
break;
case LOWERCASE:
number = number % 27;
outputCharacter = number + 'a' - 1;
if (number == 0){
mode = PUNCTUATION;
continue;
}
break;
case PUNCTUATION:
number = number % 9;
switch(number){
case 1: outputCharacter = '!';break;
case 2: outputCharacter = '?';break;
case 3: outputCharacter = ',';break;
case 4: outputCharacter = '.';break;
case 5: outputCharacter = ' ';break;
case 6: outputCharacter = ';';break;
case 7: outputCharacter = '"';break;
case 8: outputCharacter = '\';break;
}
if(number == 0){
mode = UPPERCASE;
continue;
}
break;
}
cout << outputCharacter;
}while (digitChar != 10);
cout << "\n";

今回は細かく区切ることができなかったので解答コードを見ながら自分ならではのpythonコードを書いてみました。