用Python进行UTF-8解码,其中包含ascii码

用Python进行UTF-8解码,其中包含ascii码,python,encoding,utf-8,Python,Encoding,Utf 8,从中的问答中,我可以使用binascii包来解码一个包含“\u1”的utf-8字符串 def toUtf(r): try: rhexonly = r.replace('_', '') rbytes = binascii.unhexlify(rhexonly) rtext = rbytes.decode('utf-8') except TypeError: rtext = r return rtext 此代码

从中的问答中,我可以使用binascii包来解码一个包含“\u1”的utf-8字符串

def toUtf(r):
    try:
        rhexonly = r.replace('_', '')
        rbytes = binascii.unhexlify(rhexonly)
        rtext = rbytes.decode('utf-8')
    except TypeError:
        rtext = r
    return rtext
此代码仅适用于utf-8字符:

r = '_ed_8e_b8'
print toUtf(r)
>> 편 
但是,当字符串中包含普通ascii代码时,此代码不起作用。ascii可以是字符串中的任意位置

r = '_2f119_ed_8e_b8'
print toUtf(r)
>> doesn't work - _2f119_ed_8e_b8
>> this should be '/119편'

也许,我可以使用正则表达式提取utf-8部分和ascii部分,以便在转换后重新计算,但我想知道是否有更简单的方法来进行转换。有什么好办法吗

你的输入真是太糟糕了。但它仍然是可以修复的。首先,用十六进制等价物替换非“编码”内容:

import itertools
import re

r = '_2f119_ed_8e_b8'

# Split so you have even entries in the list as ASCII, odd as hex encodings
rsplit = re.split(r'((?:_[0-9a-fA-F]{2})+)', r)   # ['', '_2f', '119', '_ed_8e_b8', '']

# Process the hex encoded UTF-8 with your existing function, leaving
# ASCII untouched
rsplit[1::2] = map(toUtf, rsplit[1::2])  # ['', '/', '119', '관', '']

rtext = ''.join(rsplit)  # '/119편'
上面是一个详细的版本,显示了各个步骤,但正如所指出的,它可以大大缩短。使用相同的正则表达式代替,并传递函数以执行替换,而不是替换模式字符串:

# One-liner equivalent to the above with no intermediate lists
rtext = re.sub(r'(?:_[0-9a-f]{2})+', lambda m: toUtf(m.group()), r, flags=re.I)
您可以将其打包为函数本身,因此有一个函数处理纯十六进制编码的UTF-8,第二个通用函数使用第一个函数作为处理混合的非编码ASCII和十六进制编码UTF-8数据的一部分


请注意,如果未编码的ASCII可能正常包含
\uuu
,那么这不一定能很好地工作;正则表达式试图尽可能有针对性,但这里有一个问题,无论您如何精确地针对启发式,一些ASCII数据都会被误认为是编码的UTF-8数据。

这是一个非常糟糕的输入。但它仍然是可以修复的。首先,用十六进制等价物替换非“编码”内容:

import itertools
import re

r = '_2f119_ed_8e_b8'

# Split so you have even entries in the list as ASCII, odd as hex encodings
rsplit = re.split(r'((?:_[0-9a-fA-F]{2})+)', r)   # ['', '_2f', '119', '_ed_8e_b8', '']

# Process the hex encoded UTF-8 with your existing function, leaving
# ASCII untouched
rsplit[1::2] = map(toUtf, rsplit[1::2])  # ['', '/', '119', '관', '']

rtext = ''.join(rsplit)  # '/119편'
上面是一个详细的版本,显示了各个步骤,但正如所指出的,它可以大大缩短。使用相同的正则表达式代替,并传递函数以执行替换,而不是替换模式字符串:

# One-liner equivalent to the above with no intermediate lists
rtext = re.sub(r'(?:_[0-9a-f]{2})+', lambda m: toUtf(m.group()), r, flags=re.I)
您可以将其打包为函数本身,因此有一个函数处理纯十六进制编码的UTF-8,第二个通用函数使用第一个函数作为处理混合的非编码ASCII和十六进制编码UTF-8数据的一部分


请注意,如果未编码的ASCII可能正常包含
\uuu
,那么这不一定能很好地工作;正则表达式试图尽可能有针对性,但这里有一个问题,无论您如何精确地针对启发式,一些ASCII数据都会被误认为编码的UTF-8数据。

使用
re.sub非常简单:

import re

bytegroup = r'(_[0-9a-z]{2})+'

def replacer(match):
    return toUtf(match.group())

rtext = re.sub(bytegroup, replacer, r, flags=re.I)

使用
re.sub
非常简单:

import re

bytegroup = r'(_[0-9a-z]{2})+'

def replacer(match):
    return toUtf(match.group())

rtext = re.sub(bytegroup, replacer, r, flags=re.I)

你可能应该在评论@ShadowRanger的答案时询问他。作为一项规则,你不应该将答案编辑成问题。另外,请注意我对@chthonicdaemon的答案所做的编辑;您需要在
r
之后传递
flags=re.I
,而不是
re.I
,否则正则表达式会区分大小写运行,并且不会执行两次以上的替换(因为oops,结果是
re.sub
flags
参数之前有一个可选的
count
参数)。此外,模式中最外层的paren仅用于
re.split
方法;对于
re.sub
,它们可以(并且应该,对于较小的性能提升)被省略。您可能应该在对@ShadowRanger答案的评论中询问他。作为一项规则,您不应该将答案编辑为问题。另外,请注意我对@chthonicdaemon的答案所做的编辑;您需要在
r
之后传递
flags=re.I
,而不是
re.I
,否则正则表达式会区分大小写运行,并且不会执行两次以上的替换(因为oops,结果是
re.sub
flags
参数之前有一个可选的
count
参数)。此外,模式中最外层的paren仅用于
re.split
方法;对于
re.sub
,它们可以(并且应该,对于较小的性能提升)被省略。@Kevin:这不是偶数对奇数字符,而是偶数对奇数分割结果。
re.split
返回值会自动为您提供偶数->ASCII,奇数->编码。我将添加示例中间值。@凯文:这不是偶数对奇数字符,而是偶数对奇数分割结果。
re.split
返回值会自动为您提供偶数->ASCII,奇数->编码。我将添加示例中间值。我应该记住
re.sub
,该函数作为
re.split
+后处理和重
''的缩写。join
。这有点神奇,但这是更好的解决方案。除非你反对,否则我将在我的答案中以简短形式复制它以使其完整(展开位说明了各个部分,
re.sub
是一体机)。不管怎样,我都会投票给你。复制它没有问题。我检查过r'([0-9a-z]{2})+也可以正常工作,你有什么理由使用r'(?:[0-9a-z]{2})+?@proseek非捕获组对于我早期的解决方案迭代是必要的,但现在不再是了。我已经把它编辑掉了。我应该记得
re.sub
,它有一个函数作为
re.split
+后处理和重新
''的缩写。加入
。这有点神奇,但这是更好的解决方案。除非你反对,否则我将在我的答案中以简短形式复制它以使其完整(展开位说明了各个部分,
re.sub
是一体机)。不管怎样,我都会投票给你。复制它没有问题。我检查过r'([0-9a-z]{2})+也可以正常工作,你有什么理由使用r'(?:[0-9a-z]{2})+?@proseek非捕获组对于我早期的解决方案迭代是必要的,但现在不再是了。我把它删掉了。