如何处理用户输入的无效 UTF-8 字符

2022-08-30 12:15:07

我正在寻找有关如何处理用户无效的UTF-8输入的一般策略/建议。

即使我的 Web 应用程序使用 UTF-8,某些用户也会以某种方式输入无效字符。这会导致PHP的json_encode()出现错误,总的来说,这似乎是一个坏主意。

W3C I18N 常见问题解答:多语言表单显示“如果收到非 UTF-8 数据,则应发回错误消息。

  • 在一个拥有数十个可以输入数据的不同位置的站点中,这究竟应该如何实际完成?
  • 如何以有用的方式向用户呈现错误?
  • 如何临时存储和显示不良表单数据,以便用户不会丢失所有文本?剥离坏字符?使用替换字符,如何使用?
  • 对于数据库中的现有数据,当检测到无效的 UTF-8 数据时,我是否应该尝试将其转换并保存回去(如何?utf8_encode()?mb_convert_encoding()?),还是在数据库中保持原样,但在json_encode()之前做一些事情(什么?)?

我非常熟悉mbstring扩展,而不是问“UTF-8在PHP中是如何工作的?我希望有实际经验的人如何处理这个问题。

作为解决方案的一部分,我真的希望看到一种将无效字符转换为U + FFFD的快速方法。


答案 1

该属性只是浏览器要遵循的准则,他们不会被迫以这种方式提交。蹩脚的表单提交机器人就是一个很好的例子...accept-charset="UTF-8"

我通常忽略坏字符,无论是通过iconv()还是使用不太可靠的utf8_encode()/ utf8_decode()函数。如果使用 ,您还可以选择音译坏字符。iconv

下面是一个使用的示例:iconv()

$str_ignore = iconv('UTF-8', 'UTF-8//IGNORE', $str);
$str_translit = iconv('UTF-8', 'UTF-8//TRANSLIT', $str);

如果你想向你的用户显示一条错误消息,我可能会以全局方式而不是按每个接收值的方式执行此操作。像这样的东西可能会做得很好:

function utf8_clean($str)
{
    return iconv('UTF-8', 'UTF-8//IGNORE', $str);
}

$clean_GET = array_map('utf8_clean', $_GET);

if (serialize($_GET) != serialize($clean_GET))
{
    $_GET = $clean_GET;
    $error_msg = 'Your data is not valid UTF-8 and has been stripped.';
}

// $_GET is clean!

您可能还希望规范化新行并去除(非)可见的控制字符,如下所示:

function Clean($string, $control = true)
{
    $string = iconv('UTF-8', 'UTF-8//IGNORE', $string);

    if ($control === true)
    {
            return preg_replace('~\p{C}+~u', '', $string);
    }

    return preg_replace(array('~\r\n?~', '~[^\P{C}\t\n]+~u'), array("\n", ''), $string);
}

要从 UTF-8 转换为 Unicode 码位的代码:

function Codepoint($char)
{
    $result = null;
    $codepoint = unpack('N', iconv('UTF-8', 'UCS-4BE', $char));

    if (is_array($codepoint) && array_key_exists(1, $codepoint))
    {
        $result = sprintf('U+%04X', $codepoint[1]);
    }

    return $result;
}

echo Codepoint('à'); // U+00E0
echo Codepoint('ひ'); // U+3072

可能比任何其他替代方案都快,但我还没有对其进行广泛的测试。


例:

$string = 'hello world�';

// U+FFFEhello worldU+FFFD
echo preg_replace_callback('/[\p{So}\p{Cf}\p{Co}\p{Cs}\p{Cn}]/u', 'Bad_Codepoint', $string);

function Bad_Codepoint($string)
{
    $result = array();

    foreach ((array) $string as $char)
    {
        $codepoint = unpack('N', iconv('UTF-8', 'UCS-4BE', $char));

        if (is_array($codepoint) && array_key_exists(1, $codepoint))
        {
            $result[] = sprintf('U+%04X', $codepoint[1]);
        }
    }

    return implode('', $result);
}

这可能就是你要找的。


答案 2

从 Web 应用程序接收无效字符可能与为 HTML 表单假定的字符集有关。您可以指定要用于具有 accept-charset 属性的表单的字符集:

<form action="..." accept-charset="UTF-8">

您可能还想看看Stack Overflow上的类似问题,以获取有关如何处理无效字符的指针,例如,右侧列中的无效字符,但我认为向用户发出错误信号比尝试清理那些导致重要数据意外丢失或用户输入意外更改的无效字符更好。


推荐