转换双斜杠utf-8编码

时间:2016-09-02 03:02:56

标签: python unicode encoding utf-8

我无法让这个工作!我有一个来自保存游戏文件解析器的文本文件,其中包含一堆UTF-8中文名称,字节形式,如source.txt中所示:

\ XE6 \ X89 \ x8e \ xe5 \ x8a \ XA0 \ XE6 \ x8b \ X89

但是,无论我如何将它导入Python(3或2),我都会得到这个字符串,充其量:

\\ XE6 \\ X89 \\ x8e \\ xe5 \\ x8a \\ XA0 \\ XE6 \\ x8b \\ X89

我试过像其他线程建议的那样,将字符串重新编码为UTF-8,然后使用unicode escape解码它,如下所示:

stringName.encode("utf-8").decode("unicode_escape")

然后它弄乱了原始编码,并将其作为字符串:

'æ\ x89 \x8eå\ x8a \xa0æ\ x8b \ x89'(打印此字符串会导致:æåæ)

现在,如果我手动复制并粘贴b +文件名中的原始字符串并对其进行编码,我会得到正确的编码。例如:

b'\xe6\x89\x8e\xe5\x8a\xa0\xe6\x8b\x89'.encode("utf-8")

结果:'扎加拉'

但是,我不能以编程方式执行此操作。我甚至无法摆脱双斜线。

要清楚,source.txt包含单个反斜杠。我尝试过以多种方式导入它,但这是最常见的:

with open('source.txt','r',encoding='utf-8') as f_open:
    source = f_open.read()

好的,所以我点击下面的答案(我认为),但这是有效的:

from ast import literal_eval
decodedString = literal_eval("b'{}'".format(stringVariable)).decode('utf-8')

由于其他编码问题,我无法在整个文件中使用它,但是将每个名称提取为字符串(stringVariable)然后执行此操作!谢谢!

更清楚的是,原始文件不仅仅是这些混乱的编码。它仅对某些字段使用它们。例如,这是文件的开头:

