web-dev-qa-db-ja.com

UnicodeのバイトPython文字列

Python 2では、Unicode文字列にはUnicodeとバイトの両方が含まれる場合があります。

a = u'\u0420\u0443\u0441\u0441\u043a\u0438\u0439 \xd0\xb5\xd0\xba'

私はこれが彼のコードでは絶対に何か書くべきではないであることを理解していますが、これは私が対処しなければならない文字列です。

上記の文字列のバイトは、ек(Unicode \u0435\u043a)のUTF-8です。

私の目的は、ユニコードのすべてを含むユニコード文字列、つまりРусский ек\u0420\u0443\u0441\u0441\u043a\u0438\u0439 \u0435\u043a)を取得することです。

UTF-8にエンコードすると

>>> a.encode('utf-8')
'\xd0\xa0\xd1\x83\xd1\x81\xd1\x81\xd0\xba\xd0\xb8\xd0\xb9 \xc3\x90\xc2\xb5\xc3\x90\xc2\xba'

次に、UTF-8からデコードすると、バイトを含む初期文字列が得られますが、これは良くありません。

>>> a.encode('utf-8').decode('utf-8')
u'\u0420\u0443\u0441\u0441\u043a\u0438\u0439 \xd0\xb5\xd0\xba'

しかし、問題を解決するためのハッキング方法を見つけました:

>>> repr(a)
"u'\\u0420\\u0443\\u0441\\u0441\\u043a\\u0438\\u0439 \\xd0\\xb5\\xd0\\xba'"
>>> eval(repr(a)[1:])
'\\u0420\\u0443\\u0441\\u0441\\u043a\\u0438\\u0439 \xd0\xb5\xd0\xba'
>>> s = eval(repr(a)[1:]).decode('utf8')
>>> s
u'\\u0420\\u0443\\u0441\\u0441\\u043a\\u0438\\u0439 \u0435\u043a'
# Almost there, the bytes are proper now but the former real-unicode characters
# are now escaped with \u's; need to un-escape them.
>>> import re
>>> re.sub(u'\\\\u([a-f\\d]+)', lambda x : unichr(int(x.group(1), 16)), s)
u'\u0420\u0443\u0441\u0441\u043a\u0438\u0439 \u0435\u043a' # Success!

これは正常に機能しますが、evalreprを使用し、Unicode文字列表現の追加の正規表現を使用しているため、非常にハックに見えます。よりクリーンな方法はありますか?

33
Etienne Perot

Python 2では、Unicode文字列にはUnicodeとバイトの両方が含まれる場合があります。

いいえ、できません。 Unicode文字が含まれています。

元の文字列内で、\xd0は、UTF-8エンコーディングの一部であるバイトではありません。コードポイント208のUnicode文字です。u'\xd0' == u'\u00d0'。 Python 2のUnicode文字列のreprは、\x可能な場合はエスケープします(つまり、コードポイント<256)。

文字列を見て、\xd0バイトは、UTF-8でエンコードされた文字の一部であるか、それ自体がそのUnicode文字を実際に表している場合に想定されています。

ただし、これらの値を常にエンコードされた値として解釈できると仮定した場合、各文字を順番に分析するコードを作成して(ordを使用してコードポイント整数に変換)、256以下の文字をデコードできますUTF-8、および256以上の文字をそのまま渡します。

22
Karl Knechtel

(上記のコメントへの応答で):このコードはutf8のように見えるすべてを変換し、他のコードポイントをそのまま残します:

a = u'\u0420\u0443\u0441 utf:\xd0\xb5\xd0\xba bytes:bl\xe4\xe4'

def convert(s):
    try:
        return s.group(0).encode('latin1').decode('utf8')
    except:
        return s.group(0)

import re
a = re.sub(r'[\x80-\xFF]+', convert, a)
print a.encode('utf8')   

結果:

Рус utf:ек bytes:blää  
12
georg

問題は、文字列が実際には特定のエンコードでエンコードされていないであることです。あなたのサンプル文字列:

a = u'\u0420\u0443\u0441\u0441\u043a\u0438\u0439 \xd0\xb5\xd0\xba'

ユニコード文字列のpythonの内部表現をutf-8エンコードされたテキストと混合しています。 「特別な」文字だけを考慮する場合:

>>> orig = u'\u0435\u043a'
>>> bytes = u'\xd0\xb5\xd0\xba'
>>> print orig
ек
>>> print bytes
ек

しかし、あなたは言う、bytesutf-8エンコードされます:

>>> print bytes.encode('utf-8')
ек
>>> print bytes.encode('utf-8').decode('utf-8')
ек

違う!しかし、どうですか:

