【发布时间】:2010-11-19 23:00:36
【问题描述】:
我需要验证一些以 UTF-8 编码的用户输入。许多人建议使用以下代码:
preg_match('/\A(
[\x09\x0A\x0D\x20-\x7E]
| [\xC2-\xDF][\x80-\xBF]
| \xE0[\xA0-\xBF][\x80-\xBF]
| [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}
| \xED[\x80-\x9F][\x80-\xBF]
| \xF0[\x90-\xBF][\x80-\xBF]{2}
| [\xF1-\xF3][\x80-\xBF]{3}
| \xF4[\x80-\x8F][\x80-\xBF]{2}
)*\z/x', $string);
这是一个取自 http://www.w3.org/International/questions/qa-forms-utf-8 的正则表达式。一切都很好,直到我发现 PHP 中的一个错误似乎至少自 2006 年以来一直存在。如果 $string 太长,Preg_match() 会导致段错误。似乎没有任何解决方法。你可以在这里查看提交的bug:http://bugs.php.net/bug.php?id=36463
现在,为了避免使用 preg_match,我创建了一个与上述正则表达式完全相同的函数。我不知道这个问题在 Stack Overflow 上是否合适,但我想知道我所做的功能是否正确。这里是:
编辑 [13.01.2010]: 如果有人感兴趣,我发布的上一个版本中有几个错误。下面是我的函数的最终版本。
function check_UTF8_string(&$string) {
$len = mb_strlen($string, "ISO-8859-1");
$ok = 1;
for ($i = 0; $i < $len; $i++) {
$o = ord(mb_substr($string, $i, 1, "ISO-8859-1"));
if ($o == 9 || $o == 10 || $o == 13 || ($o >= 32 && $o <= 126)) {
}
elseif ($o >= 194 && $o <= 223) {
$i++;
$o2 = ord(mb_substr($string, $i, 1, "ISO-8859-1"));
if (!($o2 >= 128 && $o2 <= 191)) {
$ok = 0;
break;
}
}
elseif ($o == 224) {
$o2 = ord(mb_substr($string, $i + 1, 1, "ISO-8859-1"));
$o3 = ord(mb_substr($string, $i + 2, 1, "ISO-8859-1"));
$i += 2;
if (!($o2 >= 160 && $o2 <= 191) || !($o3 >= 128 && $o3 <= 191)) {
$ok = 0;
break;
}
}
elseif (($o >= 225 && $o <= 236) || $o == 238 || $o == 239) {
$o2 = ord(mb_substr($string, $i + 1, 1, "ISO-8859-1"));
$o3 = ord(mb_substr($string, $i + 2, 1, "ISO-8859-1"));
$i += 2;
if (!($o2 >= 128 && $o2 <= 191) || !($o3 >= 128 && $o3 <= 191)) {
$ok = 0;
break;
}
}
elseif ($o == 237) {
$o2 = ord(mb_substr($string, $i + 1, 1, "ISO-8859-1"));
$o3 = ord(mb_substr($string, $i + 2, 1, "ISO-8859-1"));
$i += 2;
if (!($o2 >= 128 && $o2 <= 159) || !($o3 >= 128 && $o3 <= 191)) {
$ok = 0;
break;
}
}
elseif ($o == 240) {
$o2 = ord(mb_substr($string, $i + 1, 1, "ISO-8859-1"));
$o3 = ord(mb_substr($string, $i + 2, 1, "ISO-8859-1"));
$o4 = ord(mb_substr($string, $i + 3, 1, "ISO-8859-1"));
$i += 3;
if (!($o2 >= 144 && $o2 <= 191) ||
!($o3 >= 128 && $o3 <= 191) ||
!($o4 >= 128 && $o4 <= 191)) {
$ok = 0;
break;
}
}
elseif ($o >= 241 && $o <= 243) {
$o2 = ord(mb_substr($string, $i + 1, 1, "ISO-8859-1"));
$o3 = ord(mb_substr($string, $i + 2, 1, "ISO-8859-1"));
$o4 = ord(mb_substr($string, $i + 3, 1, "ISO-8859-1"));
$i += 3;
if (!($o2 >= 128 && $o2 <= 191) ||
!($o3 >= 128 && $o3 <= 191) ||
!($o4 >= 128 && $o4 <= 191)) {
$ok = 0;
break;
}
}
elseif ($o == 244) {
$o2 = ord(mb_substr($string, $i + 1, 1, "ISO-8859-1"));
$o3 = ord(mb_substr($string, $i + 2, 1, "ISO-8859-1"));
$o4 = ord(mb_substr($string, $i + 3, 1, "ISO-8859-1"));
$i += 5;
if (!($o2 >= 128 && $o2 <= 143) ||
!($o3 >= 128 && $o3 <= 191) ||
!($o4 >= 128 && $o4 <= 191)) {
$ok = 0;
break;
}
}
else {
$ok = 0;
break;
}
}
return $ok;
}
是的,很长。我希望我已经正确理解了该正则表达式的工作原理。也希望对其他人有所帮助。
提前致谢!
【问题讨论】:
-
为什么要检查这么多特殊值?它可以简单得多。
-
我试图准确检查 W3C 的正则表达式正在检查的内容。
-
如果字符串不是有效的 UTF-8,你打算怎么做?有乱码的数据总比没有数据好?
标签: php regex validation utf-8