ホームページ  >  記事  >  バックエンド開発  >  Python エンコードと Unicode

Python エンコードと Unicode

巴扎黑
巴扎黑オリジナル
2017-04-05 16:05:451148ブラウズ

Unicode と Python についてはたくさんの説明があると思いますが、私自身の理解と使用を容易にするために、それらについてさらにいくつか書いていくつもりです。

バイトストリームとUnicodeオブジェクト

まずはPythonで文字列を定義しましょう。文字列型を使用すると、実際にはバイト文字列が保存されます。

りー

この例では、文字列 abc はバイト文字列です。 97.、98、99 は ASCII コードです。 Python 2.x の定義では、すべての文字列を ASCII として扱います。残念ながら、ASCII はラテン文字セットの中で最も一般的ではない標準です。

ASCII は、文字マッピングに最初の 127 の数字を使用します。 Windows-1252 や UTF-8 などの文字マップは、最初の 127 文字が同じです。文字列内の各バイトの値が 127 未満の場合は、文字列エンコーディングを混合しても安全です。ただし、以下で説明するように、この仮定を行うことには危険があります。

文字列内に 126 を超える値のバイトがある場合、問題が発生します。 Windows-1252 でエンコードされた文字列を見てみましょう。 Windows-1252 の文字マッピングは 8 ビット文字マッピングであるため、合計 256 文字になります。最初の 127 文字は ASCII と同じで、次の 127 文字は Windows-1252 で定義された他の文字です。

りー

Windows-1252 はまだバイト文字列ですが、最後のバイトの値が 126 より大きいことがわかりましたか? Python がデフォルトの ASCII 標準を使用してこのバイト ストリームをデコードしようとすると、エラーが報告されます。 Python がこの文字列をデコードすると何が起こるか見てみましょう:

[  a ][  b ][  c ] = "abc"
[ 97 ][ 98 ][ 99 ] = "abc"

UTF-8 を使用して別の文字列をエンコードしてみましょう:

A windows-1252 encoded string looks like this:
[ 97 ] [ 98 ] [ 99 ] [ 150 ] = "abc–"

よく知っている Unicode エンコーディング テーブルを選択すると、英語のダッシュに対応する Unicode コード ポイントが 8211 (0x2013) であることがわかります。この値は、ASCII の最大値 127 より大きくなります。 1バイト以上の値を格納できます。 8211 (0x2013) は 2 バイトであるため、UTF-8 はいくつかのトリックを使用して、1 文字を保存するのに 3 バイト必要であることをシステムに伝える必要があります。 Python が、文字値が 126 より大きい UTF-8 でエンコードされた文字列をエンコードするためにデフォルトの ASCII を使用する準備をいつ行うかを見てみましょう。

りー

ご覧のとおり、Python はデフォルトで常に ASCII エンコーディングを使用してきました。 4 番目の文字を処理すると、その値が 226 で 126 より大きいため、Python はエラーをスローします。これは混合エンコーディングの問題です。

バイトストリームをデコードします

初めて Python Unicode を学習する場合、デコードという用語が混乱する可能性があります。バイト ストリームを Unicode オブジェクトにデコードしたり、Unicode オブジェクトをバイト ストリームにエンコードしたりできます。

Python は、バイト ストリームを Unicode オブジェクトにデコードする方法を知る必要があります。バイト ストリームを取得したら、その「decode」メソッドを呼び出して、そこから Unicode オブジェクトを作成します。 できるだけ早くバイト ストリームを Unicode にデコードすることをお勧めします。

りー

Unicode をバイトストリームにエンコードします

Unicode オブジェクトは、エンコーディングに依存しないテキスト表現です。 Unicode オブジェクトを単純に出力することはできません。出力する前にバイト文字列に変換する必要があります。 Python はこの種の作業に適していますが、Unicode をバイト ストリームにエンコードする場合、Python はデフォルトで ASCII を使用しますが、このデフォルトの動作は多くの問題を引き起こす可能性があります。

りー

コーデックモジュールを使用する

コーデック モジュールは、バイト ストリームを処理するときに非常に役立ちます。定義されたエンコーディングでファイルを開くことができ、ファイルから読み取ったコンテンツは自動的に Unicode オブジェクトに変換されます。

これを試してください:

>>> x = "abc" + chr(150)
>>> print repr(x)
'abc\x96'
>>> u"Hello" + x
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
UnicodeDecodeError: &#39;ASCII&#39; codec can&#39;t decode byte 0x96 in position 3: ordinal not in range(128)

これは Unicode オブジェクトを取得し、それを UTF-8 エンコーディングでファイルに書き込みます。他の状況でも使用できます。

これを試してください:

ファイルからデータを読み取るとき、codecs.open は、UTF-8 でエンコードされたファイルを Unicode オブジェクトに自動的に変換できるファイル オブジェクトを作成します。

上の例を続けてみましょう。今回は urllib ストリームを使用します。

りー

単行バージョン:

A UTF-8 encoded string looks like this:
[ 97 ] [ 98 ] [ 99 ] [ 226 ] [ 128 ] [ 147 ] = "abc–"
[0x61] [0x62] [0x63] [0xe2]  [ 0x80] [ 0x93] = "abc-"

コーデック モジュールには細心の注意が必要です。渡すものは Unicode オブジェクトである必要があります。そうでない場合は、バイト ストリームが ASCII として自動的にデコードされます。

りー

ああ、Python は再び ASCII を使用してすべてをデコードし始めました。

UTF-8バイトストリームのスライスの問題

