web-dev-qa-db-ja.com

Pythonでサロゲートペアを使用するには

これは 絵文字への変換 のフォローアップです。その質問では、OPにサモゲートペア-_\ud83d\ude4f_として表される絵文字を含むjson.dumps()- encodedファイルがありました。ファイルを読み取ったり、絵文字を正しく翻訳したりするときに問題が発生しました。正しい answer は、ファイルの各行をjson.loads()にし、jsonモジュールを使用することでした。サロゲートペアから(UTF8でエンコードされている)絵文字への変換を処理します。

だからここに私の状況があります:サロゲートペアを含む通常のPython 3 unicode文字列だけがあるとしましょう:

_emoji = "This is \ud83d\ude4f, an emoji."
_

この文字列を処理して emoji の表現を取得するにはどうすればよいですか?私はこのようなものを手に入れようとしています:

_"This is ????, an emoji."
# or
"This is \U0001f64f, an emoji."
_

私はもう試した:

_print(emoji)
print(emoji.encode("utf-8")) # also tried "ascii", "utf-16", and "utf-16-le"
json.loads(emoji) # and `.encode()` with various codecs
_

一般に、_UnicodeEncodeError: XXX codec can't encode character '\ud83d' in position 8: surrogates no allowed_のようなエラーが発生します。

LinuxでPython 3.5.1、_$LANG_を_en_US.UTF-8_に設定して実行しています。これらのサンプルは両方ともPythonコマンドラインのインタープリター、およびSublime Textで実行されているIPython内-違いはないようです。

27
MattDMo

ディスク上のjsonファイルでリテラル文字列_\ud83d_(6文字:_\ u d 8 3 d_)とsingle文字_u'\ud83d'_(メモリ内のPythonソースコード)の文字列リテラルを使用して指定されます。 Python 3のlen(r'\ud83d') == 6len('\ud83d') == 1の違いです。

_'\ud83d\ude4f'_ Python文字列(2文字)が表示される場合は、上流にバグがあります。通常、このような文字列は取得しないでください。あなたがそれを手に入れ、それを生成する上流を修正できない場合; surrogatepassエラーハンドラを使用して修正できます:

_>>> "\ud83d\ude4f".encode('utf-16', 'surrogatepass').decode('utf-16')
'????'
_

Python 2の方が許容度が高かった

注:jsonファイルにリテラル\ ud83d\ude4f(12文字)が含まれている場合でも、サロゲートペアを取得しないでください。

_>>> print(ascii(json.loads(r'"\ud83d\ude4f"')))
'\U0001f64f'
_

注意:結果は1文字(_'\U0001f64f'_)であり、サロゲートペア(_'\ud83d\ude4f'_)ではありません。

30
jfs

これは繰り返し発生する質問であり、エラーメッセージはややあいまいであるため、ここではより詳細な説明を示します。

サロゲートは、U + FFFFより大きいUnicodeコードポイントを表現する方法です。

Unicodeはもともと65,536文字を含むように指定されていたことを思い出してください。しかし、これは世界中のすべてのグリフに対応するには十分ではないことがすぐにわかりました。

(そうでなければ固定幅) TF-16 エンコーディングの拡張メカニズムとして、予約領域は Basic Multilingual Plane 外のコードポイントを表現するためのメカニズムを含むように設定されました:この特別な領域のコードポイントの後には、同じ領域からの別の文字コードが続く必要があり、一緒に、古い制限よりも大きい数のコードポイントを表します。

(厳密に言えば、サロゲート領域は2つの半分に分かれています。ペアの最初のサロゲートは高サロゲートの半分から、2番目のサロゲートは低サロゲートから来る必要があります。)

これは、特にUTF-16エンコーディングをサポートするレガシーメカニズムであり、他のエンコーディングでは使用しないでください。彼らはそれを必要とせず、適用可能な規格はこれが禁止されていると明確に述べています。

つまり、 + 12345 はサロゲートペアU + D808 U + DF45で表現できますが、代わりに直接表現する必要があります。

より詳細には、これがUTF-8で単一の文字としてどのように表現されるかを示します。

0xF0 0x92 0x8D 0x85

そして、これは対応するサロゲートシーケンスです:

0xED 0xA0 0x88
0xED 0xBD 0x85

受け入れられた回答ですでに示唆されているように、あなたは次のようなものと往復することができます

>>> "\ud808\udf45".encode('utf-16', 'surrogatepass').decode('utf-16').encode('utf-8')
b'\xf0\x92\x8d\x85'

たぶん http://www.russellcottrell.com/greek/utilities/surrogatepaircalculator.htm も参照してください

6
tripleee