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!
これは正常に機能しますが、eval
、repr
を使用し、Unicode文字列表現の追加の正規表現を使用しているため、非常にハックに見えます。よりクリーンな方法はありますか?
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以上の文字をそのまま渡します。
(上記のコメントへの応答で):このコードは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ää
問題は、文字列が実際には特定のエンコードでエンコードされていないであることです。あなたのサンプル文字列:
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
ек
しかし、あなたは言う、bytes
はutf-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あなたそれらをすべて混ぜ合わせます。
あなたはすでに答えを持っていますが、ここにラテン-1 Unicodeシーケンスをデコードする可能性が低いUTF-8-likeUnicodeシーケンスを解読する方法がありますエラーが発生しました。 re.sub
関数:
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'
unichr
sをchr
sに変換してからデコードする必要があります。
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コードポイントとして(これは問題です)私が間違っている場合、教えてください。