Python文字符号化の理解

6714 ワード

普通のプログラマーから優秀なプログラマーに進む途中、文字符号化は越えざるを得ないハードルであり、私たちのほとんどのプログラムは文字処理に関連しており、このハードルに越えられなければ、ほとんどいくつかの穴に直面する運命にある.本稿では,実際の例を用いて文字符号化復号化の過程を説明し,プログラムが文字をどのように処理しているのかをより明確に認識できるようにしようとする.本文に入る前に、まず文字セットと文字符号化の違いを理解する必要があります.Unicodeとは何か、UTF-8とは何か、GBKなどの基本概念を知る必要があります.もしあなたが理解していない場合は、次のいくつかの文章に進んでください.文字符号化の詳細な文字符号化ノートの後、プログラムが文字を処理する過程はどうなっているか考えてみましょう.最初は必ずエディタを開き、プログラムを書き出してソースファイル(Pythonでは.pyファイル)に保存すると思いますので、まずファイルの保存から始めましょう.
ソースファイルの保存
エディタに書かれたコードはすべて文字形式で存在しますが、これらの文字をハードディスクに格納するには、コンピュータが0/1シーケンスしか認識できないため、これらの文字はいくつかの符号化規則を通じてバイナリシーケンス列に変換し、ハードディスクに格納する必要があります.例えば次のプログラムを書きました
s = '  '
print repr(s), s

このファイルを格納する場合、GB 2312符号化で格納する場合、ファイルのバイナリ表現は、
➜  testProgram hexdump -C gb2312encodingfile.py
00000000  73 20 3d 20 27 c4 e3 ba  c3 27 0a 70 72 69 6e 74  |s = '....'.print|
00000010  20 72 65 70 72 28 73 29  2c 20 73 0a              | repr(s), s.|
0000001c

ここで、73はs 20は 3 dは= 27は' c 4 e 3は ba c 3は を表している.utf-8を使用して同じコードを格納し、そのバイナリ表現がどのようになっているかを見てみましょう.
➜  testProgram hexdump -C utf8encodingfile.py
00000000  73 20 3d 20 27 e4 bd a0  e5 a5 bd 27 0a 70 72 69  |s = '......'.pri|
00000010  6e 74 20 72 65 70 72 28  73 29 2c 20 73 0a        |nt repr(s), s.|
0000001e

同様に、ここで73はs 20は 3 dは= 27は'を表すが、 は3バイトで表されるe 4 bd a 0は e 5 a 5 bdは を表す
ソースファイルはすでにバイナリコードストリームでハードディスクに格納されていますが、ソースコードはどのように実行されますか?
ソース実行
ソースコードが実行されると、Python解釈器はまずソースファイルloadをメモリに入れ、1行でファイルの読み取りを開始し、実行を解釈します.
ただし、str文字列である場合、python解釈器はそのバイナリコードストリームのみを読み出すので、gb 2312 encodingfileを使用すると仮定する.pyでは、sが指す文字列 がメモリに読み込まれた表現がc4 e3 ba c3であり、print印刷を使用する場合、Windowsのconsole上で実行されると、正しく実行でき、以下のように表示される.
➜  testProgram python gb2312encodingfile.py
'\xc4\xe3\xba\xc3'   

ただしLinuxまたはmacでは正しく実行できず、以下のように表示されます.
➜  testProgram python gb2312encodingfile.py
'\xc4\xe3\xba\xc3' ���

これは、Windows consoleのデフォルトはGBKコーデック(GB 2312の拡張)であるため、\xc4\xe3\xba\xc3を漢字 として正しく復号表示することができるが、LinuxまたはMacではconsoleのデフォルトコーデック方式はUTF-8であるため、\xc4\xe3\xba\xc3を正しく表示することはできない.
もう1つのエピソードは、コードに漢字がある場合は、ファイルの先頭に符号化方式(#-*-coding:utf-8-または#coding=utf 8)を宣言する必要があります.そうしないと、解釈器はデフォルトでASCII符号化方式でソースファイルを開くので、次のようにエラーを報告します.
➜  testProgram python gb2312encodingfile.py
  File "gb2312encodingfile.py", line 1
SyntaxError: Non-UTF-8 code starting with '\xc4' in file gb2312encodingfile.py on line 1, but no encoding declared; see http://python.org/dev/peps/pep-0263/ for details

しかし、私たちの文字列がunicodeオブジェクトの文字列である場合、Python解釈器は文字列のバイトシーケンスを先に復号し、復号後のバイトシーケンスの参照をsに割り当て、utf 8 encodingfileを変更することができます.pyコードは次のとおりです.
#-*- coding: utf-8 -*-
s = '  '
print repr(s), s

u = u'  '
print repr(u), u

保存後、hexdumpを使用してバイナリ符号化を表示します.
➜  testProgram hexdump -C utf8encodingfile.py
00000000  23 2d 2a 2d 20 63 6f 64  69 6e 67 3a 20 75 74 66  |#-*- coding: utf|
00000010  2d 38 20 2d 2a 2d 0a 73  20 3d 20 27 e4 bd a0 e5  |-8 -*-.s = '....|
00000020  a5 bd 27 0a 70 72 69 6e  74 20 72 65 70 72 28 73  |..'.print repr(s|
00000030  29 2c 20 73 0a 0a 75 20  3d 20 75 27 e4 bd a0 e5  |), s..u = u'....|
00000040  a5 bd 27 0a 70 72 69 6e  74 20 72 65 70 72 28 75  |..'.print repr(u|
00000050  29 2c 20 75 0a                                    |), u.|
00000055

