Pythonの文字エンコード取り扱いは、バージョン2から3に上がる時に互換性をかなぐり捨てたせいで、ややこしい。
プログラミング時のために、覚書をまとめてみた
デフォルト・エンコーディングを確認する
Python 2 (Ubuntu)
>>> sys.version '2.7.17 (default, Sep 30 2020, 13:38:04) \n[GCC 7.5.0]' >>> import sys >>> print(sys.stdout.encoding) UTF-8 >>> print(sys.stdin.encoding) UTF-8 >>> print(sys.getdefaultencoding()) ascii >>> sys.getdefaultencoding() 'ascii' >>> print(sys.getfilesystemencoding()) UTF-8 >>> import locale >>> print(locale.getpreferredencoding()) UTF-8
Python 3 (Ubuntu)
>>> sys.version '3.6.9 (default, Oct 8 2020, 12:12:24) \n[GCC 8.4.0]' >>> import sys >>> print(sys.stdout.encoding) UTF-8 >>> print(sys.stdin.encoding) UTF-8 >>> print(sys.getdefaultencoding()) # str.encode(), str.decode() で引数なしの場合のデフォルトエンコード utf-8 >>> print(sys.getfilesystemencoding()) # ファイル名のエンコード utf-8 >>> import locale >>> print(locale.getpreferredencoding()) # テキストファイルを開く時のデフォルトエンコード UTF-8
Python 3 (Windows 10) の Ubuntu からの差分
>>> sys.version
'3.9.0 (tags/v3.9.0:9cf6752, Oct 5 2020, 15:34:40) [MSC v.1927 64 bit (AMD64)]'
>>> print(locale.getpreferredencoding()) # テキストファイルを開く時のデフォルトエンコード
cp932
文字列のエンコードとデコード
Python 2 (Ubuntu)
>>> s="文字列" >>> print(s) 文字列 >>> print(type(s)) <type 'str'> # Pythonスクリプト内で扱える str になっている >>> print(s.decode('utf-8')) # str から unicode に変換する 文字列 >>> print(type(s.decode('utf-8'))) <type 'unicode'> >>> print(s.encode('utf-8')) # str から str に変換することはできない Traceback (most recent call last): File "<stdin>", line 1, in <module> UnicodeDecodeError: 'ascii' codec can't decode byte 0xe6 in position 0: ordinal not in range(128) >>> print(type(s.encode('utf-8'))) Traceback (most recent call last): File "<stdin>", line 1, in <module> UnicodeDecodeError: 'ascii' codec can't decode byte 0xe6 in position 0: ordinal not in range(128) >>> print(type(s.decode('utf-8').encode('utf-8'))) # str を unicode に(decode関数)、さらに unicode から str に(encode関数)変換する <type 'str'> >>> print(s.decode('utf-8').encode('utf-8')) 文字列 >>> print(s[1]) # Python 2 では、str は(マルチバイトの)文字列を「文字」ごとに扱えない � >>> print(s.decode('utf-8')[1]) # unicode は(マルチバイトの)文字列を「文字」ごとに扱える 字 >>> s='abcdefg' >>> print(s[1]) # str は(シングルバイトの)文字列であれば「文字」ごとに扱る b
Python 3 (UbuntuとWindows 10で同一結果)
>>> s="文字列"
>>> print(s)
文字列
>>> print(type(s))
<class 'str'>
>>> print(s.decode('utf-8')) # str から str には変換できない
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'decode'
>>> print(s.encode('utf-8')) # str から bytes に変換する
b'\xe6\x96\x87\xe5\xad\x97\xe5\x88\x97'
>>> print(type(s.encode('utf-8')))
<class 'bytes'>
>>> print(s.encode('utf-8').decode('utf-8')) # str を bytes に(encode関数)、さらに bytes から str に(encode関数)変換する
文字列
>>> print(type(s.encode('utf-8').decode('utf-8')))
<class 'str'>
>>> print(s[1]) # Python 3 では、 str は(マルチバイトの)文字列を「文字」ごとに扱える
字
unicode文字列のエンコードとデコード
Python 2 (Ubuntu)
>>> su=u"文字列"
>>> print(su)
文字列
>>> print(type(su))
<type 'unicode'>
>>> print(su.decode('utf-8')) # unicode から unicode には変換できない
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python2.7/encodings/utf_8.py", line 16, in decode
return codecs.utf_8_decode(input, errors, True)
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-2: ordinal not in range(128)
>>> print(su.encode('utf-8')) # unicode から str には変換する
文字列
>>> print(type(su.encode('utf-8')))
<type 'str'>
>>> print(su.encode('utf-8').decode('utf-8')) # unicode を str に(encode関数)、さらに str から unicode に(decode関数)変換する
文字列
>>> print(type(su.encode('utf-8').decode('utf-8')))
<type 'unicode'>
Python 3 (Ubuntu)
>>> su=u"文字列"
>>> print(su)
文字列
>>> print(type(su))
<class 'str'>
>>> print(su.decode('utf-8'))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'decode'
>>> print(su.encode('utf-8')) # str から bytes に変換する
b'\xe6\x96\x87\xe5\xad\x97\xe5\x88\x97'
>>> print(type(su.encode('utf-8')))
<class 'bytes'>
>>> print(su.encode('utf-8').decode('utf-8')) # str を bytes に(encode関数)、さらに bytes から str に(encode関数)変換する
文字列
>>> print(type(su.encode('utf-8').decode('utf-8')))
<class 'str'>
テキストファイルの読み込み
Python 2 (Ubuntu)
>>> f=io.open("test.txt", "r", encoding="utf-8") # utf-8 のテキストファイルを開く
>>> print(type(f))
<type '_io.TextIOWrapper'>
>>> s=f.readline()
>>> print(type(s)) # ファイルから読み込んだ文字列は unicode 形式
<type 'unicode'>
>>> print(s)
テスト文字列1行目
>>> print(type(s.encode('utf-8'))) # unicode を str に変換する
<type 'str'>
>>> print(s.encode('utf-8'))
テスト文字列1行目
>>> f.closed # ファイルが閉じられているかチェック
False
>>> f.close()
>>> f.closed
True
Python 3 (Ubuntu)
>>> f=io.open("test.txt", "r", encoding="utf-8") # utf-8 のテキストファイルを開く
>>> print(type(f))
<class '_io.TextIOWrapper'>
>>> s=f.readline()
>>> print(type(s)) # ファイルから読み込んだ文字列は str 形式
<class 'str'>
>>> print(s)
テスト文字列1行目
>>> f.closed # ファイルが閉じられているかチェック
False
>>> f.close()
>>> f.closed
True
未知のエンコードのテキストファイルの場合、エンコードを調べてから読み込む
Python 2 および 3 (Ubuntu)
>>> import chardet
>>> f=open('test.txt', 'rb')
>>> str_encode=chardet.detect(f.read())['encoding']
>>> f.close()
>>> print(str_encode) # chardetで検出されたエンコードはShift JIS
SHIFT_JIS
# 短いテキストファイルの場合は判定に失敗し、SHIFT_JISではなくWindows-1252 と判定されることもある
>>> f=io.open('test.txt', 'r', encoding=str_encode) # chardetで検出されたエンコードを指定して、テキストファイルを開く
>>> s=f.readline()
# 以下の処理を省略
テキストファイルの書き込み
Python 2 (Ubuntu)
>>> s="ファイルに書き込む文字列"
>>> print(type(s))
<type 'str'>
>>> f=io.open('test_output.txt', 'w', encoding='shift-jis') # shift-jis エンコードで書き込む
>>> print(type(f))
<type '_io.TextIOWrapper'>
>>> f.write(s) # write関数にstrを渡すと、エラーとなる
Traceback (most recent call last):
File "", line 1, in
TypeError: write() argument 1 must be unicode, not str
>>> f.write(s.decode('utf-8')) # write関数にはstr から unicode に変換してから渡す
12L # マルチバイト文字も、ちゃんと書き込んだ文字数が数えられて表示される
>>> f.close()
Python 3 (Ubuntu)
>>> s="ファイルに書き込む文字列"
>>> f=io.open('test_output.txt', 'w', encoding='shift-jis') # shift-jis エンコードで書き込む
>>> f.write(s) # Python 3 の場合、write関数にはstr渡してよい
12
>>> f.close()
テキストファイルをバイナリファイルとして読み込んだ場合
Python 2 (Ubuntu)
>>> f=io.open("test.txt", "rb")
>>> print(type(f))
<type '_io.BufferedReader'>
>>> s=f.readline()
>>> print(type(s)) # Python 2 の場合、strとバイナリデータは透過
<type 'str'>
>>> print(s)
テスト文字列1行目
>>> f.close()
Python 3 (Ubuntu)
>>> f=io.open("test.txt", "rb")
>>> print(type(f))
<class '_io.BufferedReader'>
>>> s=f.readline()
>>> print(type(s)) # Python 3 の場合、バイナリデータはbytesとなる
<class 'bytes'>
>>> print(s)
b'\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88\xe6\x96\x87\xe5\xad\x97\xe5\x88\x971\xe8\xa1\x8c\xe7\x9b\xae\n'
>>> print(type(s.decode('utf-8'))) # bytes から str に(encode関数)変換する
<class 'str'>
>>> print(s.decode('utf-8'))
テスト文字列1行目
>>> f.close()