【问题标题】:How to find a character which can’t be stored in a MySQL “utf8” column in Java如何在 Java 中查找无法存储在 MySQL“utf8”列中的字符
【发布时间】:2019-06-28 04:04:34
【问题描述】:

我使用 MySQL 5.7,我有一个表,其中有一列使用“utf8”字符集。不幸的是,它不是 utf8mb4,因此当我的应用尝试插入超出“utf8”范围的字符(例如表情符号)时,我总是会收到错误消息。

很遗憾,我不能很快将字符集更改为“utf8mb4”,所以我想知道是否有可能在将错误插入表格之前检测到那些导致错误发生的字符,并让我们的客户知道他们不能使用它们。

我在某处读到 U+0000 到 U+FFFF 范围之外的任何内容都会导致错误发生。我的应用程序是在 Java 8 中实现的。所以,我的问题是:如何编写可以从 String 实例中找到此类有问题的字符的代码? the following code 做我想做的事吗?

import java.util.Set;
import java.util.stream.Collectors;

class Utf8Mb3Validator {

    /**
     * finds characters which can’t be stored in a MySQL “utf8” column out of a given String.
     *
     * @param input a String which you want to check
     * @return a Set which contains strings that can't be inserted into MySQL "utf8" columns
     */
    Set<String> findProblematicStrings(String input) {
        // References:
        // https://dev.mysql.com/doc/refman/5.7/en/charset-unicode-utf8mb3.html
        // https://www.oracle.com/technetwork/java/javase/downloads/supplementary-142654.html?printOnly=1
        // https://stackoverflow.com/q/56800767/3591946
        return input
                .codePoints() // get Unicode code points
                .filter(codePoint -> Character.charCount(codePoint) > 1) // search for non-BMP characters
                .mapToObj(codePoint -> new String(Character.toChars(codePoint))) // convert code points into Strings
                .collect(Collectors.toSet());
    }
}

我也把这个问题发到了 MySQL 论坛:https://forums.mysql.com/read.php?39,675862,675862#msg-675862

【问题讨论】:

标签: java mysql unicode


【解决方案1】:

事实上,当时 MySQL 的 utf8 是正确的,因为 UTF-8 多字节序列最多只有 3 个字节。但是 Unicode 有更多的符号,而且 UTF-8 也在增长。而且只有utf8mb4可以做到。

不过最多 3 个字节都可以:

return input
      .codePoints()
      .filter(codePoint -> codePoint >= 256) // Optional heuristic optimisation
      .mapToObj(codePoint -> new String(Character.toChars(codePoint)))
      .filter(cpString -> cpString.getBytes(StandardCharsets.UTF_8).length > 3)
      .collect(Collectors.toSet())

或者干脆全部codepoints above U+FFFF:

return input
      .codePoints()
      .filter(codePoint -> codePoint >= 0x1_0000)
      .mapToObj(codePoint -> new String(Character.toChars(codePoint)))
      .collect(Collectors.toSet());

老实说,我需要研究是否也可以使用 Character.charCount(codePoint),因为它检查的是 UTF-16 中的代理对,而不是 UTF-8 中的字节数。

有用的可能是Character.getName(codePoint) 来替换代码点(如果字段有足够长的大小)。

【讨论】:

【解决方案2】:

如果 Java 中有一种方法可以生成 UTF-8 编码字符串的十六进制表示,则在字符串中搜索 F0 字节。

如果 Java 中有一种方法可以生成 UTF-16 编码字符串的 16 位表示,则搜索包含 D8xx-DFFF 值的任何 16 位。

(指出一些这样的方法,也许我可以详细说明。)

【讨论】:

    猜你喜欢
    • 2011-11-18
    • 2013-01-22
    • 2012-07-13
    • 1970-01-01
    • 2012-09-06
    • 1970-01-01
    • 2019-09-05
    • 1970-01-01
    • 2011-11-29
    相关资源
    最近更新 更多