utf8_encode 和 mb_convert_encoding 冲突的 Resuts [复制]

utf8_encode and mb_convert_encoding Conflicting Resuts [duplicate]

提问人:raphie 提问时间:11/11/2023 最后编辑:raphie 更新时间:11/14/2023 访问量:83

问:

在将数据从 PHP(使用 )传输到 MySQL 时,我遇到了一个非常烦人的问题,在 web.config 设置为的 IIS 环境中default_charset = 'UTF-8'

<globalization requestEncoding="UTF-8" responseEncoding="UTF-8" />

还在具有相同默认字符集的 Apache 环境中进行了测试,结果相同。我还将MySQLi设置为在任何查询之前设置charset:

$mysqli->set_charset('UTF-8');

我还有一组准备好的语句,用于搜索字符串中的每个字符,以使用以下命令将任何无 UTF-8 编码转换为 UTF-8:

mb_convert_encoding($char, 'UTF-8', mb_detect_encoding($char));

尝试使用相同的过程解析整个字符串并失败后:

mb_convert_encoding($string, 'UTF-8', mb_detect_encoding($string));

带有脚本的文件的内容类型在标头上设置为内容类型字符集 UTF-8。但这些仍然没有解决问题。

我还将MySQL表和模式的所有排序规则设置为,当然还要使用'utf8mb4_unicode_520_ci'。尽管如此,问题仍然存在。utf8mb4

我编写的用于拆分字符串的函数如下:

    static private function split_and_convert($value, $encoding = 'UTF-8') {
        $split = mb_str_split($value);
        $filter = [];
        foreach($split as $chr) {
            $from = mb_detect_encoding($chr);
            $encoded = ($from !== $encoding)
                ? mb_convert_encoding($chr, $encoding, $from)
                : $chr;
            $filter[] = $encoded;
            // echoing for testing
            echo 'char ' . $encoded . ' (' . $from . ')<br>';
        }
        return join('', $filter);
    }

其中呼应以下试图保存:Martínez

char M (ASCII)
char a (ASCII)
char r (ASCII)
char t (ASCII)
char � (UTF-8)
char n (ASCII)
char e (ASCII)
char z (ASCII)

MySQL抛出以下错误:

Error No: 1366 - Incorrect string value: '\xEDnez' for column 'contactLast' at row 1

仍然没有喜悦。但如果我这样做,它就像一个魅力:

    static private function split_and_convert($value, $encoding = 'UTF-8') {
        $split = mb_str_split($value);
        $filter = [];
        foreach($split as $chr) {
            $from = mb_detect_encoding($chr);
            $encoded = ($from !== $encoding)
                ? mb_convert_encoding($chr, $encoding, $from)
                // set to actually encode the 'UTF-8' encoded char
                // I'm supposed not to do this, but is what works
                // which makes no sense
                : utf8_encode($chr);
            $filter[] = $encoded;
            // echoing for testing
            echo 'char ' . $encoded . ' (' . $from . ')<br>';
        }
        return join('', $filter);
    }

结果如下:

char M (ASCII)
char a (ASCII)
char r (ASCII)
char t (ASCII)
char í (UTF-8)
char n (ASCII)
char e (ASCII)
char z (ASCII)

并且MySQL不会抛出任何错误。如果我将 替换为 ,我会得到与不过滤字符串相同的错误。utf8_encodemb_convert_encoding($encoded, 'UTF-8')

我怀疑要么是错误地检测了字符的编码,要么只是mb_convert_encoding没有完成它的工作。mb_detect_encoding

如果有人能帮忙,我将不胜感激。我一直在寻找解决方案,但提供的解决方案都没有奏效。我发现自己陷入了困境,因为有效的方法已被弃用。

所以,这些是我面临的问题列表:

  1. mb_convert_encoding -> 未编码或编码为 UTF8 以外的内容
  2. mb_detect_encoding -> 未正确检测
  3. 字符串编码为 ASCII,而不是 UTF-8 作为请求标头中的字符集。
  4. 混淆:为什么我需要将 UTF-8 编码字符编码为 UTF-8 才能工作?
php mysql utf-8 utf8mb4 mb-convert-encoding

评论

2赞 Olivier 11/11/2023
我不明白你为什么要使用所有这些和.您不需要转换任何东西。mb_convert_encoding()mb_detect_encoding()
2赞 Olivier 11/11/2023
请注意,拆分是按字节而不是字符。str_split()
2赞 Sammitch 11/11/2023
utf8_encode()严格从 ISO-8859-1 转换为 UTF-8。 如果没有可选的第 3 个参数,则指定源编码将使用 PHP 配置中当前配置的默认编码。因此,如果前者有效,但后者无效,则配置的默认编码可能与输入字符串不匹配,您应该显式定义输入编码。此外,无法可靠地检测文本编码,并且声称这样做的函数是猜测。文本编码是您必须始终注意的元数据mb_convert_encoding()
0赞 raphie 11/11/2023
@Olivier这就是我问这个问题的原因。从理论上讲,如果字符或字符串已经是 UTF-8,则不需要过滤。理论上。我感到沮丧的原因是某些东西没有正确检测编码。请参阅最新的问题列表。
0赞 raphie 11/11/2023
@Sammitch这是一个很好的观点,但我遇到的问题是检测发现该字符是 UTF-8,而显然不是。我只是应用这些过滤器来测试这个理论,结果是真的。在我使用该功能的地方utf8_decode根本不需要。

答:

0赞 raphie 11/11/2023 #1

在寻找答案和一整天的研究之后,这就是问题所在。过度处理。在开发过程中的某个时候,中间件被放置到位,以验证每个 POST/GET 请求并处理每个值,以确保发送的编码是 UTF-8。我正在重构的代码已经负责处理数据。

中间件是使用 编码的,因此,我怀疑函数检查本身以避免过度处理。我还必须重构中间人,这样就不会发生冲突。utf8_encode

所以,这就是问题所在,两个不同的代码正在处理产生冲突的编码。一旦我重构了中间件,问题就停止了。

总之,问题不在于 或者,如果您有类似的问题,请确保您正在处理的代码没有使用两种不同的方法执行具有不同功能的类似过程。mb_convert_encodingmb_detect_encoding

就我而言,中间件没有验证或设置为检测字符串是否已编码为 UTF-8,它只是无论如何都实现了编码。这也是我因重构代码而获得报酬的原因。

对我来说仍然没有意义的是为什么正确的 UTF-8 编码字符串会导致 MySQL 抛出错误。为此,我怀疑过度处理正在损坏输入。

0赞 Rick James 11/11/2023 #2

ED是 的 拉丁 1 编码。您应该找到 latin1 中的编码内容,然后将其更改为 use(又名 MySQL's )或告诉 MySQL 您的数据在 并让它在存储 () 和检索 () 时进行转换。íUTF-8CHARACTER SET = utf8mb4CHARACTER SET latin1INSERTsSELECTs

不要使用任何转换例程 (mb_*),这往往会使混乱变得更糟。

acute-i 的 UTF-8 (utf8mb4) 编码为 .C3AD

有关更多讨论,请参阅Trouble with UTF-8 characters中的“black diamond”;我看到的不是我存储的

评论

0赞 raphie 11/14/2023
你是对的,但是问题的本质,í 已经被检测为 UTF-8,但是当按原样发送到 MySQL (utf8mb4) 时,它抛出了错误,这毫无意义,那么如果我通过 utf8_encode过滤已经检测到的 UTF 字符,则不会抛出错误。问题不在于 mb_* 函数,而在于过度处理字符集过滤的中间件,并且中间件正在使用 utf8_encode,而不管它是否已经是 UTF-8 字符集。