希腊语和特殊字符显示为 mojibake - 如何解码?

问题描述 投票:0回答:1

我正在尝试弄清楚如何解码电子表格中的一些损坏字符。有一个网站标题列表:一些是英语,一些是希腊语,一些是其他语言。例如,希腊短语

ΕΛΛΗΝΙΚΑ ΝΕΑ ΤΩΡΑ
显示为
ΕΛΛΗΝΙΚΑ ΝΕΑ ΤΩΡΑ
。所以空格没问题,但实际的字母全错了。

我注意到字母被转换为符号对:

  • Ε
    -
    Ε
  • Λ
    -
    Λ

等等。所以它几乎总是

Œ
,然后是其他一些符号。

我更进一步,删除了重复的字母,并检查了实际短语和损坏短语的 ASCII 代码之间的差异:

ord('ï') - ord('Ε')
等等。差异始终几乎相同:`

678
678
678
676
676
677
676
678
0 (this is a whitespace)
676
678
678
0 (this is a whitespace)
765
768
753
678

我已经手动解码了其他标题中的一些其他字母:

Greek

Œë  Α
Œî  Δ
Œï  Ε
Œõ  Λ
Œó  Η
Œô  Ι
Œö  Κ
Œù  Ν
Œ°  Ρ
Œ§  Τ
Œ©  Ω
Œµ  ε
Œª  λ
œÑ  τ
ŒØ  ί
Œø  ο
œÑ  τ
œâ  ω
ŒΩ  ν

Symbols

‚Äò ‘
‚Äô ’
‚Ķ …
‚Ć †
‚Äú “

Other

√©  é

很好,我有这个短语的翻译,但还有其他几个我没有翻译。我很高兴看到任何建议,因为在 StackOverflow 上搜索没有显示任何相关内容。

character-encoding decoding corrupt mojibake text-decoding
1个回答
2
投票

这是一个字符编码问题。该字符串似乎采用 Mac OS Roman 编码(通过 此网站 上的有根据的猜测得出)。该编码的 IANA 代码为

macintosh
,其 Windows 代码页号为 100000。

这是一个将

macintosh
解码为
utf-8
字符串的 Python 函数:

def macToUtf8(s):
  return bytes(s, 'macintosh').decode('utf-8')

print(macToUtf8('ΕΛΛΗΝΙΚΑ ΝΕΑ ΤΩΡΑ'))
# outputs: ΕΛΛΗΝΙΚΑ ΝΕΑ ΤΩΡΑ

我最好的猜测是您的电子表格保存在 Mac 计算机上,或者可能使用某些基于 Macintosh 的设置保存。

另请参阅此问题:MAC Excel 使用什么编码?


Google Apps 脚本

OP 的评论提到了 Google Sheets,因此其他人可能也有同样的问题。不幸的是,Sheets 看起来不支持除 ASCII 和 UTF-8 之外的任何内容,因此要从 Macintosh 转换为 UTF-8,我们必须制作一个转换表。我用 Python 做了这个。这会打印一个我们可以用于此目的的 JS 对象文字:

def formatUnicode(num):
    return f"\\u{num:0{4}x}"

def printUtf8ByteDecodeTable(encoding):
    print("{")
    for i in range(256):
        utf8Hex = formatUnicode(ord(i.to_bytes(1, byteorder='big').decode(encoding)))
        hexByte = hex(i)
        print(f"    '{utf8Hex}': {hexByte},")
    print("}")

printUtf8ByteDecodeTable('macintosh')

一旦我们有了查找表,我们就可以编写脚本了:

function macToUtf8(str) {
  const decodeTable = {
    '\u0000': 0x0,
    '\u0001': 0x1,
    '\u0002': 0x2,
    '\u0003': 0x3,
    '\u0004': 0x4,
    '\u0005': 0x5,
    '\u0006': 0x6,
    '\u0007': 0x7,
    '\u0008': 0x8,
    '\u0009': 0x9,
    '\u000a': 0xa,
    '\u000b': 0xb,
    '\u000c': 0xc,
    '\u000d': 0xd,
    '\u000e': 0xe,
    '\u000f': 0xf,
    '\u0010': 0x10,
    '\u0011': 0x11,
    '\u0012': 0x12,
    '\u0013': 0x13,
    '\u0014': 0x14,
    '\u0015': 0x15,
    '\u0016': 0x16,
    '\u0017': 0x17,
    '\u0018': 0x18,
    '\u0019': 0x19,
    '\u001a': 0x1a,
    '\u001b': 0x1b,
    '\u001c': 0x1c,
    '\u001d': 0x1d,
    '\u001e': 0x1e,
    '\u001f': 0x1f,
    '\u0020': 0x20,
    '\u0021': 0x21,
    '\u0022': 0x22,
    '\u0023': 0x23,
    '\u0024': 0x24,
    '\u0025': 0x25,
    '\u0026': 0x26,
    '\u0027': 0x27,
    '\u0028': 0x28,
    '\u0029': 0x29,
    '\u002a': 0x2a,
    '\u002b': 0x2b,
    '\u002c': 0x2c,
    '\u002d': 0x2d,
    '\u002e': 0x2e,
    '\u002f': 0x2f,
    '\u0030': 0x30,
    '\u0031': 0x31,
    '\u0032': 0x32,
    '\u0033': 0x33,
    '\u0034': 0x34,
    '\u0035': 0x35,
    '\u0036': 0x36,
    '\u0037': 0x37,
    '\u0038': 0x38,
    '\u0039': 0x39,
    '\u003a': 0x3a,
    '\u003b': 0x3b,
    '\u003c': 0x3c,
    '\u003d': 0x3d,
    '\u003e': 0x3e,
    '\u003f': 0x3f,
    '\u0040': 0x40,
    '\u0041': 0x41,
    '\u0042': 0x42,
    '\u0043': 0x43,
    '\u0044': 0x44,
    '\u0045': 0x45,
    '\u0046': 0x46,
    '\u0047': 0x47,
    '\u0048': 0x48,
    '\u0049': 0x49,
    '\u004a': 0x4a,
    '\u004b': 0x4b,
    '\u004c': 0x4c,
    '\u004d': 0x4d,
    '\u004e': 0x4e,
    '\u004f': 0x4f,
    '\u0050': 0x50,
    '\u0051': 0x51,
    '\u0052': 0x52,
    '\u0053': 0x53,
    '\u0054': 0x54,
    '\u0055': 0x55,
    '\u0056': 0x56,
    '\u0057': 0x57,
    '\u0058': 0x58,
    '\u0059': 0x59,
    '\u005a': 0x5a,
    '\u005b': 0x5b,
    '\u005c': 0x5c,
    '\u005d': 0x5d,
    '\u005e': 0x5e,
    '\u005f': 0x5f,
    '\u0060': 0x60,
    '\u0061': 0x61,
    '\u0062': 0x62,
    '\u0063': 0x63,
    '\u0064': 0x64,
    '\u0065': 0x65,
    '\u0066': 0x66,
    '\u0067': 0x67,
    '\u0068': 0x68,
    '\u0069': 0x69,
    '\u006a': 0x6a,
    '\u006b': 0x6b,
    '\u006c': 0x6c,
    '\u006d': 0x6d,
    '\u006e': 0x6e,
    '\u006f': 0x6f,
    '\u0070': 0x70,
    '\u0071': 0x71,
    '\u0072': 0x72,
    '\u0073': 0x73,
    '\u0074': 0x74,
    '\u0075': 0x75,
    '\u0076': 0x76,
    '\u0077': 0x77,
    '\u0078': 0x78,
    '\u0079': 0x79,
    '\u007a': 0x7a,
    '\u007b': 0x7b,
    '\u007c': 0x7c,
    '\u007d': 0x7d,
    '\u007e': 0x7e,
    '\u007f': 0x7f,
    '\u00c4': 0x80,
    '\u00c5': 0x81,
    '\u00c7': 0x82,
    '\u00c9': 0x83,
    '\u00d1': 0x84,
    '\u00d6': 0x85,
    '\u00dc': 0x86,
    '\u00e1': 0x87,
    '\u00e0': 0x88,
    '\u00e2': 0x89,
    '\u00e4': 0x8a,
    '\u00e3': 0x8b,
    '\u00e5': 0x8c,
    '\u00e7': 0x8d,
    '\u00e9': 0x8e,
    '\u00e8': 0x8f,
    '\u00ea': 0x90,
    '\u00eb': 0x91,
    '\u00ed': 0x92,
    '\u00ec': 0x93,
    '\u00ee': 0x94,
    '\u00ef': 0x95,
    '\u00f1': 0x96,
    '\u00f3': 0x97,
    '\u00f2': 0x98,
    '\u00f4': 0x99,
    '\u00f6': 0x9a,
    '\u00f5': 0x9b,
    '\u00fa': 0x9c,
    '\u00f9': 0x9d,
    '\u00fb': 0x9e,
    '\u00fc': 0x9f,
    '\u2020': 0xa0,
    '\u00b0': 0xa1,
    '\u00a2': 0xa2,
    '\u00a3': 0xa3,
    '\u00a7': 0xa4,
    '\u2022': 0xa5,
    '\u00b6': 0xa6,
    '\u00df': 0xa7,
    '\u00ae': 0xa8,
    '\u00a9': 0xa9,
    '\u2122': 0xaa,
    '\u00b4': 0xab,
    '\u00a8': 0xac,
    '\u2260': 0xad,
    '\u00c6': 0xae,
    '\u00d8': 0xaf,
    '\u221e': 0xb0,
    '\u00b1': 0xb1,
    '\u2264': 0xb2,
    '\u2265': 0xb3,
    '\u00a5': 0xb4,
    '\u00b5': 0xb5,
    '\u2202': 0xb6,
    '\u2211': 0xb7,
    '\u220f': 0xb8,
    '\u03c0': 0xb9,
    '\u222b': 0xba,
    '\u00aa': 0xbb,
    '\u00ba': 0xbc,
    '\u03a9': 0xbd,
    '\u00e6': 0xbe,
    '\u00f8': 0xbf,
    '\u00bf': 0xc0,
    '\u00a1': 0xc1,
    '\u00ac': 0xc2,
    '\u221a': 0xc3,
    '\u0192': 0xc4,
    '\u2248': 0xc5,
    '\u2206': 0xc6,
    '\u00ab': 0xc7,
    '\u00bb': 0xc8,
    '\u2026': 0xc9,
    '\u00a0': 0xca,
    '\u00c0': 0xcb,
    '\u00c3': 0xcc,
    '\u00d5': 0xcd,
    '\u0152': 0xce,
    '\u0153': 0xcf,
    '\u2013': 0xd0,
    '\u2014': 0xd1,
    '\u201c': 0xd2,
    '\u201d': 0xd3,
    '\u2018': 0xd4,
    '\u2019': 0xd5,
    '\u00f7': 0xd6,
    '\u25ca': 0xd7,
    '\u00ff': 0xd8,
    '\u0178': 0xd9,
    '\u2044': 0xda,
    '\u20ac': 0xdb,
    '\u2039': 0xdc,
    '\u203a': 0xdd,
    '\ufb01': 0xde,
    '\ufb02': 0xdf,
    '\u2021': 0xe0,
    '\u00b7': 0xe1,
    '\u201a': 0xe2,
    '\u201e': 0xe3,
    '\u2030': 0xe4,
    '\u00c2': 0xe5,
    '\u00ca': 0xe6,
    '\u00c1': 0xe7,
    '\u00cb': 0xe8,
    '\u00c8': 0xe9,
    '\u00cd': 0xea,
    '\u00ce': 0xeb,
    '\u00cf': 0xec,
    '\u00cc': 0xed,
    '\u00d3': 0xee,
    '\u00d4': 0xef,
    '\uf8ff': 0xf0,
    '\u00d2': 0xf1,
    '\u00da': 0xf2,
    '\u00db': 0xf3,
    '\u00d9': 0xf4,
    '\u0131': 0xf5,
    '\u02c6': 0xf6,
    '\u02dc': 0xf7,
    '\u00af': 0xf8,
    '\u02d8': 0xf9,
    '\u02d9': 0xfa,
    '\u02da': 0xfb,
    '\u00b8': 0xfc,
    '\u02dd': 0xfd,
    '\u02db': 0xfe,
    '\u02c7': 0xff,
  };

  // Translate
  const bytes = [];
  for (const c of str) {
    bytes.push(decodeTable[c]);
  }

  // Apps script does not have the WHATWG standard TextDecoder class
  return Utilities.newBlob("").setBytes(bytes).getDataAsString('utf-8');
}

对于 Node/浏览器,最后一行相当于

return new TextDecoder().decode(new Uint8Array(bytes));
© www.soinside.com 2019 - 2024. All rights reserved.