Pythonでバイナリデータをデコードする方法は、以下のものがあるが、
今回の課題はバイナリデータではなく、バイナリデータの文字列をデコードすることです。
1
2
3
4
5
| # エンコード(文字列の"日本"をbytesに変換)
print("日本".encode("utf-8"))
# デコード(bytesを文字列の"日本"に変換)
print(b'\xe6\x97\xa5\xe6\x9c\xac'.decode("utf-8"))
|
なぜこの変な課題があるかは、AWS S3 Triggerで起きたイベントに、日本語名KeyがUTF-8でエンコードされて、以下のように出ています。
1
2
3
4
5
| # S3 Trigger設定された元のS3 Key名
"日本/test.pdf"
# S3 Triggerで起きたイベントに出ているKey名
"%e6%97%a5%e6%9c%ac/test.pdf"
|
バイナリデータの文字列は、文字列型の変数のため、直接bytes関数よりデコードができません。
また、この変数をさらにエンコードしてから、デコードしてもできません。
以下の結果になります。
1
2
3
| bstr = "\xe6\x97\xa5\xe6\x9c\xac"
print(bstr.encode("utf-8").decode("utf-8"))
# output:\\xe6\\x97\\xa5\\xe6\\x9c\\xac
|
Pythonのbytes
仕様を調べたら、fromhex
関数があるため、
16進数の文字を以下のようにデコードができます。
1
2
3
| bstr = " e6 97 a5 e6 9c ac"
print(bytes.fromhex(bstr))
# output:"日本"
|
上記の方法を従って、S3 Triggerで起きたイベントに出ているKey名に、
16進数の文字を判別して、fromhex
でデコードできる発想がありました。
デコード例:
“%e6%97%a5%e6%9c%ac/test.pdf"から、"%e6%97%a5%e6%9c%ac"を16進数として判別し、” e6 97 a5 e6 9c ac"に変換してからfromhex
でデコードします。
デコード後の文字と16進数ではない文字を結合して、元のKey名になります。
デコード後の"日本" + 16進数ではない文字"/test.pdf" → “日本/test.pdf”
今回の課題は以下の実装をご参照ください。
fromhex
を利用して、デコード実装
見出しへのリンク
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
| def isHex(str):
try:
int(str, 16)
return True
except:
return False
def convertBstrToStr(binary_str):
decoded_str = ""
tmp_hex_str = ""
index = 0
while index < len(binary_str):
c = binary_str[index : index + 1]
if c == "%":
hex_str = binary_str[index + 1 : index + 3]
if isHex(hex_str):
tmp_hex_str = tmp_hex_str + " " + hex_str
index = index + 2
else:
decoded_str = decoded_str + c
else:
if tmp_hex_str != "":
binary_str_bytes = bytes.fromhex(tmp_hex_str)
decoded_str = decoded_str + binary_str_bytes.decode("utf-8")
tmp_hex_str = ""
decoded_str = decoded_str + c
index = index + 1
return decoded_str
|