Python文字コードの詳細

8328 ワード

Pythonの文字コードの問題を説明する良い文章を転載します。
回転:http://www.cnblogs.com/huxi/archive/2010/12/05/1897271.html
1.文字コードの概要
1.1.ASCII
ASCIIは、シングルバイトのコードです。コンピュータの世界では最初は英語だけでしたが、シングルバイトは256の異なる文字を表しています。すべての英字と多くの制御記号を表しています。しかし、ASCIIはその半分(\x 80以下)しか使われませんでした。これもMBCSの実現の基礎です。
1.2.MBCS
しかし、コンピュータの世界には他の言語があります。バイトのASCIIはもう需要を満たすことができません。その後、各言語は自分のコードを制定しました。単バイトで表現できる文字が少なすぎて、ASCIIコードと互換性が必要です。だから、これらのコードは多くのバイトを使って文字を表しています。例えば、GBxxx、BIGxxxなどです。彼らのルールは、最初のバイトが\x 80以下なら、ASCII文字を表しています。一方、\x 80以上であれば、次のバイトと一緒に1文字を表し、次のバイトをスキップして判定を続けます。
ここで、IBMはCode Pageという概念を発明し、これらのコードをすべて袋に入れてページ番号を割り当て、GBKは936ページ目、つまりCP 936です。したがって、CP936を使用してGBKを表すこともできる。
MBCS(Multi-Byte Charcter Set)はこれらの符号化の総称である。これまでは二バイトを使っていたので、DBCS(Duble-Byte Charcter Set)とも呼ばれることがあります。明確にしなければならないのは、MBCSは特定のコードではなく、Windowsではあなたが設定したエリアによってMBCSは異なるコードを指していますが、LinuxではMBCSはコードとして使用できません。WindowsではMBCSという文字は見られません。マイクロソフトはもっと洋風にするためにANSIを使って人を脅かしています。メモ帳の保存はダイアログでANSIをエンコードするのがMBCSです。また、簡体字中国語Windowsのデフォルトのエリア設定では、GBKを指す。
1.3.ユニックode
その後、コードが多すぎて世界が複雑になりすぎて、頭が痛くなりました。みんなで座って頭をたたいて、一つの方法を思い出しました。すべての言語の文字は同じ文字セットで表しています。これはユニックです。
最初のUnicode標準UCS-2は2バイトを使って一つの文字を表していますので、ユニックは2バイトを使って一つの文字を表すという話をよく聞きます。しかし、そのうち256*256が少なすぎて、まだ足りないという人がいます。そこでUCS-4標準が現れました。4バイトを使って一つの文字を表しますが、一番多く使うのはやはりUCS-2です。
UCSはまだ文字対応コードビットの一枚の表にすぎません。例えば「漢」という文字の符号位は6 C 49です。文字の具体的な伝達と保存はUTF(UCS Transformation Format)が担当します。
最初のことは簡単で、UCSのコードビットを直接使って保存します。これはUTF-16です。例えば、「漢」は直接\x 6 C\x 49を使って保存します。しかし、アメリカ人を使って自分が大損をしたと感じています。以前は英字は一バイトで保存できましたが、今は大鍋ご飯を食べたら二バイトになり、空間の消耗が倍になりました。
UTF-8はとてもこじれた符号で、具体的には彼が長くなることを表しています。ASCIIに対応しています。ASCII文字は1バイトの表現を使います。しかし、ここで節約したのは他のところから掘り出したものに違いないです。UTF-8で中国語の文字を3バイト使って保存するという話も聞いたことがありますよね?4バイト保存の文字は涙の中で走ります。
ちなみに、BOMです。私たちはファイルを保存する時に、ファイルのコードを保存していません。開く時は、保存時に使っていたコードを覚えて、このコードを使って開けてください。そうすると、多くのトラブルが発生します。(メモ帳がファイルを開く時に、選択コードを開けていないと言ってもいいですか?メモ帳を開けてからファイルを使ってみてください。)UTFは自分のコードをBOMに導入しています。最初に読み込むバイトがその中の一つであれば、次に読み込む文字を表すコードは該当符号です。
BOMUTF 8'\xef\xbb\xbf'  BOMUTF 16_LE'\xff\xfe'  BOMUTF 16_BE'\xfe\xff'
すべてのエディタがBOMに書き込むわけではありませんが、BOMがなくてもUnicodeは読み込むことができます。MBCSのコードのように、別に具体的なコードを指定する必要があります。そうでないと復号は失敗します。
UTF-8はBOMを必要としないと聞いたことがあるかもしれませんが、ほとんどのエディタはBOMがない時はUTF-8をデフォルトコードとして読みます。保存時にはデフォルトでANSI(MBCS)のメモ帳を使用しても、ファイルを読み込む時にはUTF-8を使って符号化をテストし、うまく復号できるならUTF-8を使って復号します。メモ帳のこのこじれたやり方はBUGをもたらしました。もしあなたがテキストファイルを新築して「色とりどり」を入力してANSI(MBCS)を使って保存したら、開けたら「漢a」になります。試してみてください。
2.Python 2.xにおける符号化問題
2.1.strとunicode
strとunicodeはすべてbasestringのサブクラスです。厳密には、strはバイト列であり、unicodeがコード化されたバイトからなるシーケンスである。UTF-8符号化のstr'漢'にlen()関数を使うと、結果は3です。実は、UTF-8符号化の'漢'='\xE 6\xB 1\x 89'です。
unicodeこそ本当の意味での文字列です。バイトストリングは正しい文字コードを使って復号して得られます。len(u'漢')=1です。
encode()とdecode()の二つのbasestringの実例的な方法を見てみてください。strとunicodeの違いを理解したら、この二つの方法は混同しなくなります。
# coding: UTF-8
 
