【问题标题】:Howto identify UTF-8 encoded strings如何识别 UTF-8 编码的字符串
【发布时间】:2010-09-27 11:56:47
【问题描述】:

识别字符串(是或)是否可能是 UTF-8 编码的最佳方法是什么? Win32 API IsTextUnicode 在这里没有多大帮助。此外,该字符串不会有 UTF-8 BOM,因此无法检查。而且,是的,我知道只有 ASCII 范围以上的字符才被编码为超过 1 个字节。

【问题讨论】:

  • 你用什么语言试试这个?
  • 没关系 - 我正在寻找一种通用方法。但是,如果您可以提供适用于特定语言的内容,请加入。
  • 而且,您不会在 UTF-8 流的开头找到 BOM,这是没有意义的,因为无论字节顺序是什么,UTF-8 都是相同的。
  • 是的,但它仍然被某些人称为“BOM”。我更喜欢称它为“序言”(如 .NET)或“签名”。它仍然可以用于识别编码。

标签: unicode encoding utf-8


【解决方案1】:

作为前面 answer 关于 Win32 mlang DetectInputCodepage() API 的附加组件,下面是如何在 C 中调用它:

#include <Mlang.h>
#include <objbase.h>
#pragma comment(lib, "ole32.lib")

HRESULT hr;
IMultiLanguage2 *pML;
char *pszBuffer;
int iSize;
DetectEncodingInfo lpInfo[10];
int iCount = sizeof(lpInfo) / sizeof(DetectEncodingInfo);

hr = CoInitialize(NULL);
hr = CoCreateInstance(&CLSID_CMultiLanguage, NULL, CLSCTX_INPROC_SERVER, &IID_IMultiLanguage2, (LPVOID *)&pML);
hr = pML->lpVtbl->DetectInputCodepage(pML, 0, 0, pszBuffer, &iSize, lpInfo, &iCount);

CoUninitialize();

但测试结果非常令人失望:

  • 它无法区分 CP 437 和 CP 1252 中的法语文本,即使文本在错误的代码页中打开时完全不可读。
  • 它可以检测到以 CP 65001 (UTF-8) 编码的文本,但不能检测 UTF-16 中的文本,它被错误地报告为 CP 1252 很有把握!

【讨论】:

  • 当然,这只是必要的最少代码。您必须集成自己的有效功能。
【解决方案2】:

chardet 由 Mozilla 开发的用于 FireFox 的字符集检测。 Source code

jchardet 是 Mozilla 的自动字符集检测算法的源代码的 java 端口。

NCharDet 是 Mozilla 和 FireFox 浏览器中使用的 C++ 的 Java 端口的 .Net (C#) 端口。

Code project C# 示例使用 Microsoft 的 MLang 进行字符编码检测。

UTRAC 是一个用c++编写的用于检测字符串编码的命令行工具和库

cpdetector是一个用于编码检测的java项目

chsdet 是一个 delphi 项目,是一个独立的可执行模块,用于对给定文本或文件进行自动字符集/编码检测。

另一个有用的帖子,指向很多库来帮助你确定字符编码http://fredeaker.blogspot.com/2007/01/character-encoding-detection.html

你也可以看看相关问题How Can I Best Guess the Encoding when the BOM (Byte Order Mark) is Missing?,里面有一些有用的内容。

【讨论】:

  • 很抱歉评论一个 10 年前的答案,但我有一个小的更正。 cpdetector 不是 Delphi 库(它是用 Java 编写的)。如果有人正在寻找 Delphi 编码检测,可能值得研究 chsdet
  • @AdamHenderson 感谢您的更正,我已经更新了问题
【解决方案3】:

对于 Win32,您可以使用 mlang API,这是 Windows 的一部分,并且受 Windows XP 的支持,很酷的一点是,它可以为您提供输入在特定编码中的可能性的统计数据:

CComPtr<IMultiLanguage2> lang;
HRESULT hr = lang.CoCreateInstance(CLSID_CMultiLanguage, NULL, CLSCTX_INPROC_SERVER);
char* str = "abc"; // EF BB BF 61 62 63
int size = 6;
DetectEncodingInfo encodings[100];
int encodingsCount = 100;
hr = lang->DetectInputCodepage(MLDETECTCP_NONE, 0, str, &size, &encodings, &encodingsCount);

【讨论】:

  • 我刚刚对 DetectEncodingInfo() 例程做了一些测试,结果很差: * 它无法区分 CP 437 和 CP 1252 中的法语文本,即使文本如果在错误的代码页中打开,则完全不可读。 * 可以检测到 CP 65001 (UTF-8) 编码的文本,但不能检测 UTF-16 编码的文本,有把握误报为 CP 1252!
【解决方案4】:

在 Windows 上,您可以将 MultiByteToWideChar()CP_UTF8 代码页和 MB_ERR_INVALID_CHARS 标志一起使用。如果函数失败,则字符串不是有效的 UTF-8。

【讨论】:

    【解决方案5】:

    您没有指定语言,但在 PHP 中您可以使用 mb_check_encoding

       if(mb_check_encoding($yourDtring, 'UTF-8'))
       {
       //the string is UTF-8
        }
       else 
        {
           //string is not UTF-8
         }
    

    【讨论】:

      【解决方案6】:

      基于 Mozilla 字符集检测器的 C/C++ 独立库

      https://github.com/batterseapower/libcharsetdetect

      通用字符集检测器 (UCSD) 向 Mozilla C++ UCSD 库公开 C 接口和无依赖接口的库。该库提供了一组高度准确的启发式方法,试图确定用于编码某些输入文本的字符集。当您的程序必须处理一个没有任何编码元数据的输入文件时,这非常有用。

      【讨论】:

        【解决方案7】:

        要进行字符检测在 ruby​​ 中安装 'chardet' gem

        sudo gem install chardet
        

        这是一个在标准输入流上运行 chardet 的小 ruby​​ 脚本。

        require "rubygems"
        require 'UniversalDetector' #chardet gem
        infile =  $stdin.read()
        p UniversalDetector::chardet(infile)
        

        Chardet 输出对字符集编码的猜测,以及来自其统计分析的置信度 (0-1)

        see also this snippet

        【讨论】:

          【解决方案8】:

          This W3C page 有一个用于验证 UTF-8 的 perl 正则表达式

          【讨论】:

          • 如果您正在阅读流并且您可能没有开头,您应该在开头松开 \A 或添加“.{0,5}?”在它之后捕获第一个截断的字符。
          • 我建议使用该语言的标准 Unicode 库而不是通过正则表达式重新实现它。
          【解决方案9】:

          没有真正可靠的方法,但基本上,作为字节的随机序列(例如,标准 8 位编码的字符串)不太可能是有效的 UTF-8 字符串(如果字节的最高有效位设置了,对于UTF-8中可以跟什么样的字节有非常具体的规则),您可以尝试将字符串解码为UTF-8,如果没有解码错误则认为它是UTF-8。

          确定是否存在解码错误完全是另一个问题,许多 Unicode 库只是用问号替换无效字符,而不指示是否发生错误。因此,您需要一种明确的方法来确定解码时是否发生错误。

          【讨论】:

          • 任何适当的 Unicode库都应该用U+FFFD代替未知字符,而不是用文字问号。
          猜你喜欢
          • 1970-01-01
          • 2014-06-09
          • 2013-06-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-07-02
          相关资源
          最近更新 更多