检测编码,并使所有的UTF-8

我正在从各种RSS源中读出大量文本,并将它们插入到我的数据库中。

当然,Feed中使用了几种不同的字符编码,例如UTF-8和ISO-8859-1。

不幸的是,文本的编码有时会出现问题。 例:

1)“Fußball”中的“ß”在我的数据库中应该是这样的:“Ÿ”。 如果是“Ÿ”,则显示正确。

2)有时,“Fußball”中的“ß”在我的数据库中是这样的:“ß”。 那当然是显示错误了。

3)在其他情况下,“ß”被保存为“ß” – 所以没有任何改变。 然后它也显示错误。

我能做些什么来避免案例2和3?

我怎样才能使所有的编码相同,最好是UTF-8? 何时必须使用utf8_encode(),何时必须使用utf8_decode()(显然是什么效果,但何时必须使用函数?),什么时候必须对input做什么?

你能帮助我,告诉我如何使一切相同的编码? 也许与函数mb-detect-encoding()? 我可以为此写一个函数吗? 所以我的问题是:1)如何找出文本使用什么编码2)如何将其转换为UTF-8 – 无论旧编码是什么

编辑:这样的function会工作吗?

function correct_encoding($text) { $current_encoding = mb_detect_encoding($text, 'auto'); $text = iconv($current_encoding, 'UTF-8', $text); return $text; } 

我已经testing过,但不起作用。 它出什么问题了?

如果将utf8_encode()应用于已经是UTF8的string,它将返回一个乱码的UTF8输出。

我提出了解决所有这些问题的function。 这就是所谓的Encoding :: toUTF8()。

你不需要知道你的string的编码是什么。 它可以是Latin1(ISO 8859-1),Windows-1252或UTF8,或者string可以混合使用。 Encoding :: toUTF8()将把所有东西都转换成UTF8。

我这样做是因为一个服务给了我一个混乱的数据源,在同一个string中混合使用UTF8和Latin1。

用法:

 require_once('Encoding.php'); use \ForceUTF8\Encoding; // 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(),它将修复看起来乱码的每个UTF8string。

用法:

 require_once('Encoding.php'); use \ForceUTF8\Encoding; // 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()。

你首先必须检测使用了什么编码。 在parsingRSS提要(可能通过HTTP)时,应该从Content-Type HTTP头字段的charset参数中读取编码。 如果不存在,请从XML处理指令的encoding属性读取编码。 如果缺less,则使用规范中定义的UTF-8 。


编辑这里是我可能会做的:

我会用cURL发送和获取响应。 这使您可以设置特定的头字段并获取响应头。 获取响应之后,您必须parsingHTTP响应并将其分解为标题和正文。 然后,头部应该包含Content-Type头部字段,该头部字段包含MIMEtypes和(希望) 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, "\r\n\r\n"); $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('/^<\?xml\s+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通过猜测来工作,基于你通过它的一些候选人。 在某些编码中,某些字节序列是无效的,因此它可以区分不同的候选者。 不幸的是,有很多的编码,其中相同的字节有效(但不同)。 在这些情况下,没有办法确定编码; 在这些情况下,你可以实现自己的逻辑来猜测。 例如,来自日文站点的数据可能更有可能具有日文编码。

只要你只处理西欧语言,三种主要的编码就是utf-8iso-8859-1cp-1252 。 由于这些是许多平台的默认设置,因此它们也是最可能被错误报告的。 例如。 如果人们使用不同的编码,他们可能会坦率地说,因为否则他们的软件会经常中断。 因此,一个好的策略是信任提供者,除非编码被报告为三者之一。 你仍然应该使用mb_check_encoding (注意有效不同于 – 对于许多编码,相同的input可能是有效的) mb_check_encoding它确实是有效的。 如果是其中之一,则可以使用mb_detect_encoding来区分它们。 幸运的是,这是相当确定的。 您只需要使用正确的检测序列,即UTF-8,ISO-8859-1,WINDOWS-1252

一旦你检测到编码,你需要将其转换为你的内部表示( UTF-8是唯一的理智的select)。 函数utf8_encodeISO-8859-1转换为UTF-8 ,所以它只能用于特定的inputtypes。 对于其他编码,请使用mb_convert_encoding

这个备忘录列出了一些与PHP中的UTF-8处理相关的常见警告: http : //developer.loftdigital.com/blog/php-utf-8-cheatsheet

这个函数检测string中的多字节字符也可能有帮助( 来源 ):

 function detectUTF8($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); } 

在php.net上可以find一个很好的实现isUTF8函数的方法:

 function isUTF8($string) { return (utf8_encode(utf8_decode($string)) == $string); } 

有一点点,你说在数据库中“ß”应该显示为“Ÿ”。

