在PHP中测试UTF8字符串,这是一种可靠的方法吗?

时间:2012-03-14 23:08:32

标签: php string utf-8

我在另一个答案中找到了一个有用的功能,我想知道是否有人可以向我解释它在做什么以及它是否可靠。我使用的是mb_detect_encoding(),但是当从Linux操作系统上的ISO8859-1文件中读取时,它被删除了。

这个功能似乎适用于我测试的所有情况。

以下是问题:Get file encoding

这是功能:

function isUTF8($string){
    return preg_match('%(?:
    [\xC2-\xDF][\x80-\xBF]        # non-overlong 2-byte
    |\xE0[\xA0-\xBF][\x80-\xBF]               # excluding overlongs
    |[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}      # straight 3-byte
    |\xED[\x80-\x9F][\x80-\xBF]               # excluding surrogates
    |\xF0[\x90-\xBF][\x80-\xBF]{2}    # planes 1-3
    |[\xF1-\xF3][\x80-\xBF]{3}                  # planes 4-15
    |\xF4[\x80-\x8F][\x80-\xBF]{2}    # plane 16
    )+%xs', $string);
}

这是检测UTF8字符串的可靠方法吗? 到底是做什么的? 可以变得更强大吗?

6 个答案:

答案 0 :(得分:6)

如果您知道字符串的编码,则无法以任何精度猜测编码。这就是mb_detect_encoding根本不起作用的原因。但是,如果你知道字符串应该的编码方式,你可以使用mb_check_encoding检查它是否是该编码中的有效字符串。它或多或少都与你的正则表达式有关,可能更全面一些。它可以回答问题“这个字节序列在UTF-8中是否有效?”,清楚是或否。这并不一定意味着实际上 的字符串在该编码中被编码,只是它可能是。例如,使用8位来区分使用所有8位的任何单字节编码与任何其他单字节编码是不可能的。但是UTF-8 应该可以相当区分,但是你可以产生例如也恰好是有效的UTF-8字节序列的Latin-1编码字符串。

简而言之,没有办法确切知道。如果您期望UTF-8,请检查您收到的字节序列是否在UTF-8中有效,那么您可以将该字符串安全地视为UTF-8。除此之外,你几乎无能为力。

答案 1 :(得分:0)

好吧,它只检查字符串是否包含恰好与有效UTF-8代码点对应的字节序列。但是,它不会标记序列0x00-0x7F,它是UTF-8的ASCII兼容子集。

编辑:顺便说一下,我猜测mb_detect_encoding()"没有正常工作的原因"是因为您的Latin-1编码文件仅使用ASCII兼容子集,该子集在UTF-8中也有效。毫无疑问,mb_detect_encoding()会将其标记为UTF-8并且它是#34;正确",如果数据只是纯ASCII,则答案UTF-8与拉丁语一样好-1,或ASCII,或任何无数的扩展ASCII编码。

答案 2 :(得分:0)

这将检测字符串的一部分是否是正式有效的UTF-8序列,忽略一个代码单元编码字符(表示ASCII中的代码点)。要使该函数返回true,只要有一个字符看起来像非ASCII UTF-8编码字符就足够了。

答案 3 :(得分:0)

这可能不是您的问题的答案(也许是,请参阅下面的更新),但它可能是您的问题的答案。查看我的Encoding类,它具有将字符串转换为UTF8的方法,无论它们是否已经用Latin1,Win1252或UTF8编码,或者是它们的混合:

Encoding::toUTF8($text_or_array);
Encoding::toWin1252($text_or_array);
Encoding::toISO8859($text_or_array);

// fixes UTF8 strings converted to UTF8 repeatedly: 
//  "FÃÂédÃÂération" to "Fédération"
Encoding::fixUTF8($text_or_array);  

https://stackoverflow.com/a/3479832/290221

该函数逐字节运行,并确定每个函数是否需要转换。

更新

稍微考虑一下,实际上这可能是你问题的答案:

require_once('Encoding.php');

function validUTF8($string){
    return Encoding::toUTF8($string) == $string;
}

这是Encoding类: https://github.com/neitanod/forceutf8

答案 4 :(得分:0)

基本上没有。

  • 任何 UTF8字符串都是有效的8位编码字符串(即使它产生乱码)。
  • 另一方面,具有扩展(128+)字符的大多数 8位编码字符串有效UTF8,但是,与任何其他随机字节序列一样,它们可能恰好是。
  • 并且,当然,任何ASCII文本 都是有效的UTF8,所以mb_detect_encoding实际上是正确的。不,使用ASCII文本作为UTF8不会有任何问题。这就是UTF8首先运作的原因。

据我所知,你提供的函数不检查字符串的有效性,只是它包含一些恰好类似于UTF8的序列,因此这个函数可能会失败更糟糕。您可能希望在严格模式下同时使用此功能 mb_detect_encoding,并希望它们能够抵消彼此的误报。

如果文本是用非拉丁字母书写的,检测多字节编码的“智能”方法是从相同的位开始查找大小相等的字节块序列。例如,俄语单词“привет”看起来像这样:

11010000 10111111
11010001 10000000
11010000 10111000
11010000 10110010
11010000 10110101
11010001 10000010

然而,这对于基于拉丁语的字母表(可能还有中文)不起作用。

答案 5 :(得分:0)

有问题的功能(用户pilif在链接问题中发布的功能)似乎取自PHP手册中mb_detect_encoding()页面上的this comment

正如作者所述,该功能仅用于检查字符串是否包含 UTF-8字符"并且它只查找UTF-8范围内的"非ascii多字节序列"。因此,如果您的字符串只包含简单的ascii字符(如英文文本),则该函数返回false(实际为零),这可能不是您想要的。

他的函数基于同一页面上this previous comment中的另一个函数,实际上,它是用来检查字符串是否为UTF-8并且基于W3C某人创建的this regular expression

这是原始的,正确工作的(我已经测试过的)函数,它将告诉您字符串是否为UTF-8:

// Returns true if $string is valid UTF-8 and false otherwise.
function is_utf8($string) {

    // From http://w3.org/International/questions/qa-forms-utf-8.html
    return preg_match('%^(?:
          [\x09\x0A\x0D\x20-\x7E]            # ASCII
        | [\xC2-\xDF][\x80-\xBF]             # non-overlong 2-byte
        |  \xE0[\xA0-\xBF][\x80-\xBF]        # excluding overlongs
        | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}  # straight 3-byte
        |  \xED[\x80-\x9F][\x80-\xBF]        # excluding surrogates
        |  \xF0[\x90-\xBF][\x80-\xBF]{2}     # planes 1-3
        | [\xF1-\xF3][\x80-\xBF]{3}          # planes 4-15
        |  \xF4[\x80-\x8F][\x80-\xBF]{2}     # plane 16
    )*$%xs', $string);

} // function is_utf8