u = u' '
print repr(u) # u'\u6c49'
s = u.encode('UTF-8')
print repr(s) # '\xe6\xb1\x89'
u2 = s.decode('UTF-8')
print repr(u2) # u'\u6c49'
 
#  unicode        
# s2 = u.decode('UTF-8')
#   , str         
# u2 = s.encode('UTF-8')
注意したいのは、strに対してencodeを呼び出す方法は間違っていますが、実際にはPythonは異常を投げずに他の同じ内容に戻る代わりに、idのstrが違っています。unicodeにdecodeを呼び出す方法もそうです。なぜencode()とdecode()を別々にunicodeとstrに置くのかよく分かりませんが、すでにそうなっていますので、間違えないように気をつけましょう。
2.2.文字コード宣言
ソースコードファイルにASCII以外の文字が使用されている場合、ファイルのヘッダに文字コードをエンコードする必要があるという声明は以下の通りです。
#-*- coding: UTF-8 -*-
実際にPythonは〹、codingと符号化文字列だけをチェックします。他の文字は全部綺麗にするためにつけます。また、Pythonには多くの文字コードがあります。また、多くの別名があります。また、UTF-8はu 8と書くことができます。http://docs.python.org/library/codecs.html#standard-encodingsを参照してください。
また、ステートメントのコードは実際にファイルを保存する際のコードと一致しなければなりません。現在のIDEはこのような状況を自動的に処理して、声明を変えて同時に声明のコードに変えて保存しますが、テキストエディタコントロールは注意が必要です。
2.3.書類の読み書き
内蔵のopen()メソッドでファイルを開く場合、read()はstrを読み、読み取り後は正しい符号化フォーマットでdecode()を行う必要があります。write()書き込みの際、パラメータがunicodeであれば、あなたが書き込みたい符号を使ってencode()を行い、他の符号化フォーマットのstrであれば、まずこのstrのコードでdecode()を行い、unicodeに変換してから書き込みの符号を使ってencode()を行う必要があります。unicodeを直接パラメータとしてwrite()に導入する場合、Pythonはソースコードファイル宣言の文字コードを使ってエンコードしてから書き込みます。
# coding: UTF-8
 
f = open('test.txt')
s = f.read()
f.close()
print type(s) # <type 'str'>
#    GBK  ,   unicode
u = s.decode('GBK')
 
f = open('test.txt', 'w')
#    UTF-8   str
s = u.encode('UTF-8')
f.write(s)
f.close()
また、モジュールcodecsは、コードを指定してファイルを開く方法を提供しています。この方法で開いたファイルの読み込みがunicodeに戻ります。書き込み時にパラメータがunicodeであれば、オプン()で指定されたコードを使って符号化して書き込みます。strであれば、まずソースコードファイルの声明による文字コードをunicodeに復号してから前述の動作を行います。内蔵のopen()に対して、この方法は符号化に問題が発生しにくいです。
# coding: GBK
 
import codecs
 
f = codecs.open('test.txt', encoding='UTF-8')
u = f.read()
f.close()
print type(u) # <type 'unicode'>
 
