用Java从声明为UTF-8编码的XML中删除非UTF-8字符

21

我需要在Java中处理以下情况:

我从客户端以XML格式获取了一个请求,它声明使用utf-8编码。 不幸的是,它可能包含非utf-8字符,并且有要求在我的端上(遗留系统)删除这些字符。

让我们考虑一个包含 £ (英镑符号) 的无效 XML 的示例。

1)我得到带有 £ 的 java 字符串作为 xml(我现在没有接口访问权限,但我可能会获得 java 字符串形式的 xml)。 我可以使用 replaceAll(£, "") 来摆脱该字符吗? 是否存在任何潜在问题?

2)我将 xml 作为字节数组获取 - 如何安全地处理此操作?


3
您的问题有些令人困惑。英镑符号是一个有效的UTF-8字符。此外,UTF-8几乎包含了全世界已知的所有字符。您是否可以提供一些实际的例子?难道您的意思不是想去除非ASCII字符吗? - BalusC
2
我猜你得到的XML声称是UTF-8,但实际上是Windows-1252、ISO 8859-1或类似编码。这会使任何非ASCII字符无效,因为它们被错误地编码了。需求明确要求删除这些字符吗?还是修复XML错误(你/他们认为通过删除有问题的字符来完成)?如果是后者,在解析之前应该能够将输入转换为UTF-8,假设你的客户端始终使用相同的(错误的)编码。我不太了解Java,无法告诉你如何做到这一点。 - mercator
请注意,您需要使用非 XML 工具来完成此操作,因为当输入不是 100% 正确时,解析器可以终止。 - Thorbjørn Ravn Andersen
6个回答

35

1)我得到的XML是一个带有"£"符号的Java字符串(我现在没有接口访问权限,但我可能会得到一个Java字符串)。我可以使用replaceAll("£", "")来去掉这个字符吗?

我假设你的意思是想要去掉非ASCII字符,因为你谈论了一个“旧版”的问题。你可以使用以下正则表达式来去掉可打印ASCII范围之外的任何内容:

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

2) 我得到的XML是一个字节数组 - 在这种情况下如何安全处理此操作?

您需要使用ByteArrayInputStreambyte[]包装起来,以便您可以使用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]", "");
        // ...
    }
    // ...

非常感谢!我的问题不同,但这解决了我两天的痛苦 :) - HitchHiker
这个回答已经8年了,但仍然有效!非常感谢! - Digao

20

UTF-8是一种编码方式;Unicode是一个字符集。但英镑符号绝对在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);
...

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

这段代码从字符串中删除所有的4字节UTF8字符。在进行Mysql innodb varchar输入时可能需要这样做。


3

在读取本地目录中的文件时,我遇到了同样的问题并尝试了以下方法:

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

你可能需要使用网络输入流而不是FileInputStream。
- Kapil

2
请注意,第一步应该是询问XML的创建者(很可能是自制的“仅打印数据”XML生成器)确保他们的XML正确无误,然后再将其发送给您。如果他们使用Windows,则最简单的测试方法是要求他们在Internet Explorer中查看它,并查看第一个有问题的字符处的解析错误。
在他们修复之前,您可以编写一个小程序,将标题部分更改为声明编码为ISO-8859-1:
<?xml version="1.0" encoding="iso-8859-1" ?>

并且保留其余部分不变。


1
一旦在Java机器上将字节数组转换为字符串,您将获得(在大多数机器上默认)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
                , ""));
    }

如何使整个Web应用程序兼容UTF8,请阅读此处:
在Java Web应用程序中如何使UTF-8工作
有关字节编码和字符串的更多信息
您可以在此处检查您的模式这里
PHP中同样适用这里


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