如何理解一个编码不当的信息?

10 投票
3 回答
2614 浏览
提问于 2025-04-16 07:43
---------------------------
ƒGƒ‰[
---------------------------
ƒfƒBƒXƒvƒŒƒCƒ‚[ƒh‚ªÝ’è‚Å‚«‚Ü‚¹‚ñ.
---------------------------
OK   
---------------------------

我在使用Shooter's Solitude system 4时,遇到了一个清晰的错误信息,原因是我输入了这个版本的 d3drm.dll(唉)。

为了方便你们,这里有一个十六进制转储:

00000000  c6 92 66 c6 92 42 c6 92  58 c6 92 76 c6 92 c5 92  |..f..B..X..v....|
00000010  c6 92 43 c6 92 e2 80 9a  c2 81 5b c6 92 68 e2 80  |..C.......[..h..|
00000020  9a c2 aa c2 90 c3 9d e2  80 99 c3 a8 e2 80 9a c3  |................|
00000030  85 e2 80 9a c2 ab e2 80  9a c3 9c e2 80 9a c2 b9  |................|
00000040  e2 80 9a c3 b1 2e 0a                              |.......|
00000047

你会怎么把这个转化成一个通顺的错误信息呢——也就是说,你会怎么找到这个错误信息的正确编码和解码方式?


这是我尝试过的。

我猜问题出在开发者使用了错误的编码设置(考虑到这个游戏的年代,它是为WinXP开发的,这并不奇怪)。从外观来看,人们可能会猜测这个信息是用某种多字节编码编码的(ƒf ƒB ƒX ƒv ƒŒ)。

不过,每组似乎是由三个字节组成的(可能是可变的?)。这就排除了常见的编码方式:

>>> wat = "ƒfƒBƒXƒvƒŒƒCƒ‚[ƒh‚ªÝ’è‚Å‚«‚Ü‚¹‚ñ. "
>>> wat.encode("UTF-8").decode("UTF-32")
UnicodeDecodeError: 'utf32' codec cannot decode bytes in position 0-3:
codepoint not in range(0x110000)
>>> wat.encode("UTF-8").decode("UTF-16")
UnicodeDecodeError: 'utf16' codec cannot decode bytes in position 70-70:
truncated data
>>> wat.encode("UTF-8")[:-1].decode("UTF-16")
'鋆왦䊒鋆왘皒鋆鋅鋆왃\ue292骀臂왛梒胢슚슪쎐\ue29d馀ꣃ胢쎚\ue285骀ꯂ胢쎚\ue29c骀맂胢쎚⺱'
#meaningless according to Google Translate.

我选择UTF-8作为起始编码,因为ASCII不行(UnicodeEncodeError: 'ascii' codec can't encode character '\u0192' in position 0: ordinal not in range(128)),而且UTF-8应该是Windows 7的默认编码(我尝试使用的操作系统)。


还没到位。

Kabie可能有一些想法,但这并不是全部。首先,我无法重现他的编码:

>>> print (wat.encode("UTF-8").decode("Shift-JIS"))
UnicodeDecodeError: 'shift_jis' codec cannot decode bytes in position 22-23: illegal multibyte sequence
>>> print (wat.encode("UTF-8")[:22].decode("Shift-JIS"))
ニ断ニ達ニ湛ニ致ニ椎槌辰ニ停

维基百科说有一种非常相似的编码:cp932。

>>> print(wat.encode("UTF-8").decode("932"))
UnicodeDecodeError: 'cp932' codec cannot decode bytes in position 44-45: illegal multibyte sequence
>>> print(wat.encode("UTF-8")[:44].decode("932"))
ニ断ニ達ニ湛ニ致ニ椎槌辰ニ停喙ニ檀窶堋ェテ昶凖ィ窶堙

不过,这和他粘贴的内容非常不同。让我们看看:

>>> print("ディスプレイモ\x81[ドが\x90ン定できません.\n")
ディスプレイモ[ドがン定できません.

不过,这对谷歌翻译来说是无用的。我然后尝试去掉一些多余的部分。考虑到“ディスプレイ”意思是“显示”,如果我去掉那些无法解码的“垃圾”部分,我得到:

  ディスプレイモ\x81[ドが\x90ン定できません.
→ ディスプレイ      ドが    ン定できません.
→ The display mode is not specified.

然而,由于我在Stack Overflow上提问,这并不是全部。那些无法解码的字节怎么回事?你要怎么获取这些字节呢?

3 个回答

4

也许这会对你有帮助:

from binascii import unhexlify

data = '''\
c6 92 66 c6 92 42 c6 92 58 c6 92 76 c6 92 c5 92
c6 92 43 c6 92 e2 80 9a c2 81 5b c6 92 68 e2 80
9a c2 aa c2 90 c3 9d e2 80 99 c3 a8 e2 80 9a c3
85 e2 80 9a c2 ab e2 80 9a c3 9c e2 80 9a c2 b9
e2 80 9a c3 b1 2e 0a
'''

data = unhexlify(data.replace(' ','').replace('\n',''))
print data.decode('utf8').encode('windows-1252','xmlcharrefreplace').decode('shift-jis')

输出结果

ディスプレイモ[ドがン定できません.

你提供的十六进制数据是先用 Shift_JIS 解码为 windows-1252,然后再重新编码为 UTF-8。

编辑

基于 John Machin 的回答:

from binascii import unhexlify
import re

data = '''\
c6 92 66 c6 92 42 c6 92 58 c6 92 76 c6 92 c5 92
c6 92 43 c6 92 e2 80 9a c2 81 5b c6 92 68 e2 80
9a c2 aa c2 90 c3 9d e2 80 99 c3 a8 e2 80 9a c3
85 e2 80 9a c2 ab e2 80 9a c3 9c e2 80 9a c2 b9
e2 80 9a c3 b1 2e 0a
'''

data = unhexlify(data.replace(' ','').replace('\n',''))
data = data.decode('utf8').encode('windows-1252','xmlcharrefreplace')
# convert the XML entities that windows-1252 couldn't encode back into bytes
data = re.sub(r'&#(\d+);',lambda x: chr(int(x.group(1))),data)
print data.decode('shift-jis')

输出结果

ディスプレイモードが設定できません.
6

显然。

因为这是一个日本游戏。

'ディスプレイモ\x81[ドが\x90ン定できません.\n'

'Disupureimo \ x81 [the de \ x90 applications can not be fixed. \ N'

因为我粘贴的字符串有些缺失。

这个编码叫做Shift-JIS。我用我的Opera浏览器来显示这些字符。

编辑:可惜我所有的浏览器都无法在Stack Overflow上添加评论。我想这可能和网络有关。所以我只能在这里更新。

你可能应该把显示模式设置为256色。这是很多日本游戏所需要的。

编辑2:有趣的故事。

关于我如何得到这个字符串,最搞笑的是,我并没有像你可能尝试的那样直接编码原始字节,只得到了这个:

ニ断ニ達ニ湛ニ致ニ椎槌辰ニ停�堋ーニ檀窶堋ェツ静昶�凖ィ窶堙��堋ォ窶堙懌�堋ケ窶堙ア.

但是把这个字符串粘贴到另一个网页作为源代码,然后用Opera浏览器把编码改成了Shift-JIS。

Opera有一个功能,可以让你修改网页的源代码并显示出来。所以我写了一个页面:

<!DOCTYPE html>
<head>
<title>test</title>
</head>
<body>
'ƒfƒBƒXƒvƒŒƒCƒ‚ƒh‚ªÝ’è‚Å‚«‚Ü‚¹‚ñ.
</body>
</html>

然后我得到了这个:

'ディスプレイモドがン定できません.

这甚至更没有意义。你试过把颜色模式改成256色吗?

6

=== 文件 disupure.py ===

# start with the OP's hex dump:
hexbytes = """
c6 92 66 c6 92 42 c6 92  58 c6 92 76 c6 92 c5 92
c6 92 43 c6 92 e2 80 9a  c2 81 5b c6 92 68 e2 80
9a c2 aa c2 90 c3 9d e2  80 99 c3 a8 e2 80 9a c3
85 e2 80 9a c2 ab e2 80  9a c3 9c e2 80 9a c2 b9
e2 80 9a c3 b1 2e 0a
"""
strg = ''.join(
    chr(int(hexbyte, 16))
    for hexbyte in hexbytes.split()
    )
uc = strg.decode('utf8') # decodes OK but result is gibberish
uc_hex = ' '.join("%04X" % ord(x) for x in uc)
print uc_hex
# but it's stuffed ... U+0192??? oh yeah, 0x83
badenc = 'cp1252' # sort of, things like 0x81 have to be allowed for
fix_bad = {}
for i in xrange(256):
    b = chr(i)
    try:
        fix_bad[ord(b.decode(badenc))] = i
    except UnicodeDecodeError:
        fix_bad[i] = i

recoded = uc.translate(fix_bad).encode('latin1')
better_uc = recoded.decode('cp932')
# It's on Windows; cp932 what would have been used
# but 'sjis' gives the same answer
better_uc_hex = ' '.join("%04X" % ord(x) for x in better_uc)
print better_uc_hex
print repr(better_uc)
print better_uc

在 IDLE 中运行这个的结果(为了清晰添加了空行):

0192 0066 0192 0042 0192 0058 0192 0076 0192 0152 0192 0043 0192 201A 0081 005B 0192 0068 201A 00AA 0090 00DD 2019 00E8 201A 00C5 201A 00AB 201A 00DC 201A 00B9 201A 00F1 002E 000A

30C7 30A3 30B9 30D7 30EC 30A4 30E2 30FC 30C9 304C 8A2D 5B9A 3067 304D 307E 305B 3093 002E 000A

u'\u30c7\u30a3\u30b9\u30d7\u30ec\u30a4\u30e2\u30fc\u30c9\u304c\u8a2d\u5b9a\u3067\u304d\u307e\u305b\u3093.\n'

ディスプレイモードが設定できません.

谷歌翻译:你可以设置显示模式。

微软(必应)翻译:显示模式未设置。

更新 这里多解释一下为什么需要翻译表,以及为什么把 \x81 等映射到 U+0081,来自于维基百科关于 cp1252 的文章

根据微软和 Unicode 联盟网站上的信息,位置 81、8D、8F、90 和 9D 是未使用的。不过,Windows 的 API 调用在从代码页转换到 Unicode 时,会把这些位置映射到相应的 C1 控制代码。

撰写回答