我想确保我对UTF-8的了解都是正确的。我已经尝试使用UTF-8一段时间了,但是我不断遇到越来越多的错误和其他怪异的东西,使得拥有100%UTF-8网站似乎几乎是不可能的。我似乎总是想念某个地方。也许有人可以改正我的清单或确定清单,所以我不会错过任何重要事项。
数据库
每个站点都必须将数据存储在某个地方。无论您使用什么PHP设置,都必须配置数据库。如果无法访问配置文件,请确保在连接后立即 设置 “ SET NAMES’utf8’ ”。另外,请确保在所有表上使用utf8_ unicode_ ci。假设MySQL用于数据库,则您将不得不更改其他数据库。
正则表达式
我做了很多正则表达式,比您的平均搜索替换更为复杂。我必须记住使用“ / u”修饰符,以便PCRE不会破坏我的字符串。但是,即使那样,显然仍然存在问题]。
字符串函数
所有默认的字符串函数(strlen(),strpos()等)都应替换为查看字节而不是字节的多字节字符串函数。
标头 您应确保服务器为浏览器返回正确的标头,以了解您要使用的字符集(就像必须告诉MySQL一样)。
header(’Content-Type:text / html; charset = utf-8’);
将正确的标记放在页面标题中也是一个好主意。尽管实际的标头会覆盖此标头,但它们应该不同。
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
问题
是否需要在页面加载时将从用户代理(HTML表单和URI)接收到的所有内容转换为UTF-8,或者是否可以将字符串/值保持原样并通过这些函数运行它们而不会出现问题? ?
如果我确实需要将所有内容都转换为UTF-8,那么我应该采取什么步骤?mb_detect_encoding似乎是为此目的而构建的,但我一直看到人们抱怨它并不总是有效。mb_check_encoding似乎也很难从格式错误的字符串中分辨出一个好的UTF-8字符串。
PHP是否根据使用的编码方式(例如文件类型)以不同的方式将字符串存储在内存中,还是仍然像常规的字符串一样存储字符串,并且某些字符的解释方式有所不同(例如&amp; vs和HTML中的)。 chazomaticus回答了这个问题:
在PHP中(无论如何,最高为PHP5),字符串只是字节序列。没有与之关联的隐式或显式字符集;这是程序员必须跟踪的东西。
如果将非UTF-8字符串提供给mb_ *函数,是否会引起问题?
如果UTF字符串编码不正确,会出问题(例如正则表达式中的解析错误吗?),还是只是将实体标记为错误的(html)?是否有可能因为字符串不正确而对字符串的编码不正确而导致函数返回FALSE吗?
我听说您也应该将表格标记为UTF-8(accept-charset =“ UTF-8”),但是我不确定这样做有什么好处。
是否编写了UTF-16以解决UTF-8中的限制?就像UTF-8的字符空间用完了吗?(Y2(UTF)k?)
功能
这是我找到的几个自定义PHP函数,但是我没有任何方法可以验证它们是否确实有效。也许有人举了一个我可以使用的例子。首先是convertToUTF8(),然后是来自wordpress的似乎screw_utf8。
function seems_utf8($str) { $length = strlen($str); for ($i=0; $i < $length; $i++) { $c = ord($str[$i]); if ($c < 0x80) $n = 0; # 0bbbbbbb elseif (($c & 0xE0) == 0xC0) $n=1; # 110bbbbb elseif (($c & 0xF0) == 0xE0) $n=2; # 1110bbbb elseif (($c & 0xF8) == 0xF0) $n=3; # 11110bbb elseif (($c & 0xFC) == 0xF8) $n=4; # 111110bb elseif (($c & 0xFE) == 0xFC) $n=5; # 1111110b else return false; # Does not match any model for ($j=0; $j<$n; $j++) { # n bytes matching 10bbbbbb follow ? if ((++$i == $length) || ((ord($str[$i]) & 0xC0) != 0x80)) return false; } } return true; } function is_utf8($str) { $c=0; $b=0; $bits=0; $len=strlen($str); for($i=0; $i<$len; $i++){ $c=ord($str[$i]); if($c > 128){ if(($c >= 254)) return false; elseif($c >= 252) $bits=6; elseif($c >= 248) $bits=5; elseif($c >= 240) $bits=4; elseif($c >= 224) $bits=3; elseif($c >= 192) $bits=2; else return false; if(($i+$bits) > $len) return false; while($bits > 1){ $i++; $b=ord($str[$i]); if($b < 128 || $b > 191) return false; $bits--; } } } return true; }
页面加载时,是否需要将我从用户代理收到的所有内容(HTML表单和URI)转换为UTF-8?
否。用户代理应该以UTF-8格式提交数据;否则,您将失去Unicode的优势。
确保用户代理以UTF-8格式提交的方法是提供包含以UTF-8编码提交的表单的页面。使用Content- Type标头(如果要保存表单并独立工作,也可以使用meta http-equiv)。
我听说您也应该将表单标记为UTF-8(accept-charset =“ UTF-8”)
别。 在HTML标准中,这是一个不错的主意,但是IE却没有做到这一点。本来应该声明允许的字符集的排他性列表,但是IE将其视为按字段尝试的其他字符集的列表。因此,如果您有ISO-8859-1页面和“ accept-charset =“ UTF-8””表单,则IE首先会尝试将字段编码为ISO-8859-1,如果非8859-1字符, 然后 使用UTF-8。
但是由于IE不会告诉您它是否使用过ISO-8859-1或UTF-8,因此这绝对对您没有用。您将不得不分别针对每个字段猜测正在使用哪种编码!没用。忽略该属性,并将您的页面作为UTF-8服务;这是您目前可以做的最好的事情。
如果UTF字符串编码不正确,将会出问题
如果让这样的顺序进入浏览器,您可能会遇到麻烦。有些“超长序列”会以比所需长度更长的字节序列来编码低编号的代码点。这意味着,如果要通过按字节顺序查找ASCII字符来过滤“ <”,则可能会漏掉一个,并将脚本元素放入您认为是安全的文本中。
在Unicode成立之初,就禁止使用过长的序列,但是微软花了很长时间才把它们弄得一团糟:IE直到IE6 Service Pack 1才将字节序列’\ xC0 \ xBC’解释为’<’。 Opera甚至在版本7上也弄错了。幸运的是,这些较旧的浏览器正在逐渐消失,但是仍然值得过滤掉冗长的序列,以防那些浏览器仍然存在(或者新的白痴浏览器将来会犯同样的错误) )。你可以这样做,并修复等不良序列,用正则表达式只允许适当的UTF-8通过,比如这一个从W3。
如果在PHP中使用mb_函数,则 可能 会遇到这些问题。我不能肯定地说,因为当我还在写PHP时mb_ *是不可用的脆弱。
无论如何,这也是删除控制字符的好时机,控制字符是大量的错误源,通常是不被重视的。除了W3正则表达式删除的其他字符外,我还将从提交的字符串中删除字符9和13。对于您不应该将其视为多行文本框的字符串,也可以删除普通的换行符。
是否编写了UTF-16以解决UTF-8中的限制?
不,UTF-16是每代码点两个字节的编码,用于使索引Unicode字符串在内存中更容易(从所有Unicode都可以容纳两个字节的时代开始;像Windows和Java这样的系统仍然可以这样做)。与UTF-8不同,它与ASCII不兼容,并且在Web上几乎没有用。但是您有时会在保存的文件中遇到它,通常是Windows用户保存的文件,这些文件被Windows在另存为菜单中将UTF-16LE描述为“ Unicode”而误导了。
seen_utf8
与正则表达式相比,这是非常低效的!
另外,请确保在所有表上使用utf8_unicode_ci。
实际上,您可以在这种情况下摆脱困境,将MySQL视为只存储字节的存储,而在脚本中仅将它们解释为UTF-8。使用utf8_unicode_ci的优点是它将使用有关非ASCII字符的知识来整理(排序并进行不区分大小写的比较)。“ŕ”和“Ŕ”是相同的字符。如果使用非UTF8归类,则应坚持二进制(区分大小写)匹配。
无论您选择哪种方式,都必须始终如一地进行操作:对表使用与连接相同的字符集。您要避免的是脚本和数据库之间的有损字符集转换。