よく見ると、2つの 文字列がe4 bd a0 e5 a5 bdに符号化されmac上で実行され、結果は以下の通りである.
➜  testProgram python utf8encodingfile.py
'\xe4\xbd\xa0\xe5\xa5\xbd'   
u'\u4f60\u597d'   

sが指すバイトシーケンスは\xe4\xbd\xa0\xe5\xa5\xbdであり、uが指すバイトシーケンスは\u4f60\u597dであることがわかる(すなわち、e 4 bd a 0 e 5 a 5 bdをu 4 f 60u 597 dに復号化する)
しかし、gb 2312 encodingfileを変更するとします.py、gb 2312符号化で保存し、このプログラムを実行して結果を見てみましょう.結果の直接エラー:
➜  testProgram python gb2312encodingfile.py
  File "gb2312encodingfile.py", line 5
    u = u'���'
SyntaxError: (unicode error) 'utf8' codec can't decode byte 0xc4 in position 0: invalid continuation byte

これは、Python解釈器が、gb 2312によって符号化されたバイトシーケンスを宣言されたutf−8符号化方式で復号しようとしたため、このようなエラーが発生したためである.
これでPythonがソースファイルをどのように読み書きするかが分かりましたが、Pythonが実行するときに外部ファイルをどのように読み書きするのでしょうか.
ファイルの読み書き
次に、次のコードを使用して文字列をファイルに書き込んでみます.ソースコードの保存にはutf-8を使用します.ファイル名はutf 8 encodingfile_です.write.py
#-*- coding: utf-8 -*-
s = '  '
with open('stroutput.txt', 'w') as f:
    f.write(s)

u = u'  '
with open('unicodeoutput.txt', 'w') as f:
    f.write(u)

macで実行します.結果は次のとおりです.
➜  testProgram python utf8encodingfile_write.py
Traceback (most recent call last):
  File "utf8encodingfile_write.py", line 8, in 
    f.write(u)
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)

'ascii'codec can't encode characters in position 0-1:ordinal not in range(128)説明システムはファイルを書くときにASCIIを使って符号化しようとしていますが、utf-8を使うと宣言しているのに.
元のファイルヘッダ宣言はutf-8を使用していたが、解釈器がソースファイルを解釈する際に使用され、writeを呼び出してファイルを書くと、システムのデフォルトの符号化設定が呼び出されて符号化される.システムのデフォルトの符号化を見てみましょう.
>>> import sys
>>> sys.getdefaultencoding()
'ascii'

やはり、システムのデフォルトはascii符号化方式です.この問題を解決するには2つの方法があり、1つはシステムのデフォルトの符号化方式を修正することであり、もう1つはopenのときに符号化方式を指定することであり、そのうち2つ目は明らかにより優雅である.
#                utf-8  
import sys
reload(sys) #     reload      setdefaultencoding method
sys.setdefaultencoding('utf-8')


#    codecs.open       
import codecs

with codecs.open("filename", "w", encoding="utf-8") as f:
    f.write(u)

同様に、ファイルを読み込むときにcodecsを通過することもできます.Openは符号化方式を設定するが、まず、この読み込むファイルの符号化方式を知る必要があり、ファイルがutf-8で符号化されていると仮定すると、読み取るときは以下のようにすることができる.
import codecs

with open("somefile", "r", encoding="utf-8") as f:
    content = f.read()
    

また、ファイルから読み取るのではなく、標準以外の文字を直接使用する場合があります.これはdecodeを使用して先に復号する必要があります.
# if not decode, will raise exception: 'ascii' codec can't
# decode byte 0xe2 in position 0: ordinal not in range(128)
dash = '–'.decode("utf8")
if dash in title:
    title = title.split(dash)[0]

これでPythonコードについては終わりますが、収穫があれば、「いいね」を押してください.