PHP:在不知道原始字符集的情况下将任何字符串转换为UTF-8,或者至少尝试

Gri*_*... 143 php utf-8 character-encoding

我有一个应用程序来处理来自世界各地的客户,当然,我希望进入我的数据库的所有内容都是UTF-8编码的.

对我来说主要的问题是我不知道任何字符串的来源是什么编码 - 它可以来自文本框(使用<form accept-charset="utf-8">仅在用户实际提交表单时才有用),或者它可能是从上传的文本文件,所以我真的无法控制输入.

我需要的是一个函数或类,它确保进入我的数据库的内容尽可能采用UTF-8编码.我已经尝试iconv(mb_detect_encoding($text), "UTF-8", $text); 但是有问题(如果输入是'fiancée'它返回'fianc').我尝试了很多东西= /

对于文件上传,我喜欢要求最终用户指定他们使用的编码,并向他们展示输出结果的预览,但这对于讨厌讨厌的黑客没有帮助(事实上,它可以使他们的生活更容易一点).

我已经阅读了关于这个主题的其他SO问题,但它们似乎都有微妙的差异,例如"我需要解析RSS提要"或"我从网站上搜集数据"(或者,实际上,"你不能").

但必须有一些东西至少有一个好的尝试!

Jef*_*Day 244

你所要求的是非常困难的.如果可能,让用户指定编码是最好的.防止攻击不应该更容易或更难.

但是,您可以尝试这样做:

iconv(mb_detect_encoding($text, mb_detect_order(), true), "UTF-8", $text);
Run Code Online (Sandbox Code Playgroud)

将其设置为严格可能会帮助您获得更好的结果.

  • 请查看php发行版中的`mb_detect_encoding`源代码(这里有一个地方:ext/mbstring/libmbfl/mbfl/mbfl_ident.c).此功能根本无法正常工作.对于某些编码,它甚至"返回真实",哈哈.其他人在Ctrl + c Ctrl + v函数中.那是因为如果没有某种字典或统计方法(比如我的话),你就无法检测编码. (5认同)
  • 根据我的理解,`mb_detect_encoding` 遍历提供的编码列表,并接受字符串中没有无效字节序列的第一个......对于没有无效字节序列的编码,例如 ISO-8859-1,这总是真的。没有“智能”启发式方法,结果会因您传递的编码列表(和顺序)而异。 (2认同)

Oro*_*102 29

在祖国俄罗斯,我们有4种流行的编码,所以你的问题在这里有很大的需求.

只有符号的char代码才能检测到编码,因为代码页相交.一些不同语言的代码页甚至有完整的交集.所以,我们需要另一种方法.

使用未知编码的唯一方法是使用概率.所以,我们不想回答"这个文本的编码是什么?"这个问题,我们试图理解" 这个文本最有可能的编码是什么? ".

俄罗斯科技博客中的一个人发明了这种方法:

在您想要支持的每个编码中构建字符代码的概率范围.你可以使用你的语言中的一些大文本来构建它(例如,一些小说,使用莎士比亚的英语和托尔斯泰的俄语,哈哈).你会得到像这样的smth:

    encoding_1:
    190 => 0.095249209893009,
    222 => 0.095249209893009,
    ...
    encoding_2:
    239 => 0.095249209893009,
    207 => 0.095249209893009,
    ...
    encoding_N:
    charcode => probabilty
Run Code Online (Sandbox Code Playgroud)

下一个.您可以使用未知编码的文本和"概率词典"中的每个编码来搜索未知编码文本中每个符号的频率.符号的概率概率.具有更高评级的编码可能是赢家.更大的文本更好的结果.

如果您有兴趣,我很乐意帮助您完成这项任务.我们可以通过构建两个charcodes概率列表来大大提高准确性.

顺便说一句.mb_detect_encoding certanly不起作用.是的,完全没有.请查看"ext/mbstring/libmbfl/mbfl/mbfl_ident.c"中的mb_detect_encoding源代码.


Ale*_*mov 11

你可能已经尝试过了,但为什么不使用mb_convert_encoding函数呢?它将尝试自动检测所提供文本的字符集,或者您可以将其传递给列表.

另外,我试图运行:

$text = "fiancée";
echo mb_convert_encoding($text, "UTF-8");
echo "<br/><br/>";
echo iconv(mb_detect_encoding($text), "UTF-8", $text);
Run Code Online (Sandbox Code Playgroud)

两者的结果都是一样的.你如何看待你的文字被截断为'fianc'?是在数据库中还是在浏览器中?


mat*_*lie 5

无法识别完全准确的字符串的字符集.有办法尝试猜测字符集.mb_detect_encoding()是其中一种方式,也许是目前最好的PHP方式.这将扫描您的字符串并查找某些字符集特有的内容.根据您的字符串,可能没有这种可区分的事件.

采用ISO-8859-1 charset vs ISO-8859-15(http://en.wikipedia.org/wiki/ISO/IEC_8859-15#Changes_from_ISO-8859-1)

只有少数几个不同的角色,更糟糕的是,它们由相同的字节表示.在没有知道编码的情况下,没有办法检测到字符串被赋予字符串0xA4是否应该表示字符串中的¤或€,因此无法知道它的确切字符集.

(注意:你可以添加一个人为因素,或者更高级的扫描技术(例如Oroboros102建议的那样),试图根据周围环境弄清楚,如果角色应该是¤或€,尽管这看起来像一座桥梁太远)

在例如UTF-8和ISO-8859-1之间存在更多可区别的差异,因此当您不确定时,仍然值得尝试解决它,尽管您可以而且绝不应该依赖它是正确的.

有趣的阅​​读:http://kore-nordmann.de/blog/php_charset_encoding_FAQ.html#how-do-i-determine-the-charset-encoding-of-a-string

还有其他方法可以确保正确的字符集.关于表单,尝试尽可能强制执行UTF-8(请查看雪人以确保您在每个浏览器中都提交UTF-8:http://intertwingly.net/blog/2010/07/29/Rails-and -Snowmen)这样做,至少你可以肯定通过你的表格提交的每个文本都是utf_8.关于上传的文件,尝试通过例如exec()(如果可能在您的服务器上)运行unix'file -i'命令以帮助检测(使用文档的BOM).关于抓取数据,您可以读取HTTP头,通常指定charset.解析XML文件时,请查看XML元数据是否包含charset定义.

您应该首先尝试在可能的情况下自己确定某个字符集,或者在尝试检测之前尝试从您获取它的源(如果适用)中获取定义,而不是尝试自动猜测字符集.