PythonでUnicodeエスケープされた文字列・バイト列を変換
\u3042
のように\u
と4桁の16進数からなるUnicodeエスケープシーケンスを含む文字列・バイト列を相互に変換する方法を説明する。
Python3の場合について、以下の内容を説明する。
- 文字列をUnicodeエスケープされたバイト列に変換(エンコード)
- Unicodeエスケープされたバイト列を文字列に変換(デコード)
- Unicodeエスケープされた文字列を通常の文字列に変換
- 通常の文字列をUnicodeエスケープされた文字列に変換
- Unicodeエスケープシーケンスをprint()でそのまま出力
- Unicodeエスケープされた文字列を含むファイルを読み込み
- JSONのUnicodeエスケープ
Unicodeコードポイント(文字コード)と文字を相互に変換する方法については以下の記事を参照。
文字列をUnicodeエスケープされたバイト列に変換(エンコード)
文字列からバイト列への変換(エンコード)は文字列(str
型)のメソッドencode()
を使う。
Unicodeエスケープされたバイト列へエンコードする場合は、第一引数encoding
に'unicode-escape'
を指定する。ハイフンではなくアンダースコアの'unicode_escape'
でもOK。
unicode-escape
はPython特有のエンコーディング。Python2ではstring-escape
という名称だった。
s = 'あいうえお'
b = s.encode('unicode-escape')
print(b)
# b'\\u3042\\u3044\\u3046\\u3048\\u304a'
print(type(b))
# <class 'bytes'>
Unicodeエスケープされたバイト列を文字列に変換(デコード)
バイト列から文字列への変換(デコード)はバイト列(bytes
型)のメソッドdecode()
を使う。
エンコードと同じく第一引数encoding
に'unicode-escape'
を指定するとUnicodeエスケープされたバイト列が元の文字列に戻る。
s_from_b = b.decode('unicode-escape')
print(s_from_b)
# あいうえお
print(type(s_from_b))
# <class 'str'>
Unicodeエスケープされた文字列を通常の文字列に変換
Unicodeエスケープされたバイト列をutf-8
でデコードすると、Unicodeエスケープのまま文字列に変換される。第一引数encoding
のデフォルト値は'utf-8'
なので省略しても同じ結果。
s_from_b_error = b.decode('utf-8')
print(s_from_b_error)
# \u3042\u3044\u3046\u3048\u304a
print(type(s_from_b_error))
# <class 'str'>
このような文字列は、encode()
でバイト列に変換してから再度decode()
で文字列に変換すると、Unicodeエスケープされていない文字列に戻る。
s_from_s = s_from_b_error.encode().decode('unicode-escape')
print(s_from_s)
# あいうえお
print(type(s_from_s))
# <class 'str'>
標準ライブラリのcodecsモジュールを使って直接変換することも可能。
import codecs
s_from_s_codecs = codecs.decode(s_from_b_error, 'unicode-escape')
print(s_from_s_codecs)
# あいうえお
print(type(s_from_s_codecs))
# <class 'str'>
なお、ここでは説明のためにUnicodeエスケープされた文字列を作成したが、本来はUnicodeエスケープ(\u
)が残らないようにしておくべき。大元の処理(バイト列からのデコード)を修正できる状況であればそちらを修正したほうがいい。
通常の文字列をUnicodeエスケープされた文字列に変換
Unicodeエスケープシーケンス(\uXXXX
)を確認したい場合は、組み込み関数ascii()
を使う。全角文字などの非ASCII 文字が\u
でエスケープされる。
ascii()
は先頭と末尾に引用符'
を含んだ文字列(\uXXXX
の6文字分とあわせて8文字)を返す。
s_ascii = ascii('あ')
print(s_ascii)
# '\u3042'
print(type(s_ascii))
# <class 'str'>
print(s_ascii[0])
# '
print(s_ascii[-1])
# '
print(len(s_ascii))
# 8
以下の文字列と等価。
print(ascii('あ') == "'\\u3042'")
# True
引用符を取り除きたい場合はスライスを使う。
s_unicode_escape = ascii('あ')[1:-1]
print(s_unicode_escape)
# \u3042
print(type(s_unicode_escape))
# <class 'str'>
print(s_unicode_escape == '\\u3042')
# True
Unicodeエスケープシーケンスをprint()でそのまま出力
Unicodeエスケープシーケンス(\uXXXX
)は文字列(str
型)中にそのまま記述すると対応する文字一文字分として扱われ、print()
では対応する文字が出力される。
print('\u3042')
# あ
print(len('\u3042'))
# 1
print('\u3042' == 'あ')
# True
そのまま出力したい場合は、バックスラッシュを\\
で表すか、エスケープシーケンスを無視するraw文字列を使う。
print('\\u3042')
# \u3042
print(r'\u3042')
# \u3042
print(len(r'\u3042'))
# 6
Unicodeエスケープされた文字列を含むファイルを読み込み
\u3042\u3044\u3046\u3048\u304a
という文字列のテキストファイルを読み込む。
open()
の引数encoding
を設定しないとそのまま読み込まれる。
with open('data/src/unicode_escape.txt') as f:
s = f.read()
print(s)
print(type(s))
print(len(s))
# \u3042\u3044\u3046\u3048\u304a
# <class 'str'>
# 30
encoding='unicode-escape'
とすると対応する文字列に変換される。
with open('data/src/unicode_escape.txt', encoding='unicode-escape') as f:
s = f.read()
print(s)
print(type(s))
print(len(s))
# あいうえお
# <class 'str'>
# 5
JSONのUnicodeエスケープ
PythonでUnicodeエスケープに遭遇しがちなのが、Web APIでjsonなどを取得する場合。
標準ライブラリのurllib.request
モジュールの関数urllib.request.urlopen()
はバイト列(bytes
型)を返す。
Unicodeエスケープされたバイト列をdecode()
メソッド文字列に変換(デコード)する場合、第一引数encoding
に'utf-8'
を指定すると(引数を省略した場合も'utf-8'
)、Unicodeエスケープシーケンス(\uXXXX
)を含んだ文字列となる。
上で説明したように、第一引数encoding
に'unicode-escape'
を指定すればよい。
b_json = b'{"a": "\u3042"}'
print(b_json)
# b'{"a": "\\u3042"}'
print(b_json.decode())
# {"a": "\u3042"}
print(b_json.decode('unicode-escape'))
# {"a": "あ"}
jsonモジュールのloads()関数
標準ライブラリのjsonモジュールのloads()
関数を使って、JSON形式の文字列を辞書(dict
型オブジェクト)に変換する場合は、Unicodeエスケープシーケンスを含んだ文字列のままでOK。
loads()
関数の内部でUnicodeエスケープシーケンスを変換してくれる。
import json
print(json.loads(b_json.decode()))
# {'a': 'あ'}
print(type(json.loads(b_json.decode())))
# <class 'dict'>
バージョン3.6からはloads()
の引数にバイト列(bytes
型)を指定できるようになったので、Unicodeエスケープされたバイト列もそのまま指定可能。
print(json.loads(b_json))
# {'a': 'あ'}
内部でdetect_encoding()
という関数が定義されており、エンコーディングをutf-8
, utf-16
, utf-32
から自動判別してバイト列をデコードしている。
utf-8
, utf-16
, utf-32
以外でエンコードされたバイト列の場合は、loads()
に直接渡すのではなく、decode()
メソッドでエンコーディングを指定してデコードする必要があるので注意。