有谁知道检测被错误解码为 Latin-1 文本的 UTF-8 的启发式方法吗?

Does anyone know a good heuristic for detecting UTF-8 badly decoded as Latin-1 text?

我正在从气象服务处收到天气警报。虽然 HTTP 响应声称是 UTF-8,但显然它包含一些这样的文本:

Suðaustan 13-20 m/s og snjókoma með lélegu skyggni og versnandi akstursskilyrðum.

...应该如下所示:

Suðaustan 13-20 m/s og snjókoma með lélegu skyggni og versnandi akstursskilyrðum.

...但在它第一次到达我之前已经被错误地解码,被错误地解码后重新编码为UTF-8。我们大多数人以前可能都见过这种“mojibake”垃圾,至少在视觉上,它通常具有许多共同特征——例如很多 Ã 个字符、¢ 个符号等等。

我现在正在使用此代码修复它:

  // Check for UTF-8 wrongly decoded as Latin-1
  if (/[\x80-\xC5]/.test(result)) {
    const bytes = Buffer.from(result, 'latin1');
    const altText = bytes.toString('utf8');

    if (altText.length < result.length)
      result = altText;
  }

...这就是目前的工作,但这不是一个非常复杂的测试。

有人知道更好的方法吗?

Anyone know of a better method?

不知道你怎么判断比较好。我刚才写了这个函数来对字符串做这个转换。

不知道这是否比缓冲区更好。

function utf8_decode(str) {
  //assuming the input is a valid utf-8 string. 
  //Invalid parts are ignored / remain in the string.
  return str.replace(
    /[\u00c0-\u00df][\u0080-\u00bf]|([\u00e0-\u00ef][\u0080-\u00bf]{2})|([\u00f0-\u00f7][\u0080-\u00bf]{3})/g,
    (two, three, four) => String.fromCodePoint(
      // UTF-16 codePoints
      four ? (four.charCodeAt(0) & 7) << 18 | (four.charCodeAt(1) & 63) << 12 | (four.charCodeAt(2) & 63) << 6 | (four.charCodeAt(3) & 63) :
      // UTF-8 multibytes
      three ? (three.charCodeAt(0) & 15) << 12 | (three.charCodeAt(1) & 63) << 6 | (three.charCodeAt(2) & 63) :
      (two.charCodeAt(0) & 31) << 6 | (two.charCodeAt(1) & 63)
    )
  )
}

console.log(utf8_decode("Suðaustan 13-20 m/s og snjókoma með lélegu skyggni og versnandi akstursskilyrðum."));

console.log(utf8_decode("ð\x9F\x98\x8B"));

正则表达式比你的好。

之后无需检查转换是否导致某些更改。