检测UTF-8数据类型,包括3字节或4字节的Unicode。

8

我的数据库出现了错误

com.mysql.jdbc.MysqlDataTruncation: Data truncation: Data too long for column

我使用Java和MySQL 5。据我所知,4字节Unicode在Java中是合法的,但在MySQL 5中是非法的。我认为这可能会导致我的问题,并且我想检查数据类型,因此我的问题是: 如何检查UTF-8数据是3字节还是4字节Unicode?


我建议首先查看您的列数据类型长度限制和要插入的数据大小。如果您将100K个字符插入到VARCHAR中,则没有编码错误。 - Jon
我不认为4字节的UTF-8编码字符是这个问题的原因。更有可能的原因是一个n字符的字符串,当以UTF-8编码时占用了m个字节(其中m>n),但应该放入一个VARCHAR(n)中。 - Joachim Sauer
@Jon 我先检查了一下,那不是问题。我已经解决了它,但我仍然认为将来我会使用编码检查,谢谢大家的帮助。 - akuzma
@JoachimSauer FYI,MySQL将VARCHAR(n) CHARSET utf8视为一个N*3字节结构,因此这不太可能是问题所在。问题很可能是OP试图插入过多的数据。 - Christopher Schultz
3个回答

18

UTF-8 编码将基本多文种平面(即U+0000到U+FFFF,包括在内)的所有内容编码为1-3个字节。 因此,您只需要检查您的字符串中的每个字符 是否 在BMP中。

在Java中,这意味着检查任何char(它是一个UTF-16代码单元)是否是高代理或低代理字符,因为Java将使用代理对来编码非BMP字符:

public static boolean isEntirelyInBasicMultilingualPlane(String text) {
    for (int i = 0; i < text.length(); i++) {
        if (Character.isSurrogate(text.charAt(i))) {
            return false;
        }
    }
    return true;
}

10

如果您不想支持 BMP 之外的字符,您可以在交给 MySQL 之前删除这些字符:

public static String withNonBmpStripped( String input ) {
    if( input == null ) throw new IllegalArgumentException("input");
    return input.replaceAll("[^\\u0000-\\uFFFF]", "");
}

如果您想支持 BMP 以外的字符集,您需要 MySQL 5.5+ 并且您需要将所有使用的 utf8 改为 utf8mb4 (包括排序规则、字符集等)。但是,您还需要检查我不熟悉的驱动程序中是否支持该字符集。在 Java 中处理这些字符也很麻烦,因为它们分散在 2 个 chars 中,因此在许多操作中需要特殊处理。


这实际上效果不佳,因为正则表达式在代码点而非代码单元级别进行评估。你需要匹配范围 \u0000-\uFFFF 之外的字符(请参阅我的答案)。 - verglor
@jako512 这很令人惊讶,因为其他所有内容都涉及代码单元:I 我已经编辑过它,使其可以处理完整的非BMP字符,但原始版本的意图也是要删除不成对的代理项。 - Esailija
请注意,REGEX 可能需要针对您的语言进行微调。对于 PHP,请使用 preg_replace('/[^\x{0000}-\x{FFFF}]/u', '\x{FFFD}', $input); - DOOManiac
\uF000 - \uFFFF 的 UTF8 序列仍然可以被你的正则表达式接受,但是只用来组成 4 字节字符。所以我使用较小的范围 \u0000 - \uEFFF 来删除所有 4 字节字符。 - nrc

6

在Java中,去除非BMP字符的最佳方法是我在这里找到的:

inputString.replaceAll("[^\\u0000-\\uFFFF]", "\uFFFD");

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接