>>> bytes = '\xd0\xb5\xd0\xba'
>>> print bytes
ек
>>> print bytes.decode('utf-8')
ек

ハラー。

そう。 これは私にとって何を意味しますか?これは、(おそらく)間違った問題を解決していることを意味します。あなたが私たちに尋ねるべき/理解しようとするのは、あなたの文字列が最初にこの形式である理由とそれを回避/修正する方法ですbeforeあなたそれらをすべて混ぜ合わせます。

11
beerbajay

あなたはすでに答えを持っていますが、ここにラテン-1 Unicodeシーケンスをデコードする可能性が低いUTF-8-likeUnicodeシーケンスを解読する方法がありますエラーが発生しました。 re.sub 関数:

  1. 有効なUTF-8シーケンスに似ているUnicode文字<U + 0100に一致します(参照: RFC 3629 )。
  2. Unicodeシーケンスを同等のlatin-1バイトシーケンスにエンコードします。
  3. UTF-8を使用してシーケンスをUnicodeにデコードします。
  4. 元のUTF-8のようなシーケンスを一致するUnicode文字で置き換えます。

just正しい文字が隣り合って表示される場合、これはUnicodeシーケンスと一致する可能性がありますが、その可能性ははるかに低いことに注意してください。

import re

# your example
a = u'\u0420\u0443\u0441\u0441\u043a\u0438\u0439 \xd0\xb5\xd0\xba'

# printable Unicode characters < 256.
a += ''.join(chr(n) for n in range(32,256)).decode('latin1')

# a few UTF-8 characters decoded as latin1.
a += ''.join(unichr(n) for n in [2**7-1,2**7,2**11-1,2**11]).encode('utf8').decode('latin1')

# Some non-BMP characters
a += u'\U00010000\U0010FFFF'.encode('utf8').decode('latin1')

print repr(a)

# Unicode codepoint sequences that resemble UTF-8 sequences.
p = re.compile(ur'''(?x)
    \xF0[\x90-\xBF][\x80-\xBF]{2} |  # Valid 4-byte sequences
        [\xF1-\xF3][\x80-\xBF]{3} |
    \xF4[\x80-\x8F][\x80-\xBF]{2} |

    \xE0[\xA0-\xBF][\x80-\xBF]    |  # Valid 3-byte sequences
        [\xE1-\xEC][\x80-\xBF]{2} |
    \xED[\x80-\x9F][\x80-\xBF]    |
        [\xEE-\xEF][\x80-\xBF]{2} |

    [\xC2-\xDF][\x80-\xBF]           # Valid 2-byte sequences
    ''')

def replace(m):
    return m.group(0).encode('latin1').decode('utf8')

print
print repr(p.sub(replace,a))

出力

u '\ u0420\u0443\u0441\u0441\u043a\u0438\u0439 \ xd0\xb5\xd0\xba ! "#$%&\ '()* +、-。/ 0123456789:; <=>?@ ABCDEFGHIJKLMNOPQRSTUVWXYZ [\] ^ _` abcdefghijklmnopqrstuvwxyz {|}〜\ x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff\x7f\ xc2\x80\xdf\xbf\xe0\xa0\x80\xf0\x90\x80\x80\xf4\x8f\xbf\xbf'

u '\ u0420\u0443\u0441\u0441\u043a\u0438\u0439 \ u0435\u043a ! "#$%&\ '()* +、-。/ 0123456789:; <=>?@ ABCDEFGHIJKLMNOPQRSTUVWXYZ [\] ^ _` abcdefghijklmnopqrstuvwxyz {|}〜\ x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff\x7f\ x80\u07ff\u0800\U00010000\U0010ffff'

5
Mark Tolonen

unichrsをchrsに変換してからデコードする必要があります。

u'\xd0' == u'\u00d0'Trueです

$ python
>>> import re
>>> a = u'\u0420\u0443\u0441\u0441\u043a\u0438\u0439 \xd0\xb5\xd0\xba'
>>> re.sub(r'[\000-\377]*', lambda m:''.join([chr(ord(i)) for i in m.group(0)]).decode('utf8'), a)
u'\u0420\u0443\u0441\u0441\u043a\u0438\u0439 \u0435\u043a'
  • r'[\000-\377]*'はunichrsと一致しますu'[\u0000-\u00ff]*'
  • u'\xd0\xb5\xd0\xba' == u'\u00d0\u00b5\u00d0\u00ba'
  • あなたが使う utf8エンコードされたバイトをUnicodeコードポイントとして(これは問題です
  • これらの誤ったユニチャーを対応するバイトのふりをして問題を解決します
  • 私はこれらすべての間違ったユニチャーを検索し、それらを文字に変換してからデコードします。

私が間違っている場合、教えてください。

5
kev