【问题标题】:How to replace/remove 4(+)-byte characters from a UTF-8 string in Java?如何从 Java 中的 UTF-8 字符串中替换/删除 4(+)字节字符?
【发布时间】:2012-03-04 20:57:51
【问题描述】:

因为 MySQL 5.1 不支持 4 字节 UTF-8 序列,我需要替换/删除这些字符串中的 4 字节序列。

我正在寻找一种干净的方法来替换这些字符。

Apache 库在这种情况下用问号替换字符很好,当然 ASCII 等价物会更好。

注意输入来自外部来源(电子邮件名称),目前升级数据库不是解决方案。

【问题讨论】:

  • 你在开玩笑。 MySQL 在这个时代仍然不支持 Unicode?这是不合情理的。在只能处理 1、2 或 3 字节 UTF-8 序列时假装支持 Unicode,就像在只支持 1 字节 ASCII 序列时说支持 Unicode 一样大谎言。您要么支持任何合法的 Unicode 代码点,要么不支持 Unicode。这是一个二进制的东西。听起来 MySQL 不支持 Unicode。请告诉我这是个玩笑。
  • @tchrist:MySQL 5.5.3 及更高版本通过新的“utf8mb4”“字符集”(dev.mysql.com/doc/refman/5.5/en/charset-unicode.html)支持正确的 UTF-8。但是,“utf8”“字符集”最多只支持 3 字节的 UTF-8 多字节字符,据说是为了防止不同 MySQL 版本之间的复制问题。在未来的 MySQL 版本中,“utf8”可能会更改为“utf8mb4”的别名。
  • this question 类似,但它请求的是 PHP 而不是 Java 的解决方案。
  • @pvgoddin 哪个 Apache lib 正在用问号替换字符?

标签: java mysql utf-8


【解决方案1】:

5 字节 utf-8 序列以 111110xx 字节开头,6 字节 utf-8 序列以 1111110x 字节开头。需要注意的重要一点是,1-4 字节 utf-8 序列的后续字节不包含那么大的字节,因为后续字节始终采用 10xxxxxx 的形式。

因此,您可以只浏览字节,每次看到 111110xx 类型的字节时,只发出一个“?”到输出流/数组,同时从输入中跳过接下来的 4 个字节; 6字节序列的类比。

【讨论】:

  • 5 和 6 字节序列在 UTF-8 中是无效的——但这并不是说它们不能出现在源文本中。
  • 如果 5 和 6 字节序列不合法,它们(应该)不会成为问题。我的问题目前是 4 字节序列,这些序列是合法的,但 mysql 支持 nog。
  • 哎哟。您将问题更改为考虑 4 字节序列而不是 5/6。现在人们会认为我的回答是错误的。也许您可以改回您的问题并为 4 字节的情况创建一个新问题?谢谢!
【解决方案2】:

我们最终在 Java 中为这个问题实现了以下方法。 基本上用比最后一个 3byte UTF-8 字符更高的代码点替换字符。

偏移量计算是为了确保我们保持在 unicode 代码点上。

public static final String LAST_3_BYTE_UTF_CHAR = "\uFFFF";
public static final String REPLACEMENT_CHAR = "\uFFFD"; 

public static String toValid3ByteUTF8String(String s)  {
    final int length = s.length();
    StringBuilder b = new StringBuilder(length);
    for (int offset = 0; offset < length; ) {
       final int codepoint = s.codePointAt(offset);

       // do something with the codepoint
       if (codepoint > CharUtils.LAST_3_BYTE_UTF_CHAR.codePointAt(0)) {
           b.append(CharUtils.REPLACEMENT_CHAR);
       } else {
           if (Character.isValidCodePoint(codepoint)) {
               b.appendCodePoint(codepoint);
           } else {
               b.append(CharUtils.REPLACEMENT_CHAR);
           }
       }
       offset += Character.charCount(codepoint);
    }
    return b.toString();
}

【讨论】:

  • 谢谢。我用它来避免转换我的整个 MySQL 字符集。我的数据中不需要外星人字符或便便字符。
  • LAST_3_BYTE_UTF_CHAR 不应该是charint 类型吗?否则codepoint &gt; CharUtils.LAST_3_BYTE_UTF_CHAR 甚至不应该编译。
【解决方案3】:

另一个简单的解决方案是使用正则表达式[^\u0000-\uFFFF]。例如在java中:

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

【讨论】:

    猜你喜欢
    • 2012-01-19
    • 1970-01-01
    • 2013-05-05
    • 1970-01-01
    • 2018-01-23
    • 1970-01-01
    • 2019-04-30
    • 2013-12-22
    • 1970-01-01
    相关资源
    最近更新 更多