检测编码并使所有的UTF
我从各种RSS源中读出大量文本并将它们插入到我的数据库中。
当然,Feed中使用了几种不同的字符编码,例如UTF-8和ISO-8859-1。
不幸的是,文本的编码有时会出现问题。 例:
1)“Fußball”中的“ß”在我的数据库中应该如下所示:“Ÿ”。 如果是“Ÿ”,则显示正确。
2)有时,“Fußball”中的“ß”在我的数据库中看起来像这样:“ß”。 然后,它显示错误,当然。
3)在其他情况下,“ß”被保存为“ß” - 所以没有任何改变。 然后它也显示错误。
我能做些什么来避免案例2和3?
我怎样才能使所有的编码相同,最好是UTF-8? 何时必须使用utf8_encode(),何时必须使用utf8_decode()(显然是什么效果,但是何时必须使用这些函数?)以及什么时候必须对输入不做任何处理?
你能帮助我,告诉我如何使所有的编码都一样吗? 也许用函数mb-detect-encoding()? 我可以为此写一个函数吗? 所以我的问题是:1)如何找出文本使用什么编码2)如何将其转换为UTF-8 - 无论旧编码是什么
编辑:这样的功能会工作吗?
function correct_encoding($text) {
$current_encoding = mb_detect_encoding($text, 'auto');
$text = iconv($current_encoding, 'UTF-8', $text);
return $text;
}
我已经测试过,但不起作用。 它出什么问题了?
如果您将utf8_encode()
应用于已有的UTF8字符串,它将返回一个乱码的UTF8输出。
我提出了解决所有这些问题的功能。 它被称为Encoding::toUTF8()
。
你不需要知道你的字符串的编码是什么。 它可以是Latin1(iso 8859-1),Windows-1252或UTF8,或者字符串可以混合使用。 Encoding::toUTF8()
会将所有内容转换为UTF8。
我这样做是因为一个服务给了我所有混乱的数据源,在同一个字符串中混合使用UTF8和Latin1。
用法:
require_once('Encoding.php');
use ForceUTF8Encoding; // It's namespaced now.
$utf8_string = Encoding::toUTF8($utf8_or_latin1_or_mixed_string);
$latin1_string = Encoding::toLatin1($utf8_or_latin1_or_mixed_string);
下载:
https://github.com/neitanod/forceutf8
更新:
我已经包含了另一个函数, Encoding::fixUFT8()
,它将修复每个看起来乱码的UTF8字符串。
用法:
require_once('Encoding.php');
use ForceUTF8Encoding; // It's namespaced now.
$utf8_string = Encoding::fixUTF8($garbled_utf8_string);
例子:
echo Encoding::fixUTF8("Fédération Camerounaise de Football");
echo Encoding::fixUTF8("Fédération Camerounaise de Football");
echo Encoding::fixUTF8("FÃÂédÃÂération Camerounaise de Football");
echo Encoding::fixUTF8("Fédération Camerounaise de Football");
会输出:
Fédération Camerounaise de Football
Fédération Camerounaise de Football
Fédération Camerounaise de Football
Fédération Camerounaise de Football
更新:我已将函数( forceUTF8
)转换为一个名为Encoding
的类的静态函数族。 新函数是Encoding::toUTF8()
。
您首先必须检测已使用的编码。 在解析RSS提要(可能通过HTTP)时,您应该从Content-Type
HTTP头字段的charset
参数中读取编码。 如果它不存在,请从XML处理指令的encoding
属性中读取编码。 如果缺少,则使用规范中定义的UTF-8。
编辑这里是我可能会做的事情:
我会用cURL发送和获取响应。 这允许您设置特定的头字段并获取响应头。 获取响应后,您必须解析HTTP响应并将其分解为标头和正文。 然后头部应该包含Content-Type
头部字段,其中包含MIME类型和(有希望) charset
参数与编码/字符集。 如果不是,我们将分析XML PI的encoding
属性的存在并从那里获取编码。 如果这也没有,XML规范定义使用UTF-8作为编码。
$url = 'http://www.lr-online.de/storage/rss/rss/sport.xml';
$accept = array(
'type' => array('application/rss+xml', 'application/xml', 'application/rdf+xml', 'text/xml'),
'charset' => array_diff(mb_list_encodings(), array('pass', 'auto', 'wchar', 'byte2be', 'byte2le', 'byte4be', 'byte4le', 'BASE64', 'UUENCODE', 'HTML-ENTITIES', 'Quoted-Printable', '7bit', '8bit'))
);
$header = array(
'Accept: '.implode(', ', $accept['type']),
'Accept-Charset: '.implode(', ', $accept['charset']),
);
$encoding = null;
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HEADER, true);
curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
$response = curl_exec($curl);
if (!$response) {
// error fetching the response
} else {
$offset = strpos($response, "rnrn");
$header = substr($response, 0, $offset);
if (!$header || !preg_match('/^Content-Type:s+([^;]+)(?:;s*charset=(.*))?/im', $header, $match)) {
// error parsing the response
} else {
if (!in_array(strtolower($match[1]), array_map('strtolower', $accept['type']))) {
// type not accepted
}
$encoding = trim($match[2], '"'');
}
if (!$encoding) {
$body = substr($response, $offset + 4);
if (preg_match('/^<?xmls+version=(?:"[^"]*"|'[^']*')s+encoding=("[^"]*"|'[^']*')/s', $body, $match)) {
$encoding = trim($match[1], '"'');
}
}
if (!$encoding) {
$encoding = 'utf-8';
} else {
if (!in_array($encoding, array_map('strtolower', $accept['charset']))) {
// encoding not accepted
}
if ($encoding != 'utf-8') {
$body = mb_convert_encoding($body, 'utf-8', $encoding);
}
}
$simpleXML = simplexml_load_string($body, null, LIBXML_NOERROR);
if (!$simpleXML) {
// parse error
} else {
echo $simpleXML->asXML();
}
}
检测编码很困难。
mb_detect_encoding
根据你通过它的一些候选人猜测mb_detect_encoding
工作。 在某些编码中,某些字节序列无效,因此它可以区分各种候选。 不幸的是,有很多编码,其中相同的字节是有效的(但不同)。 在这些情况下,无法确定编码; 您可以在这些情况下实施自己的逻辑来猜测。 例如,来自日文站点的数据可能更有可能具有日文编码。
只要您只处理西欧语言,需要考虑的三种主要编码是utf-8
, iso-8859-1
和cp-1252
。 由于这些是许多平台的默认设置,因此它们也最有可能被错误地报告。 例如。 如果人们使用不同的编码,他们可能会坦率地说,因为否则他们的软件会经常中断。 因此,一个好的策略是信任提供者,除非编码被报告为三者之一。 你仍然应该使用mb_check_encoding
(请注意,有效不同于 - 相同的输入可能对许多编码有效) mb_check_encoding
它确实是有效的。 如果它是其中之一,则可以使用mb_detect_encoding
来区分它们。 幸运的是,这是相当确定性的; 你只需要使用正确的检测序列,即UTF-8,ISO-8859-1,WINDOWS-1252
。
检测到编码后,您需要将其转换为内部表示( UTF-8
是唯一的理智选择)。 函数utf8_encode
将ISO-8859-1
转换为UTF-8
,因此它只能用于特定的输入类型。 对于其他编码,请使用mb_convert_encoding
。