前回の続きです。
前回↓
今回の更新内容
今回実装するのはズバリ「可変長コード」です。
よく使う文字、例えばEやTを2~3ビット、めったに使わない文字は8ビット以上などと可変していくやつです。
しかし、E=0, T=01だとした場合、EなのかAなのか判断することができません。
そのため以下のように定義していきます。
E=00
T=010
A=011
O=1000
......
すると、ここまでが一文字だ!とわかるようになります。
本当であれば、実際に入力されている文字で、回数が多い文字を少ないビットにするべきですが、今回は簡単な事前定義式でいきます。
今回のcharmap.csvの内容は以下のようになります。
char,code E,00 T,010 A,011 O,1000 I,1001 N,1010 S,1011 H,11000 R,11001 D,11010 L,11011 C,111000 U,111001 M,111010 W,111011 F,1111000 G,1111001 Y,1111010 P,1111011 B,1111100 V,1111101 K,11111100 J,11111101 X,111111100 Q,111111101 Z,111111110 ,1111111110 .,11111111110 \n,111111111110 END,111111111111
はい、小文字が犠牲になりました。実用的では、、、ないような、、、
ソースコード
まず最初にエンコード用のPythonコードです。
import csv
def load_charmap(filepath):
char_map = {}
with open(filepath, mode='r', encoding='utf-8') as f:
reader = csv.DictReader(f)
for row in reader:
char = row['char']
# CSVの中の "\n" 文字列を、実際の改行文字 '\n' に置換する
if char == r'\n':
char = '\n'
code_str = row['code']
char_map[char] = code_str
return char_map
def encode_to_binary(input_text_path, output_bin_path, charmap_path):
char_map = load_charmap(charmap_path)
with open(input_text_path, 'r', encoding='utf-8') as f:
text = f.read()
bit_stream_list = []
# 1文字ずつループ処理(アルファベットだけ大文字化)
for char in text:
# アルファベットなら大文字に、それ以外(改行や記号)はそのまま
target_char = char.upper() if char.isalpha() else char
if target_char in char_map:
bit_stream_list.append(char_map[target_char])
else:
print(f"警告: 文字 {repr(char)} は定義されていないためスキップされました。")
# 終端記号 (END) を追加
bit_stream_list.append(char_map['END'])
bit_stream = "".join(bit_stream_list)
remainder = len(bit_stream) % 8
if remainder != 0:
bit_stream += "0" * (8 - remainder)
byte_list = [int(bit_stream[i:i+8], 2) for i in range(0, len(bit_stream), 8)]
with open(output_bin_path, 'wb') as f:
f.write(bytes(byte_list))
print("【エンコード完了】")
if __name__ == '__main__':
encode_to_binary('test.txt', 'encoded.bin', 'charmap.csv')せっかくなので「E」「T」「A」などを多用していきましょう。自分の場合こうです。
そして出来上がったencoded.binとファイルサイズを比較していきましょう。
まあ、何度も言うように実用的ではありませんが、、、
続いてデコード用のプログラムです。
import csv
def load_inv_charmap(filepath):
inv_map = {}
with open(filepath, mode='r', encoding='utf-8') as f:
reader = csv.DictReader(f)
for row in reader:
char = row['char']
# 【ここを修正】2文字の "\n" を実際の改行文字 '\n' に置換する
if char == r'\n':
char = '\n'
code_str = row['code']
inv_map[code_str] = char
return inv_map
def decode_from_binary(input_bin_path, output_text_path, charmap_path):
inv_map = load_inv_charmap(charmap_path)
with open(input_bin_path, 'rb') as f:
raw_bytes = f.read()
bit_stream = "".join(f"{b:08b}" for b in raw_bytes)
decoded_chars = []
current_buffer = ""
for bit in bit_stream:
current_buffer += bit
if current_buffer in inv_map:
target = inv_map[current_buffer]
if target == 'END':
break
decoded_chars.append(target)
current_buffer = "" # バッファリセット
decoded_text = "".join(decoded_chars)
with open(output_text_path, 'w', encoding='utf-8') as f:
f.write(decoded_text)
print("【アプローチA:デコード完了】")
if __name__ == '__main__':
decode_from_binary('encoded.bin', 'decoded.txt', 'charmap.csv')
おわり
今回はここでおわりです。また続きをかこうかなあ。
読み込んだ文章の中で文字の出現回数から動的にビット数を変えるとかやってみたいな。
0 件のコメント:
コメントを投稿