怒涛のテスト期間が終わったのでまた記事を書いていきたいと思います。今回のテスト範囲には文字コードに関するものがありました。文字コードに関する勉強をしたら、やはり最初にやりたくなるのは文字コードを自作することですよね。
最近はAIが発達しているおかげでそのような馬鹿げた発想でも簡単に実装ができるようになりました。載せているコードもAIで生成したやつです。
方針
まず最初につくる文字コードとして、5ビット固定でやっていきたいと思います。
これであれば2^5=32パターンなのでアルファベットのA-Zまですべて網羅することができます。
しかしながらこの場合では小文字か大文字どちらかにしか対応できません。
ということでそこは制御文字を入れ現在大文字モードだったら大文字としてエンコード/デコードし、現在小文字モードだったら小文字としてエンコード/デコードするといった形に。
ということで、最終的な文字のマッピング表は以下のようになります。
mode,char,code 0,A,0 0,B,1 0,C,2 0,D,3 0,E,4 0,F,5 0,G,6 0,H,7 0,I,8 0,J,9 0,K,10 0,L,11 0,M,12 0,N,13 0,O,14 0,P,15 0,Q,16 0,R,17 0,S,18 0,T,19 0,U,20 0,V,21 0,W,22 0,X,23 0,Y,24 0,Z,25 0,?,26 0,",",27 0,!,28 0,SHIFT_LOWER,29 0,\n,30 0,END,31 1,a,0 1,b,1 1,c,2 1,d,3 1,e,4 1,f,5 1,g,6 1,h,7 1,i,8 1,j,9 1,k,10 1,l,11 1,m,12 1,n,13 1,o,14 1,p,15 1,q,16 1,r,17 1,s,18 1,t,19 1,u,20 1,v,21 1,w,22 1,x,23 1,y,24 1,z,25 1, ,26 1,.,27 1,!,28 1,SHIFT_UPPER,29 1,\n,30 1,END,31
このファイルをcharmap.csvとして保存してください。
エンコード用
ではこれをもとに、エンコード用のプログラムとデコード用のプログラムを依頼しました。
import csv
def load_charmap(filepath):
# (mode, char) -> code のキーで辞書化
encode_map = {}
with open(filepath, mode='r', encoding='utf-8') as f:
reader = csv.DictReader(f)
for row in reader:
mode = int(row['mode'])
char = row['char']
if char == r'\n':
char = '\n'
code = int(row['code'])
encode_map[(mode, char)] = code
return encode_map
def encode_to_binary(input_text_path, output_bin_path, charmap_path):
encode_map = load_charmap(charmap_path)
with open(input_text_path, 'r', encoding='utf-8') as f:
text = f.read()
current_mode = 0 # 最初は大文字モード (0)
codes = []
for char in text:
in_mode_0 = (0, char) in encode_map
in_mode_1 = (1, char) in encode_map
if current_mode == 0:
if in_mode_0:
codes.append(encode_map[(0, char)])
elif in_mode_1:
# 小文字モードにシフトしてから文字を追加
codes.append(encode_map[(0, 'SHIFT_LOWER')])
current_mode = 1
codes.append(encode_map[(1, char)])
else:
print(f"未対応文字: '{char}'")
elif current_mode == 1:
if in_mode_1:
codes.append(encode_map[(1, char)])
elif in_mode_0:
# 大文字モードにシフトしてから文字を追加
codes.append(encode_map[(1, 'SHIFT_UPPER')])
current_mode = 0
codes.append(encode_map[(0, char)])
else:
print(f"未対応文字: '{char}'")
# 終端記号 (END) を挿入
codes.append(encode_map[(current_mode, 'END')])
# 5ビット列の連結とバイナリ出力
bit_stream = "".join(f"{code:05b}" for code in codes)
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(f"【エンコード完了】")
print(f" - 元テキスト: '{text}' ({len(text)}文字)")
print(f" - 変換後コード数: {len(codes)} 個 (制御文字含む)")
print(f" - 出力ファイルサイズ: {len(byte_list)} バイト")
if __name__ == '__main__':
encode_to_binary('test.txt', 'encoded.bin', 'charmap.csv')このような感じです。
ちなみに生成されたencoded.binをメモ帳などで開こうとすると当然文字化けします。
なぜから、存在しない文字コードで書いていますからね。
VScodeで開いてみたのが以下のやつです。
当然改行の制御文字も一致しないため、一行のよくわからない文章となっちゃいます。
デコード用
これをまた複合化しなければいけませんのでデコード用のソースコードを依頼しました。
import csv
def load_inv_charmap(filepath):
# (mode, code) -> char のキーで辞書化
decode_map = {}
with open(filepath, mode='r', encoding='utf-8') as f:
reader = csv.DictReader(f)
for row in reader:
mode = int(row['mode'])
char = row['char']
if char == r'\n':
char = '\n'
code = int(row['code'])
decode_map[(mode, code)] = char
return decode_map
def decode_from_binary(input_bin_path, output_text_path, charmap_path):
decode_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)
current_mode = 0 # 最初は大文字モード (0)
decoded_chars = []
for i in range(0, len(bit_stream), 5):
chunk = bit_stream[i:i+5]
if len(chunk) < 5:
break
code = int(chunk, 2)
target = decode_map.get((current_mode, code), '?')
# 制御文字の判定
if target == 'END':
break
elif target == 'SHIFT_LOWER':
current_mode = 1 # 内部モードを小文字(1)に変更
elif target == 'SHIFT_UPPER':
current_mode = 0 # 内部モードが大文字(0)に変更
else:
decoded_chars.append(target) # 通常文字を出力
decoded_text = "".join(decoded_chars)
with open(output_text_path, 'w', encoding='utf-8') as f:
f.write(decoded_text)
print(f"【デコード完了】")
print(f" - 復元テキスト: '{decoded_text}'")
if __name__ == '__main__':
decode_from_binary('encoded.bin', 'decoded.txt', 'charmap.csv')
これで行けると思います。
ちなみに1文字5ビットとかいうめちゃくちゃ小さいサイズです。ファイルは1バイト=8ビットづずしか読めないらしいのでなんかいろいろしてます。
この文字コードの特徴:めちゃくちゃファイルサイズが小さい
以下のスクリーンショットをご覧ください。
しっかりとファイルの軽量化に成功しています。
......まあ対応文字がめちゃくちゃ少ないので。。。。。。
ということで今回はここまでです。
次回はもう少し改善し、さらなる軽量化にいどみます。
0 件のコメント:
コメントを投稿