【问题标题】:Will comparing the binary data of a string with an unknown character encoding validate what its encoding is?将字符串的二进制数据与未知字符编码进行比较是否会验证其编码是什么?
【发布时间】:2021-12-30 08:37:24
【问题描述】:

我需要自动从电子邮件内容和标题中确定字符串的字符编码。在大多数情况下,这不是问题,但偶尔会有一封包含内容和/或标题具有奇怪字符(例如 en dash)的电子邮件。现在我收到了一个答案,如果我在特定电子邮件的特定标头上对其进行静态测试,从技术上讲似乎可行,但是这公然忽略了导入电子邮件需要是一个完全自动化的过程这一事实,在这种情况下,我完全无法自动确定字符串的字符编码。

我已经从基本知识开始,例如检测似乎可以保证会出现字符编码问题的常见问题字符。然而,strpos('en dash: –', '–')有意/手动测试时工作正常,但直接添加到自动化流程时会彻底失败。我猜测问题在于字符串参数具有 UTF-8 编码,而自动化过程正在测试一个还不是 UTF-8 的字符串,因此在内部相同的字符没有使用相同的子集代码(通过字符编码)。

所以我的第二次尝试是mb_detect_encoding 的第二个参数可以是一个数组。所以我尝试了以下方法:

$encodings = array('UTF-8','UCS-4','UCS-4BE','UCS-4LE','UCS-2','UCS-2BE','UCS-2LE','UTF-32','UTF-32BE','UTF-32LE','UTF-16','UTF-16BE','UTF-16LE','UTF-7','UTF7-IMAP','ASCII','EUC-JP','SJIS','eucJP-win','SJIS-win','ISO-2022-JP','ISO-2022-JP-MS','CP932','CP51932','SJIS-mac','SJIS-Mobile#DOCOMO','SJIS-Mobile#KDDI','SJIS-Mobile#SOFTBANK','UTF-8-Mobile#DOCOMO','UTF-8-Mobile#KDDI-A','UTF-8-Mobile#KDDI-B','UTF-8-Mobile#SOFTBANK','ISO-2022-JP-MOBILE#KDDI','JIS','JIS-ms','CP50220','CP50220raw','CP50221','CP50222','ISO-8859-1','ISO-8859-2','ISO-8859-3','ISO-8859-4','ISO-8859-5','ISO-8859-6','ISO-8859-7','ISO-8859-8','ISO-8859-9','ISO-8859-10','ISO-8859-13','ISO-8859-14','ISO-8859-15','ISO-8859-16','byte2be','byte2le','byte4be','byte4le','BASE64','HTML-ENTITIES','7bit','8bit','EUC-CN','CP936','GB18030','HZ','EUC-TW','CP950','BIG-5','EUC-KR','UHC','ISO-2022-KR','Windows-1251','Windows-1252','CP866','KOI8-R','KOI8-U','ArmSCII-8');
$encoding = mb_detect_encoding($s, $encodings, true);
$compare = mb_convert_encoding($s, 'UTF-8', $encoding);

foreach ($encodings as $k1)
{
 if (mb_convert_encoding($s, 'UTF-8', $k1) === $s) {$encoding = $k1; break;}
}

不幸的是,基于我认为是相同的潜在问题,这似乎导致了同样的失败。

所以我的第三个想法是我正在寻找一些更有经验的验证。我可以将字符串转换为二进制形式(1 和 0,not 二进制数据)。然后我可以尝试转换字符串,然后将第二个字符串转换为二进制以比较两个二进制版本;如果他们=== 匹配,那么我可能已经确定了正确的字符编码?

现在我可以很容易地从一个不相关的线程中尝试这个with this answer,但是我不确定这是否是一个有效的想法。这都是为了回答我的问题:

如何确定字符串的实际字符编码,以便通过全自动验证将其转换为 UTF-8 而不会损坏数据?

通过验证,我说的是再次比较二进制数据之类的东西,但我不确定这是否是一种有效的方法。我知道我绝对讨厌破折号。

【问题讨论】:

    标签: php validation utf-8 character-encoding


    【解决方案1】:

    The answer won't change: it's impossible。您必须依赖外部信息对文本使用哪种编码。

    猜测编码可能会出错:

    • 根据您对其进行测试的顺序,结果可能是 ASCIIUTF-8Windows-1252,只是因为到目前为止它适合。您的列表是有问题的,因为它可能匹配 Base64 甚至不是 文本 编码。
    • 如果源本身没有正确编码,那么猜测其编码很可能会排除正确的编码。并且猜错了。这让事情变得更糟。
    • 许多编码共享相同的区域:源可以适合即 Windows-1252Windows-1251,即使检测文本的词汇意义也不能保证哪个两者都是正确的。

    另外:一和零二进制。 PHP strings are only byte arrays,所以它们一开始是二进制的。如何解释它们取决于您:如果您的代码是 $text= "グリーン";,那么取决于您的 PHP 文本文件的编码方式以及您的 PHP defaults 的设置方式。没有“internal ... character”,只有字节。这也是为什么有些函数对字节(即strlen())和特定文本编码(即mb_strlen())进行操作的原因。

    如果您讨厌单个字符:它们可以很容易地用作它们的本来面目:文本中的字符。与- 相比, 有其自身的有效含义;不要用个人意见代替它,因为这可能会破坏上下文的含义。这就像忽略 AΑA 都是不同的字符的事实。您可能想查找 homoglyphs and synoglyphs 之间的区别 - 后者是您当前的观点。

    您可能会问“PHP 以哪种编码方式解释脚本?”幸运的是,ASCII 对于大多数编码来说是最常见的分母,因此解释 a像这样的文件来搜索<?php(所有这些都是ASCII字符,所以对于PHP代码本身来说,它是有效的UTF-8还是ISO-8859-1Shift-JIS) 只会在文档以 UTF-16 编码时失败 - 在这种情况下,您必须设置您的PHP 默认使用该编码。这再次证明:必须在文本之外告知文本编码。

    【讨论】:

    • 我基本上是在问00000001 === 00000001,不知道你为什么在相反的情况下验证我的问题,就好像我在反对它一样。我也更喜欢保留原始字符,并且在过去一周改进了对 UTF-8 的转换,以按照作者的意图保留原始字符。
    • 在 PHP 中,前导零是octal literals - binary literals have the prefix 0b,即0b00000001。 “过去一周” - 所以,您的问题不再相关?
    • 您发布的内容很有用,尽管就像我说的那样,自从我发布问题以来,我已经设法在其他领域改进了对 UTF-8 的转换。
    猜你喜欢
    • 2021-06-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-17
    相关资源
    最近更新 更多