【问题标题】:remove non-UTF-8 characters from xml with declared encoding=utf-8 - Java使用声明的 encoding=utf-8 从 xml 中删除非 UTF-8 字符 - Java
【发布时间】:2011-02-21 13:57:55
【问题描述】:

我必须用 Java 处理这种情况:

我收到了来自客户端的 XML 格式的请求,其中声明了 encoding=utf-8。不幸的是,它可能不包含 utf-8 字符,并且需要从我这边的 xml 中删除这些字符(旧版)。

让我们考虑一个示例,其中这个无效的 XML 包含 £(英镑)。

1) 我将 xml 作为 java 字符串获取,其中包含 £(我现在无法访问接口,但我可能将 xml 作为 java 字符串获取)。我可以使用 replaceAll(£, "") 摆脱这个角色吗?有什么潜在的问题吗?

2) 我将 xml 作为字节数组 - 在这种情况下如何安全地处理此操作?

【问题讨论】:

  • 您的问题令人困惑。磅是有效的 UTF-8 字符。此外,UTF-8 几乎涵盖了世界已知的每个字符。你可以发布一些真实世界的例子吗?你不是说要去掉非 ASCII 字符吗?
  • 我猜你得到的 XML 声称是 UTF-8,但实际上是 Windows-1252、ISO 8859-1 左右。这会使任何非 ASCII 字符无效,因为它的编码错误。是明确要求删除这些字符,还是修复 XML 错误(您/他们认为是通过删除有问题的字符来完成的)?如果是后者,您应该能够在解析之前将输入转换为 UTF-8,假设您的客户端始终使用相同(错误)的编码。我对 Java 了解的不够多,无法告诉你如何做到这一点。
  • 请注意,您需要使用 XML 工具执行此操作NOT,因为当输入不是 100% 正确时,解析器可以终止。

标签: java xml encoding utf-8


【解决方案1】:

1) 我将 xml 作为 java 字符串获取,其中包含 £(我现在无法访问接口,但我可能将 xml 作为 java 字符串获取)。我可以使用 replaceAll(£, "") 摆脱这个角色吗?

我假设您的意思是要删除非ASCII 字符,因为您说的是“遗留”方面。您可以使用以下正则表达式摆脱 printable ASCII range 之外的任何内容:

string = string.replaceAll("[^\\x20-\\x7e]", "");

2) 我将 xml 作为字节数组 - 在这种情况下如何安全地处理此操作?

您需要将byte[] 包装在ByteArrayInputStream 中,以便您可以使用InputStreamReader 在UTF-8 编码的字符流中读取它们,其中您指定编码,然后使用BufferedReader 读取它一行一行。

例如

BufferedReader reader = null;
try {
    reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(bytes), "UTF-8"));
    for (String line; (line = reader.readLine()) != null;) {
        line = line.replaceAll("[^\\x20-\\x7e]", "");
        // ...
    }
    // ...

【讨论】:

  • 非常感谢!!我的问题有所不同,但这结束了我两天的痛苦:)
  • 此答案已有 8 年历史,仍然有效!非常感谢!
【解决方案2】:

UTF-8 是一种编码; Unicode 是一个字符集。但是 GBP 符号绝对是在 Unicode 字符集中,因此肯定可以用 UTF-8 表示。

如果您实际上是指 UTF-8,并且您实际上是在尝试删除不是 UTF-8 中字符的有效编码的字节序列,那么...

CharsetDecoder utf8Decoder = Charset.forName("UTF-8").newDecoder();
utf8Decoder.onMalformedInput(CodingErrorAction.IGNORE);
utf8Decoder.onUnmappableCharacter(CodingErrorAction.IGNORE);
ByteBuffer bytes = ...;
CharBuffer parsed = utf8Decoder.decode(bytes);
...