这可能是因为你正在使用拉丁文字符编码数据库或可能您的php-mysql连接设置错误,这是PHP相信你的mysql设置为使用utf-8,所以它发送数据为utf8,但你的MySQL php正在发送编码为iso-8859-1的数据,所以它可能会再次尝试将你发送的数据编码为utf-8,造成这种麻烦。

看看这个,可以帮助你: http : //php.net/manual/en/function.mysql-set-charset.php

你的编码看起来像你编码成UTF-8 两次 ; 也就是从其他编码转换成UTF-8,再转换成UTF-8。 就好像你有iso-8859-1,从iso-8859-1转换为utf-8,并把新string作为iso-8859-1处理,再转换成UTF-8。

以下是你所做的一些伪代码:

 $inputstring = getFromUser(); $utf8string = iconv($current_encoding, 'utf-8', $inputstring); $flawedstring = iconv($current_encoding, 'utf-8', $utf8string); 

你应该试试:

  1. 检测使用mb_detect_encoding()或任何你喜欢使用的编码
  2. 如果是UTF-8,则转换为iso-8859-1,然后重复步骤1
  3. 最后,转换回UTF-8

假设在“中间”转换中使用了iso-8859-1。 如果你使用windows-1252,那么转换成windows-1252(latin1)。 原始的源代码不重要。 你用的有缺陷的,第二次转换是。

这是我的猜测, 除了一个扩展的ASCII字节之外,还有很less的事情可以完成四个字节。

德语也使用iso-8859-2和windows-1250(latin2)。

您需要testinginput的字符集,因为响应可以用不同的编码进行编码。
我强制所有的内容被发送到UTF-8使用以下function进行检测和翻译:

 function fixRequestCharset() { $ref = array( &$_GET, &$_POST, &$_REQUEST ); foreach ( $ref as &$var ) { foreach ( $var as $key => $val ) { $encoding = mb_detect_encoding( $var[ $key ], mb_detect_order(), true ); if ( !$encoding ) continue; if ( strcasecmp( $encoding, 'UTF-8' ) != 0 ) { $encoding = iconv( $encoding, 'UTF-8', $var[ $key ] ); if ( $encoding === false ) continue; $var[ $key ] = $encoding; } } } } 

该例程将把来自远程主机的所有PHPvariables转换为UTF-8。
如果无法检测或转换编码,则忽略该值。
您可以根据自己的需求进行定制。
在使用variables之前调用它。

很简单:当你得到的东西不是UTF8时,你必须对INTO UTF8进行编码。

所以,当你提取一个ISO-8859-1的feed时,通过utf8_encodeparsing它。

但是,如果您要获取UTF8 Feed,则无需执行任何操作。

制定RSS源的字符编码似乎很复杂 。 即使是正常的网页也经常忽略,或者说谎言,他们的编码。

所以你可以尝试使用正确的方法来检测编码,然后回到某种forms的自动检测(猜测)。

我知道这是一个老问题,但我认为一个有用的答案永远不会伤害。 我在桌面应用程序,SQLite和GET / POSTvariables之间遇到了问题。 有些是UTF-8,有些是ASCII码的,当涉及到外国人时,基本上所有的东西都会搞砸。

这是我的解决scheme。 它会在处理之前刷新每个页面加载的GET / POST / REQUEST(我省略了Cookie,但是可以添加它们)。 它在标题中运作良好。 如果PHP不能自动检测源代码,PHP会发出警告,所以这些警告被@所抑制。

 //Convert everything in our vars to UTF-8 for playing nice with the database... //Use some auto detection here to help us not double-encode... //Suppress possible warnings with @'s for when encoding cannot be detected try { $process = array(&$_GET, &$_POST, &$_REQUEST); while (list($key, $val) = each($process)) { foreach ($val as $k => $v) { unset($process[$key][$k]); if (is_array($v)) { $process[$key][@mb_convert_encoding($k,'UTF-8','auto')] = $v; $process[] = &$process[$key][@mb_convert_encoding($k,'UTF-8','auto')]; } else { $process[$key][@mb_convert_encoding($k,'UTF-8','auto')] = @mb_convert_encoding($v,'UTF-8','auto'); } } } unset($process); } catch(Exception $ex){} 

关于mb_detect_encodingmb_convert_encoding的一个有趣的事情是,你build议的编码顺序很重要:

 // $input is actually UTF-8 mb_detect_encoding($input, "UTF-8", "ISO-8859-9, UTF-8"); // ISO-8859-9 (WRONG!) mb_detect_encoding($input, "UTF-8", "UTF-8, ISO-8859-9"); // UTF-8 (OK) 

所以你可能想在指定预期的编码时使用特定的顺序。 不过,请记住,这不是万无一失的。

