【发布时间】:2010-11-21 23:24:43
【问题描述】:
我想确保我所知道的关于 UTF-8 的一切都是正确的。我一直在尝试使用 UTF-8 一段时间,但我不断发现越来越多的错误和其他奇怪的事情,这使得拥有 100% UTF-8 的网站似乎几乎是不可能的。总有一个我似乎想念的地方。也许这里有人可以更正我的列表或确定它,这样我就不会错过任何重要的事情。
数据库
每个站点都必须在某处存储数据。无论您的 PHP 设置是什么,您还必须配置数据库。如果您无法访问配置文件,请确保在连接后立即“SET NAMES 'utf8'”。此外,请确保在所有表格上使用utf8_ unicode_ ci。这假设 MySQL 用于数据库,您将不得不为其他数据库进行更改。
正则表达式
我做了很多 more complex 的正则表达式,而不是您的平均搜索替换。我必须记住使用“/u”修饰符,以便PCRE doesn't corrupt my strings。然而,即便如此,仍有still problems apparently。
字符串函数
所有默认的字符串函数(strlen()、strpos() 等)都应该替换为Multibyte String Functions,它查看的是字符而不是字节。
标题 您应该确保您的服务器为浏览器返回正确的标头,以了解您尝试使用的字符集(就像您必须告诉 MySQL 一样)。
header('内容类型: text/html; 字符集=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 & in 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 中的似乎_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;
}
如果有人感兴趣,我找到了一个很好的示例页面来使用when testing UTf-8。
【问题讨论】:
-
实际上,你有它倒退。编写 UTF-8 是为了解决 UTF-16 的问题。具体来说,UTF-16 要求每个字符占用 2 个字节(16 位),而我们美国程序员不喜欢这样,因为这意味着我们所有的文件都会翻倍,所以他们创建了 UTF-8,这是倒退的- 与 ASCII 兼容,因此所有纯 ASCII 文件都将在 UTF-8 中有效,从而省去了很多人将所有源代码文件从 ASCII 转换为 UTF-16 的麻烦。
-
我没有看到任何提及使用 mb_internal_encoding。您可能想调查一下,看看它是否与您相关。
-
对于 MySQL,不要手动调用
set names,因为它不会更新用于 real_escape_string 的字符集。请改用mysql_set_character_set。见dev.mysql.com/doc/refman/5.0/en/mysql-set-character-set.html 和stackoverflow.com/a/1317239/632951 -
@Pacerier,从大约 5 年前开始,没有人应该使用mysql_real_escape_string()。如果您仍在使用它,请尽快升级到 PDO。不建议手动引用字符串而不是使用准备好的语句。
-
@Xeoncross,见stackoverflow.com/q/26596294/632951