schedule2018-07-17

08. 暗号化

はじめに

言語処理100本ノック 2015

Pythonを勉強するため、東京工業大学の岡崎教授が出題されている言語処理100本ノック 2015を解いていきます。

より深く理解するため、別解や利用したライブラリの解説もまとめていきます。

環境

Python3.6

OS : mac

問題

  1. 暗号文
    与えられた文字列の各文字を,以下の仕様で変換する関数cipherを実装せよ.

英小文字ならば(219 - 文字コード)の文字に置換
その他の文字はそのまま出力
この関数を用い,英語のメッセージを暗号化・復号化せよ.

解答

import re


def cipher(src):
    """
    英小文字ならば(219 - 文字コード)の文字に置換
    その他の文字はそのまま出力
    """
    return re.sub(r'[a-z]', lambda m: chr(219 - ord(m.group(0))), src)

text = "Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can."

# 暗号化
print(cipher(text))
# => Hr Hv Lrvw Bvxzfhv Blilm Clfow Nlg Ocrwrav Foflirmv. Nvd Nzgrlmh Mrtsg Aohl Srtm Pvzxv Svxfirgb Cozfhv. Aigsfi Krmt Czm.

# 復号化
print(cipher(cipher(text)))
# => Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can.

解説

正規表現で得た値をlambdaで処理する記事を、見た覚えがあったのでやってみました。

参考

正規表現を使って置換するre.sub(pattern, repl, string)を利用します。 patternは置換される文字列、stringは対象の文字列です。

replは置換する文字列ですが、関数を置くこともできます。

def dashrepl(matchobj):
    if matchobj.group(0) == '-':
        return ' '
    else:
        return '-'

print(re.sub('-{1,2}', dashrepl, 'pro----gram-files'))
# => pro--gram files

関数を使った例です。
'-'のとき半角スペース、それ以外('--')を'-'と置いています。

今回は、lambda関数を使いました。

re.sub(r'[a-z]', lambda m: chr(219 - ord(m.group(0))), src)

ここで、mはマッチオブジェクトです。中身はこんな感じ<_sre.SRE_Match object; span=(1, 2), match='b'>

そのままだと、TypeError:が起きるため文字を取り出す必要があります。 マッチオブジェクトから文字列を取り出すには、m.group(0)とします。

暗号化、復号化

この問題のアルゴリズムでは暗号化と復号化が同じ関数で出来てしまいます。

ord()は文字を数値に、chr()は数値を文字に変換します。 どちらも、Unicode コードポイントを表す整数を基準としています。

小文字のポイントを見てみると、ord('a')は97、ord('z')は122です。

ここで219という数値がどう関係するか見えてきました。 式にするとord('a') + ord('z') = 219です。

つまり、暗号化も復号化もaからzまでの並びを反転した位置の文字に置き換えています。

大文字にも対応してみる。

ord('A') + ord('Z') = 155でした。
つまり、英大文字ならば(155 - 文字コード)の文字に置換を加えてあげれば良さそうです。

import re


def cipher(src):
    """
    英小文字ならば(219 - 文字コード)の文字に置換
    英大文字ならば(155 - 文字コード)の文字に置換
    その他の文字はそのまま出力
    """
    tmp = re.sub(r'[a-z]', lambda m: chr(219 - ord(m.group(0))), src)
    return re.sub(r'[A-Z]', lambda m: chr(155 - ord(m.group(0))), tmp)

text = "Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can."

# 暗号化
print(cipher(text))
# => Sr Sv Orvw Yvxzfhv Ylilm Xlfow Mlg Lcrwrav Uoflirmv. Mvd Mzgrlmh Nrtsg Zohl Hrtm Kvzxv Hvxfirgb Xozfhv. Zigsfi Prmt Xzm.

# 復号化
print(cipher(cipher(text))) 
# => Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can.

lambdaを使いまわしてしまいましたが、これで大丈夫そうです。

続いての記事

Python3で言語処理100本ノックまとめ

前の問題:07. テンプレートによる文生成

次の問題:09. Typoglycemia