自AGES以来,我正在检查编码解决scheme,而这个页面可能是多年search的结果! 我testing了你提到的一些build议,这里是我的笔记:

这是我的testingstring:

这是一个“wròngwrìtten”stringbùt我nèed到pù'sòme'特殊chàrs看到thèm,由fùnctìon转换! & 而已!

我做一个INSERT来将这个string保存在一个设置为utf8_general_ci的字段中的数据库上

我的页面的字符集是UTF-8

如果我这样做INSERT,在我的数据库中我有一些可能来自火星的字符…所以我需要将它们转换成一些“理智的”UTF-8。 我试过utf8_encode()但仍然外国人的字符入侵我的数据库…

所以我试图使用functionforceUTF8张贴在第8号,但在数据库string保存看起来像这样:

这是一个“wróngngwrôtten”的string,我没有去看看这个特殊的地方,看到了这个地方,转换成了fènctône! & 而已!

因此,在这个页面上收集更多的信息,并将它们与其他页面上的其他信息合并,我解决了这个解决scheme的问题:

 $finallyIDidIt = mb_convert_encoding( $string, mysql_client_encoding($resourceID), mb_detect_encoding($string) ); 

现在在我的数据库中,我有正确的编码我的string。

注:只有注意照顾是函数mysql_client_encoding ! 您需要连接到数据库,因为此function需要资源ID作为参数。

但是,我只是在INSERT之前重新编码,所以对我来说不是问题。

我希望这会帮助像这个页面的人帮助我!

感谢大家!

毛罗

mb_detect_encoding

 echo mb_detect_encoding($str, "auto"); 

要么

 echo mb_detect_encoding($str, "UTF-8, ASCII, ISO-8859-1"); 

我真的不知道结果是什么,但我build议你只是采取不同的编码你的一些饲料,并尝试如果mb_detect_encoding工程或不。

更新
auto是“ASCII,JIS,UTF-8,EUC-JP,SJIS”的缩写。 它返回检测到的字符集,你可以使用iconv将string转换为utf-8。

 <?php function convertToUTF8($str) { $enc = mb_detect_encoding($str); if ($enc && $enc != 'UTF-8') { return iconv($enc, 'UTF-8', $str); } else { return $str; } } ?> 

我没有testing过,所以没有保证。 也许有一个更简单的方法。

@harpax为我工作。 就我而言,这足够好了:

 if (isUTF8($str)) { echo $str; } else { echo iconv("ISO-8859-1", "UTF-8//TRANSLIT", $str); } 

在整理你的PHP脚本之后,不要忘记告诉mysql你传递了什么字符集,并且想要接受。

例如:设置字符集utf8

将utf8数据传递给latin1 I / O会话中的latin1表将给出令人讨厌的birdfeets。 我每隔一天在oscommerce商店看到这个。 回到第四,它可能看起来是正确的。 但是phpmyadmin会显示真相。 通过告诉mysql你传递的是什么字符集,它将为你处理mysql数据的转换。

如何恢复现有的乱码mysql数据是另一个讨论的话题。 🙂