【讨论】:

    【解决方案3】:
    "test text".replaceAll("[^\\u0000-\\uFFFF]", "");
    

    此代码从字符串中删除所有 4 字节 utf8 字符。在执行 Mysql innodb varchar 条目时,出于某些目的可能需要此代码

    【讨论】:

      【解决方案4】:

      我在从本地目录读取文件时遇到了同样的问题并尝试了这个:

      BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(filePath), "UTF-8"));
      DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
      Document xmlDom = db.parse(new InputSource(in));
      

      您可能必须使用网络输入流而不是 FileInputStream。

      -- 卡皮尔

      【讨论】:

        【解决方案5】:

        请注意,第一步应该是您在发送给您之前询问 XML 的创建者(这很可能是一个自制的“仅打印数据”XML 生成器)以确保他们的 XML 是正确的。如果他们使用 Windows,最简单的测试是让他们在 Internet Explorer 中查看它,并在第一个违规字符处查看解析错误。

        虽然他们修复了这个问题,但您可以简单地编写一个小程序来更改标头部分以声明编码为 ISO-8859-1:

        <?xml version="1.0" encoding="iso-8859-1" ?>
        

        其余部分保持不变。

        【讨论】:

          【解决方案6】:

          在 java 机器上将字节数组转换为 String 后,您将获得(在大多数机器上默认情况下)UTF-16 编码的字符串。摆脱非 UTF-8 字符的正确解决方案是使用以下代码:

          String[] values = {"\\xF0\\x9F\\x98\\x95", "\\xF0\\x9F\\x91\\x8C", "/*", "look into my eyes 〠.〠", "fkdjsf ksdjfslk", "\\xF0\\x80\\x80\\x80", "aa \\xF0\\x9F\\x98\\x95 aa"};
          for (int i = 0; i < values.length; i++) {
              System.out.println(values[i].replaceAll(
                              "[\\\\x00-\\\\x7F]|" + //single-byte sequences   0xxxxxxx
                              "[\\\\xC0-\\\\xDF][\\\\x80-\\\\xBF]|" + //double-byte sequences   110xxxxx 10xxxxxx
                              "[\\\\xE0-\\\\xEF][\\\\x80-\\\\xBF]{2}|" + //triple-byte sequences   1110xxxx 10xxxxxx * 2
                              "[\\\\xF0-\\\\xF7][\\\\x80-\\\\xBF]{3}" //quadruple-byte sequence 11110xxx 10xxxxxx * 3
                      , ""));
          }
          

          或者如果您想验证某个字符串是否包含非 utf8 字符,您可以使用 Pattern.matches,例如:

          String[] values = {"\\xF0\\x9F\\x98\\x95", "\\xF0\\x9F\\x91\\x8C", "/*", "look into my eyes 〠.〠", "fkdjsf ksdjfslk", "\\xF0\\x80\\x80\\x80", "aa \\xF0\\x9F\\x98\\x95 aa"};
          for (int i = 0; i < values.length; i++) {
              System.out.println(Pattern.matches(
                              ".*(" +
                              "[\\\\x00-\\\\x7F]|" + //single-byte sequences   0xxxxxxx
                              "[\\\\xC0-\\\\xDF][\\\\x80-\\\\xBF]|" + //double-byte sequences   110xxxxx 10xxxxxx
                              "[\\\\xE0-\\\\xEF][\\\\x80-\\\\xBF]{2}|" + //triple-byte sequences   1110xxxx 10xxxxxx * 2
                              "[\\\\xF0-\\\\xF7][\\\\x80-\\\\xBF]{3}" //quadruple-byte sequence 11110xxx 10xxxxxx * 3
                              + ").*"
                      , values[i]));
          }
          

          如果您有可用的字节数组,则可以使用以下方法更正确地过滤它们:

          BufferedReader bufferedReader = null;
          try {
              bufferedReader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(bytes), "UTF-8"));
              for (String currentLine; (currentLine = bufferedReader.readLine()) != null;) {
                  currentLine = currentLine.replaceAll(
                                  "[\\x00-\\x7F]|" + //single-byte sequences   0xxxxxxx
                                  "[\\xC0-\\xDF][\\x80-\\xBF]|" + //double-byte sequences   110xxxxx 10xxxxxx
                                  "[\\xE0-\\xEF][\\x80-\\xBF]{2}|" + //triple-byte sequences   1110xxxx 10xxxxxx * 2
                                  "[\\xF0-\\xF7][\\x80-\\xBF]{3}" //quadruple-byte sequence 11110xxx 10xxxxxx * 3
                          , ""));
              }
          

          要使整个网络应用程序与 UTF8 兼容,请阅读此处:
          How to get UTF-8 working in Java webapps
          More on Byte Encodings and Strings.
          您可以检查您的模式here
          在 PHP here 中也是如此。

          【讨论】:

            猜你喜欢
            • 2019-10-17
            • 2015-05-12
            • 2011-04-04
            • 1970-01-01
            • 2011-11-22
            • 1970-01-01
            • 2018-01-23
            • 2018-01-05
            • 2011-12-04
            相关资源
            最近更新 更多