{'m_cacheHandles': ['s2ma\x00\x00CN\x1f\x1b"\x8d\xdb\x1fr \\\xbf\xd4D\x05R\x87\x10\x0b\x0f9\x95\x9b\xe8\x16T\x81b\xe4\x08\x1e\xa8U\x11',
                's2ma\x00\x00CN\x1a\xd9L\x12n\xb9\x8aL\x1d\xe7\xb8\xe6\xf8\xaa\xa1S\xdb\xa5+\t\xd3\x82^\x0c\x89\xdb\xc5\x82\x8d\xb7\x0fv',
                's2ma\x00\x00CN\x92\xd8\x17D\xc1D\x1b\xf6(\xedj\xb7\xe9\xd1\x94\x85\xc8`\x91M\x8btZ\x91\xf65\x1f\xf9\xdc\xd4\xe6\xbb',
                's2ma\x00\x00CN\xa1\xe9\xab\xcd?\xd2PS\xc9\x03\xab\x13R\xa6\x85u7(K2\x9d\x08\xb8k+\xe2\xdeI\xc3\xab\x7fC',
                's2ma\x00\x00CNN\xa5\xe7\xaf\xa0\x84\xe5\xbc\xe9HX\xb93S*sj\xe3\xf8\xe7\x84`\xf1Ye\x15~\xb93\x1f\xc90',
                's2ma\x00\x00CN8\xc6\x13F\x19\x1f\x97AH\xfa\x81m\xac\xc9\xa6\xa8\x90s\xfdd\x06\rL]z\xbb\x15\xdcI\x93\xd3V'],
'm_campaignIndex': 0,
'm_defaultDifficulty': 7,
'm_description': '',
'm_difficulty': '',
'm_gameSpeed': 4,
'm_imageFilePath': '',
'm_isBlizzardMap': True,
'm_mapFileName': '',
'm_miniSave': False,
'm_modPaths': None,
'm_playerList': [{'m_color': {'m_a': 255, 'm_b': 255, 'm_g': 92,   'm_r': 36},
               'm_control': 2,
               'm_handicap': 0,
               'm_hero': '\xe6\x89\x8e\xe5\x8a\xa0\xe6\x8b\x89',

'm_hero':字段之前的所有信息都不是utf-8。因此,如果文件仅由这些假的utf编码组成,那么使用ShadowRanger的解决方案是有效的,但是当我已经将m_hero解析为字符串并尝试转换它时它不起作用。 Karin的解决方案确实有用。

6 个答案:

答案 0 :(得分:9)

问题在于the unicode_escape codec is implicitly decoding the result of the escape fixes by assuming the bytes are latin-1, not utf-8。您可以通过以下方式解决此问题:

# Read the file as bytes:
with open(myfile, 'rb') as f:
    data = f.read()

# Decode with unicode-escape to get Py2 unicode/Py3 str, but interpreted
# incorrectly as latin-1
badlatin = data.decode('unicode-escape')

# Encode back as latin-1 to get back the raw bytes (it's a 1-1 encoding),
# then decode them properly as utf-8
goodutf8 = badlatin.encode('latin-1').decode('utf-8')

其中(假设文件包含文字反斜杠和代码,而不是它们代表的字节)会留下'\u624e\u52a0\u62c9'(这应该是正确的,我只是在没有字体支持的系统上,所以这只是基于Unicode转义的安全repr。您可以使用第一阶段string-escape的{​​{1}}编解码器跳过Py2中的一个步骤(我相信这将允许您省略decode步骤),但此解决方案应该是可移植的,并且成本不应该太糟糕。

答案 1 :(得分:2)

我假设您正在使用Python 3.在Python 2中,默认情况下字符串是字节,所以它只适合您。但是在Python 3中,字符串是unicode并被解释为unicode,如果您将字节字符串读取为unicode,这就会使这个问题变得更难。

这个解决方案的灵感来自于mgilson的回答。我们可以使用literal_eval

逐字地将您的unicode字符串评估为字节字符串
from ast import literal_eval

with open('source.txt', 'r', encoding='utf-8') as f_open:
    source = f_open.read()
    string = literal_eval("b'{}'".format(source)).decode('utf-8')
    print(string)  # 扎加拉

答案 2 :(得分:1)

你可以做一些愚蠢的事情,比如eval使用字符串:

import ast
s = r'\xe6\x89\x8e\xe5\x8a\xa0\xe6\x8b\x89'
print ast.literal_eval('"%s"' % s).decode('utf-8')
  • 如果您不希望攻击者访问您的系统,请使用ast.literal_eval :-P

在你的情况下使用它可能看起来像:

with open('file') as file_handle:
    data = ast.literal_eval('"%s"' % file.read()).decode('utf-8')

我认为这里真正的问题是可能你有一个包含表示字节的字符串的文件(而不是只有一个文件只存储字节本身)。因此,首先修复生成该文件的任何代码可能是更好的选择。但是,除此之外,这是我能提出的下一个最好的事情......

答案 3 :(得分:1)

Python3中的解决方案,只有字符串操作和编码转换,没有恶意eval :)

import binascii

str = '\\xe6\\x89\\x8e\\xe5\\x8a\\xa0\\xe6\\x8b\\x89'
str = str.replace('\\x', '')  # str == 'e6898ee58aa0e68b89'

# we can use any encoding as long as it translate ascii as is,
# for example we can do str.encode('ascii') here
str = str.encode('utf8')  # str == b'e6898ee58aa0e68b89'

str = binascii.a2b_hex(str)  # str == b'\xe6\x89\x8e\xe5\x8a\xa0\xe6\x8b\x89'
str = str.decode('utf8')  # str == '扎加拉'

如果您喜欢单行,那么我们可以简单地说:

binascii.a2b_hex(str.replace('\\x', '').encode()).decode('utf8')

答案 4 :(得分:0)

在一天结束时,你得到的是一个字符串对吗?我将使用string.replace方法将双斜杠转换为单斜杠并添加b前缀以使其工作。

答案 5 :(得分:0)

因此,有几种不同的方法可以解释以字节形式存储数据""让我们假设你真的这样做:

s = b'\xe6\x89\x8e\xe5\x8a\xa0\xe6\x8b\x89'

b前缀表示这些是字节。没有进入 整个混乱是字节与代码点/字符和长期差异 在Python 2和3之间,b - 前缀字符串表示这些是预期的 为字节(例如原始UTF-8字节)。

然后解码它,转换UTF-8编码(你已经编码) 拥有字节,成为真正的Unicode字符。在Python 2.7中,例如:

print s.decode('utf-8')

的产率:

扎加拉

你的一个例子是编码,然后是解码,只能导致悲伤和痛苦。如果您的变量包含真正的UTF-8字节,则只需要解码。

更新根据讨论情况,数据似乎不是UTF-8字节,而是字符串序列化版本。从字符串串行到字节有很多种方法。这是我的:

from struct import pack

def byteize(s):
    """
    Given a backslash-escaped string serialization of bytes,
    decode it into a genuine byte string.
    """
    bvals = [int(s[i:i+2], 16) for i in range(2, len(s), 4)]
    return pack(str(len(bvals)) + 'B', *bvals)

然后:

print byteize(s).decode('utf-8')

如前所述:

扎加拉

byteize()与基于literal_eval()的{​​{3}}一般不同,但%timeit基准测试显示短字符串的速度提高了约33%。可以通过在Python 2下更换range xrange来进一步加速。literal_eval方法可以轻松获得长字符串,但是,它具有较低级别的性质。

100000 loops, best of 3: 6.19 µs per loop
100000 loops, best of 3: 8.3 µs per loop