该版本适用于德语,但可以修改$ CHARSETS和$ TESTCHARS

 class CharsetDetector { private static $CHARSETS = array( "ISO_8859-1", "ISO_8859-15", "CP850" ); private static $TESTCHARS = array( "€", "ä", "Ä", "ö", "Ö", "ü", "Ü", "ß" ); public static function convert($string) { return self::__iconv($string, self::getCharset($string)); } public static function getCharset($string) { $normalized = self::__normalize($string); if(!strlen($normalized))return "UTF-8"; $best = "UTF-8"; $charcountbest = 0; foreach (self::$CHARSETS as $charset) { $str = self::__iconv($normalized, $charset); $charcount = 0; $stop = mb_strlen( $str, "UTF-8"); for( $idx = 0; $idx < $stop; $idx++) { $char = mb_substr( $str, $idx, 1, "UTF-8"); foreach (self::$TESTCHARS as $testchar) { if($char == $testchar) { $charcount++; break; } } } if($charcount>$charcountbest) { $charcountbest=$charcount; $best=$charset; } //echo $text."<br />"; } return $best; } private static function __normalize($str) { $len = strlen($str); $ret = ""; for($i = 0; $i < $len; $i++){ $c = ord($str[$i]); if ($c > 128) { if (($c > 247)) $ret .=$str[$i]; elseif ($c > 239) $bytes = 4; elseif ($c > 223) $bytes = 3; elseif ($c > 191) $bytes = 2; else $ret .=$str[$i]; if (($i + $bytes) > $len) $ret .=$str[$i]; $ret2=$str[$i]; while ($bytes > 1) { $i++; $b = ord($str[$i]); if ($b < 128 || $b > 191) {$ret .=$ret2; $ret2=""; $i+=$bytes-1;$bytes=1; break;} else $ret2.=$str[$i]; $bytes--; } } } return $ret; } private static function __iconv($string, $charset) { return iconv ( $charset, "UTF-8" , $string ); } } 

从头获取编码并将其转换为utf-8。

 $post_url='http://website.domain'; /// Get headers //////////////////////////////////////////////////////////// function get_headers_curl($url) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HEADER, true); curl_setopt($ch, CURLOPT_NOBODY, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_TIMEOUT, 15); $r = curl_exec($ch); return $r; } $the_header = get_headers_curl($post_url); /// check for redirect ///////////////////////////////////////////////// if (preg_match("/Location:/i", $the_header)) { $arr = explode('Location:', $the_header); $location = $arr[1]; $location=explode(chr(10), $location); $location = $location[0]; $the_header = get_headers_curl(trim($location)); } /// Get charset ///////////////////////////////////////////////////////////////////// if (preg_match("/charset=/i", $the_header)) { $arr = explode('charset=', $the_header); $charset = $arr[1]; $charset=explode(chr(10), $charset); $charset = $charset[0]; } /////////////////////////////////////////////////////////////////////////////// // echo $charset; if($charset && $charset!='UTF-8') { $html = iconv($charset, "UTF-8", $html); } 

Ÿ是Mojibakeß。 在你的数据库中,你可能有hex

 DF if the column is "latin1", C39F if the column is utf8 -- OR -- it is latin1, but "double-encoded" C383C5B8 if double-encoded into a utf8 column 

你不应该在PHP中使用任何编码/解码function。 相反,您应该正确设置数据库和连接。

如果涉及MySQL,请参阅: 使用utf8字符的问题; 我看到的不是我所存储的

我在这里find解决办法http://deer.org.ua/2009/10/06/1/

 class Encoding { /** * http://deer.org.ua/2009/10/06/1/ * @param $string * @return null */ public static function detect_encoding($string) { static $list = ['utf-8', 'windows-1251']; foreach ($list as $item) { try { $sample = iconv($item, $item, $string); } catch (\Exception $e) { continue; } if (md5($sample) == md5($string)) { return $item; } } return null; } } $content = file_get_contents($file['tmp_name']); $encoding = Encoding::detect_encoding($content); if ($encoding != 'utf-8') { $result = iconv($encoding, 'utf-8', $content); } else { $result = $content; } 

我认为@是不好的决定,并从deer.org.ua做一些解决scheme,

最投票答案不起作用。 这是我的,希望它有帮助。

 function toUTF8($raw) { try{ return mb_convert_encoding($raw, "UTF-8", "auto"); }catch(\Exception $e){ return mb_convert_encoding($raw, "UTF-8", "GBK"); } } 

当您尝试处理日语和韩语等多种语言时,可能会遇到麻烦。 使用'auto'参数的mb_convert_encoding不能正常工作。 设置mb_detect_order('ASCII,UTF-8,JIS,EUC-JP,SJIS,EUC-KR,UHC')没有帮助,因为它会错误地检测到EUC- *。

我总结说,只要inputstring来自HTML,它应该使用meta元素中的“字符集”。 我使用简单的HTML DOMparsing器,因为它支持无效的HTML。

下面的代码片段从网页中提取标题元素。 如果你想转换整个页面,那么你可能要删除一些行。

 <?php require_once 'simple_html_dom.php'; echo convert_title_to_utf8(file_get_contents($argv[1])), PHP_EOL; function convert_title_to_utf8($contents) { $dom = str_get_html($contents); $title = $dom->find('title', 0); if (empty($title)) { return null; } $title = $title->plaintext; $metas = $dom->find('meta'); $charset = 'auto'; foreach ($metas as $meta) { if (!empty($meta->charset)) { // html5 $charset = $meta->charset; } else if (preg_match('@charset=(.+)@', $meta->content, $match)) { $charset = $match[1]; } } if (!in_array(strtolower($charset), array_map('strtolower', mb_list_encodings()))) { $charset = 'auto'; } return mb_convert_encoding($title, 'UTF-8', $charset); } 

我有与phpQueryISO-8859-1而不是UTF-8 )相同的问题,这个黑客帮助我:

 $html = '<?xml version="1.0" encoding="UTF-8" ?>' . $html; 

mb_internal_encoding('UTF-8')phpQuery::newDocumentHTML($html, 'utf-8')mbstring.internal_encoding和其他操作没有任何效果。

尝试没有“自动”

那是:

 mb_detect_encoding($text) 

代替:

 mb_detect_encoding($text, 'auto') 

更多的信息可以在这里find: mb_detect_encoding