f = codecs.open('test.txt', 'a', encoding='UTF-8')
#   unicode
f.write(u)
 
#   str,          
# GBK   str
s = ' '
print repr(s) # '\xba\xba'
#      GBK   str   unicode    UTF-8  
f.write(s) 
f.close()
2.4.符号化に関する方法
sys/localeモジュールでは、現在の環境におけるデフォルトの符号化を取得する方法がいくつか提供されています。
# coding:gbk
 
import sys
import locale
 
def p(f):
    print '%s.%s(): %s' % (f.__module__, f.__name__, f())
 
#                 
p(sys.getdefaultencoding)
 
#       Unicode               
p(sys.getfilesystemencoding)
 
#               (  ,   )
p(locale.getdefaultlocale)
 
#              
#     this function only returns a guess
p(locale.getpreferredencoding)
 
# \xba\xba ' ' GBK  
# mbcs         ,               
print r"'\xba\xba'.decode('mbcs'):", repr('\xba\xba'.decode('mbcs'))
 
#    Windows    (       (  ,   ))
#sys.getdefaultencoding(): gbk
#sys.getfilesystemencoding(): mbcs
#locale.getdefaultlocale(): ('zh_CN', 'cp936')
#locale.getpreferredencoding(): cp936
#'\xba\xba'.decode('mbcs'): u'\u6c49'
3.いくつかの提案
3.1.文字コードステートメントを使用して、同じプロジェクトのソースコードファイルはすべて同じ文字コードステートメントを使用します。
これは必ずします。
3.2.strを捨てて、全部unicodeを使います。
引用符を押す前にまずuを押して最初にすると確かに慣れません。そしてよく忘れてから戻りますが、90%の符号化問題を減らすことができます。コードの問題が深刻でないなら、この条を参考しなくてもいいです。
3.3.内蔵のopenの代わりにcodecs.open()を使用します。
コードの問題が深刻でないなら、この条を参考しなくてもいいです。
3.4.絶対避けたい文字コード:MBCS/DBCSとUTF-16。
ここで言うMBCSはGBKではなく、Pythonで「MBCS」というコードを使用しないでください。プログラムが完全に移植されない限り、。
Pythonにおける符号化'MBCS'と'DBCS'は同義語であり、現在のWindows環境におけるMBCSのコーデックを指す。LinuxのPython実現にはこのコードがないので、Linuxに移植すると必ず異常が発生します。また、設定されているWindowsシステムのエリアが異なる限り、MBCSフィンガーのコードも異なります。それぞれ異なるエリアを設定して2.4小節のコードを実行した結果:
#  (  ,   )
#sys.getdefaultencoding(): gbk
#sys.getfilesystemencoding(): mbcs
#locale.getdefaultlocale(): ('zh_CN', 'cp936')
#locale.getpreferredencoding(): cp936
#'\xba\xba'.decode('mbcs'): u'\u6c49'
 
#  (  )
#sys.getdefaultencoding(): UTF-8
#sys.getfilesystemencoding(): mbcs
#locale.getdefaultlocale(): ('zh_CN', 'cp1252')
#locale.getpreferredencoding(): cp1252
#'\xba\xba'.decode('mbcs'): u'\xba\xba'
 
#  (  )
#sys.getdefaultencoding(): gbk
#sys.getfilesystemencoding(): mbcs
#locale.getdefaultlocale(): ('zh_CN', 'cp1252')
#locale.getpreferredencoding(): cp1252
#'\xba\xba'.decode('mbcs'): u'\xba\xba'
 
#  (  )
#sys.getdefaultencoding(): gbk
#sys.getfilesystemencoding(): mbcs
#locale.getdefaultlocale(): ('zh_CN', 'cp932')
#locale.getpreferredencoding(): cp932
#'\xba\xba'.decode('mbcs'): u'\uff7a\uff7a'
領域を変更した後、mbcs復号を使って不正な結果が得られました。だから、「GBK」を使う必要がある時は、直接に「GBK」と書くべきです。「MBCS」と書かないでください。
UTF-16と同じように、ほとんどのオペレーティングシステムの「UTF-16」は「UTF-16-LIE」の同義語ですが、「UTF-16-LIE」を直接書くのは3文字だけです。もしあるオペレーティングシステムの「UTF-16」が「UTF-16-BE」の同義語になったら、エラーの結果があります。実際、UTF-16はかなり少ないですが、使う時は注意が必要です。
--END--