Python 2.xのStringとUnicode


Python 2.xには2つの文字列相関タイプがあり、それぞれStringとUnicodeであり、両者が提供するインタフェースは非常に類似しており、時には自動変換することができ、人を誤導しやすい.Python 3では、この2つのタイプがそれぞれBytesとStringで置き換えられています.この名前は両者の本質を説明することができる:Python 2.xのStringには符号化情報のないバイトシーケンス、すなわちStringには符号化されたシーケンスが格納されているが、自身がどの符号化を用いているのかは分からない.反対のUnicodeには、符号化が記載された文字列情報が格納されており、そこには対応する文字のUnicode番号が格納されている.ここでプログラムで説明すると、encoding.pyという簡単なスクリプト名を作成します.コードは以下の通りです.
#!/usr/bin/python
# -*- coding: utf-8 -*-

strs = "    "
unis = "     ".decode("utf8")

print strs[0:2]
print unis[0:2].encode('gbk')

print len(strs)
print len(unis)

前の2行は、実行環境とスクリプトファイルを限定する符号化フォーマットについて説明します.このスクリプトはここでダウンロードできます.自分で書く場合は、スクリプトの符号化がutf 8であることを確認してください.Windowsでの実行結果はここで、ちょうど問題を説明できると思います.
C:\SHARED\Dev\scripts>encoding.py
 
  
12
5

ここで説明する必要がありますが、私たちのプログラムはUTF 8符号化であり、主な意味は、このプログラムのすべての直接書かれた文字列(「」で囲まれた文字列)がUTF 8フォーマットで符号化されていることです.ただし、WindowsのコマンドラインはGBK環境です.ここでstrsStringです.実はPython 2.xに直接プログラムに書かれた文字列は、いずれもStringのタイプである(ここではstring literalは考慮しない).まずstrs[0:2]を直接出力し、乱符号文字を得た(この文字はたまたま1文字になっただけだ).前述したように、Stringには符号化情報のない文字列シーケンスが格納されており、ここではstrsの最初の2つの番号を取り出して表示しようとする.コマンドライン環境がGBK符号化であるため,ここでは対応する字がたまたま1文字にまとまっているが,元の字とは何の関係もない.unisは、Python 2において、Stringが呼び出すdecode()の方法によって得る.xでUnicodeを取得する最も基本的な方法.Stringは、それ自体がどのような符号化フォーマットによって符号化されているのか分からないため、ここでは、彼がどの符号化方式で符号化されていたのかを決定する責任があります.コード内の符号化フォーマットがUTF 8であることを知っているので、Stringを呼び出すdecode()の方法で逆符号化、すなわち復号を行い、ある符号化されたフォーマットから一意に対応するUnicode番号に文字列を変換することができる.unisは、Python 2における復号化の結果である.xの対応タイプはUnicodeで、各文字に対応するUnicode番号が格納されています.unisの最初の2文字を出力しようとしたが,ここではUnicodeencode()メソッドを呼び出した.これが符号化のプロセスです.Windowsコマンドラインの符号化はGBKであり、GBK符号化された文字のみが正しく表示されることを知っています.そこでここではUnicodeencode()メソッドを呼び出し、unisに格納されているUnicode番号をGBKのルールに従って符号化し、画面に出力する.ここでは、unisの最初の2文字が正しく表示されていることを示します.コマンドラインで直接print UnicodeであればPythonは自動的に現在の環境に従って符号化されて表示されますが、両者の違いは隠されています.encodeメソッドとdecodeメソッドを常に手動で呼び出すことをお勧めします.そうすれば、自分でもよくわかります.
後の両者の長さの違いも私たちの前の例を裏付けている.strsにはUTF 8符号化後の番号付けシーケンスが格納されており、UTF 8符号化後に1つの中国語文字が3つ連続して表示されるため、strsの長さは3 x 4=12である.strsに格納されているのは中国語ではなく、意味のないビットシーケンスであることを想像することができます.unisには対応する中国語のUnicode符号化が格納されている.各文字が1つの番号に対応していることを知っているので、5つの字は5つの番号に対応し、長さは5です.
符号化による問題を回避し、解決する.
Python Unicode符号化復号化のこれらの概念を理解した後、煩わしい符号化問題をできるだけ避ける方法を見てみましょう.
まず、コードに中国語がある場合は、必ずコードの符号化フォーマットを宣言しなければなりません.PEP-0263の説明によれば、プログラムの最初に以下の2行の注釈を加えることで符号化が決定される.
#!/usr/bin/python
# -*- coding: utf-8 -*-

このうちutf-8は、指定された符号化フォーマットである.実際には、将来のPython 3のすべてのファイルがデフォルトでUTF 8で符号化されるため、常にUTF 8をPythonプログラムの符号化フォーマットとして使用する必要があります.また、Pythonプログラムを編集するエディタが本当にUTF 8符号化でファイルを格納しているかどうかを確認する必要があります.
その後、符号化復号に関する良い習慣を身につける.プログラムにStringが入力されている場合は、できるだけ早くUnicodeに変換し、プログラムで処理する必要があります.再出力する場合も、できるだけ遊んで、最終出力の時点までUnicodeを所望の符号化フォーマットのStringに符号化して出力します.同じように、プログラム内のすべての演算に参加する文字列がUnicode形式であることを維持する必要があります.多くの有名なPythonライブラリ、例えばdjangoはこのような方法を採用しており、効果も優れています.Python自身に依存して両者の間の変換を行わないでください.StringUnicodeを一緒に演算しないでください.これらの行為は誤りを引き起こしやすい一方で、Python 3では再現できません.Stringの符号化フォーマットがプログラマーの責任であることを確認していますが、文字列が何で符号化されているのか本当に分からないことがあります.ここには不思議なchardetがあります.以下に、ページから抜粋した例を示します.任意の文字列を読み込み、符号化フォーマットを推測し、推測の確信度を与える役割を説明します.
>>> import urllib
>>> urlread = lambda url: urllib.urlopen(url).read()
>>> import chardet
>>> chardet.detect(urlread("http://google.cn/"))
{'encoding': 'GB2312', 'confidence': 0.99}

>>> chardet.detect(urlread("http://yahoo.co.jp/"))
{'encoding': 'EUC-JP', 'confidence': 0.99}

>>> chardet.detect(urlread("http://amazon.co.jp/"))
{'encoding': 'SHIFT_JIS', 'confidence': 1}

>>> chardet.detect(urlread("http://pravda.ru/"))
{'encoding': 'windows-1251', 'confidence': 0.9355}

confidenceが非常に低い場合やchardetが直接エラーを報告する場合、文字列が複数回のエラー符号化復号を経て、他の場所から問題を解決する方法を探すことが多い.
漢字を含むテキストファイルを扱う場合、よくある問題はUTF BOMのあるファイルに遭遇することです.これは簡単に言えば、ファイルの最初の数バイトがファイルが大端なのか小端なのかを表すために使用されます.実際に使うことが少なく、頭の痛い問題をもたらします.UTF 8でエンコードされたファイルがあると確信している場合がありますが、最初の数バイトを読むとエラーが発生します.十中八九はこの問題です.Pythonはファイルを読み込むときもすべてのバイト順に読み込まれ、これを透明に処理することはできません.エディタでファイルをBOMなしに保存するか、Pythonで処理することができます.標準ライブラリにはcodecの機能があります.
import codecs
s = f.read(3)
if s == codecs.BOM_UTF8:
    print "BOM detected"

これでBOMが存在するかどうかを簡単に検出でき、残りの部分は自分で発揮しなければなりません.