UTF-8 でエンコードされた文字列はバイトのリストであるため、len() およびスライス操作は正しく機能しません。まず、前に使用した文字列を使用します。

りー

次に、次の操作を行います:

>>> x = "abc\xe2\x80\x93"
>>> print repr(x)
&#39;abc\xe2\x80\x93&#39;
>>> u"Hello" + x
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
UnicodeDecodeError: &#39;ASCII&#39; codec can&#39;t decode byte 0xe2 in position 3: ordinal not in range(128)

何? 4 文字のように見えますが、len の結果では 6 文字になっています。 len は文字数ではなくバイト数をカウントするためです。

りー

では、この文字列を分割してみましょう。

りー

セグメンテーションの結果は最後のバイトであり、最後の文字ではありません。

UTF-8 を正しくセグメント化するには、バイト ストリームをデコードして Unicode オブジェクトを作成することをお勧めします。そうすれば、安全に操作してカウントできます。

りー

Python が自動的にエンコード/デコードする場合

場合によっては、Python が ASCII を使用して自動的にエンコード/デコードするときにエラーがスローされます。

  第一个案例是当它试着将Unicode和字节串合并在一起的时候。

>>> u"" + u"\u2019".encode("utf-8")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: &#39;ascii&#39; codec can&#39;t decode byte 0xe2 in position 0:   ordinal not in range(128)

  在合并列表的时候会发生同样的情况。Python在列表里有string和Unicode对象的时候会自动地将字节串解码为Unicode。

>>> ",".join([u"This string\u2019s unicode", u"This string\u2019s utf-8".encode("utf-8")])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: &#39;ascii&#39; codec can&#39;t decode byte 0xe2 in position 11:  ordinal not in range(128)

  或者当试着格式化一个字节串的时候:

>>> "%s\n%s" % (u"This string\u2019s unicode", u"This string\u2019s  utf-8".encode("utf-8"),)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: &#39;ascii&#39; codec can&#39;t decode byte 0xe2 in position 11: ordinal not in range(128)

  基本上当你把Unicode和字节串混在一起用的时候,就会导致出错。

  在这个例子里面,你创建一个utf-8文件,然后往里面添加一些Unicode对象的文本。就会报UnicodeDecodeError错误。

>>> buffer = []
>>> fh = open("utf-8-sample.txt")
>>> buffer.append(fh.read())
>>> fh.close()
>>> buffer.append(u"This string\u2019s unicode")
>>> print repr(buffer)
[&#39;This file\xe2\x80\x99s got utf-8 in it\n&#39;, u&#39;This string\u2019s unicode&#39;]
>>> print "\n".join(buffer)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: &#39;ascii&#39; codec can&#39;t decode byte 0xe2 in position 9: ordinal not in range(128)

  你可以使用codecs模块把文件作为Unicode加载来解决这个问题。

>>> import codecs
>>> buffer = []
>>> fh = open("utf-8-sample.txt", "r", "utf-8")
>>> buffer.append(fh.read())
>>> fh.close()
>>> print repr(buffer)
[u&#39;This file\u2019s got utf-8 in it\n&#39;, u&#39;This string\u2019s unicode&#39;]
>>> buffer.append(u"This string\u2019s unicode")
>>> print "\n".join(buffer)
This file’s got utf-8 in it

This string’s unicode

  正如你看到的,由codecs.open 创建的流在当数据被读取的时候自动地将比特串转化为Unicode。

  最佳实践

  1.最先解码,最后编码

  2.默认使用utf-8编码

  3.使用codecs和Unicode对象来简化处理

  最先解码意味着无论何时有字节流输入,需要尽早将输入解码为Unicode。这会防止出现len( )和切分utf-8字节流发生问题。

  最后编码意味着只有在准备输入的时候才进行编码。这个输出可能是一个文件,一个数据库,一个socket等等。只有在处理完成之后才编码unicode对象。最后编码也意味着,不要让Python为你编码Unicode对象。Python将会使用ASCII编码,你的程序会崩溃。

  默认使用UTF-8编码意味着:因为UTF-8可以处理任何Unicode字符,所以你最好用它来替代windows-1252和ASCII。

  codecs模块能够让我们在处理诸如文件或socket这样的流的时候能少踩一些坑。如果没有codecs提供的这个工具,你就必须将文件内容读取为字节流,然后将这个字节流解码为Unicode对象。

  codecs模块能够让你快速的将字节流转化为Unicode对象,省去很多麻烦。

  解释UTF-8

  最后的部分是让你能入门UTF-8,如果你是个超级极客可以无视这一段。

  利用UTF-8,任何在127和255之间的字节是特别的。这些字节告诉系统这些字节是多字节序列的一部分。

Our UTF-8 encoded string looks like this:
[ 97 ] [ 98 ] [ 99 ] [ 226 ] [ 128 ] [ 147 ] = "abc–"

  最后3字节是一个UTF-8多字节序列。如果你把这三个字节里的第一个转化为2进制可以看到以下的结果:

11100010

  前3比特告诉系统它开始了一个3字节序列226,128,147。

  那么完整的字节序列。

11100010 10000000 10010011

  然后你运用三字节序列的下面的掩码。

1110xxxx 10xxxxxx 10xxxxxx
XXXX0010 XX000000 XX010011 Remove the X&#39;s
0010       000000   010011 Collapse the numbers
00100000 00010011          Get Unicode number 0x2013, 8211 The "–"

  这是基本的UTF-8入门,如果想知道更多的细节,可以去看UTF-8的维基页面。

以上がPython エンコードと Unicodeの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。