除了逐行预处理文件并替换这些字符外,是否有好的方法可以剥离所有这些字符?到目前为止,我遇到了三种不同的无效字符(0x5、0x6和0x7)。该数据库转储约有4GB大小,我们将对其进行多次处理,因此每次获取新的转储并运行预处理程序需要额外等待30分钟,这将很麻烦,而且我已经不是第一次遇到这个问题。
我使用了Xalan的org.apache.xml.utils.XMLChar
类:
public static String stripInvalidXmlCharacters(String input) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < input.length(); i++) {
char c = input.charAt(i);
if (XMLChar.isValid(c)) {
sb.append(c);
}
}
return sb.toString();
}
XMLChar#isValid()
会分别针对高位和低位返回false,但如果将它们组合起来是有效的,则会返回true。 - ankon我个人没有使用过这个工具,但Atlassian开发了一个命令行XML清理器,可能适合您的需要(主要是为JIRA开发的,但XML就是XML):
下载atlassian-xml-cleaner-0.1.jar
在DOS控制台或shell中定位计算机上的XML或ZIP备份文件,假定文件名为data.xml
运行: java -jar atlassian-xml-cleaner-0.1.jar data.xml > data-clean.xml
这将把data.xml的副本写入data-clean.xml,删除无效字符。
Pattern INVALID_XML_CHARS = Pattern.compile("[^\\u0009\\u000A\\u000D\\u0020-\\uD7FF\\uE000-\\uFFFD\uD800\uDC00-\uDBFF\uDFFF]");
...
INVALID_XML_CHARS.matcher(stringToCleanup).replaceAll("");
\x{10000}-\x{10FFFF}
来表示BMP之外的最后一个范围,而不是那个难以理解的符号\uD800\uDC00-\uDBFF\uDFFF
。当我将澳大利亚出口关税的内容解析成XML文档时,我遇到了类似的问题。我不能使用这里提出的解决方案,例如: - 使用从命令行调用的外部工具(一个jar)。 - 要求澳大利亚海关清理源文件。
目前唯一解决此问题的方法是逐个字符地迭代整个源文件的内容,并测试每个字符是否不属于ASCII范围0x00至0x1F(包括0x1F)。虽然可以做到,但我想知道是否有更好的方法使用Java字符串类型的方法。
编辑 我找到了一个解决方案,可能对他人有用:使用Java方法String#ReplaceAll替换或删除XML文档中的任何不良字符。
示例代码(我删除了一些必要的语句以避免混乱):
BufferedReader reader = null;
...
String line = reader.readLine().replaceAll("[\\x00-\\x1F]", "");
在这个例子中,我删除(即用一个空字符串替换)0x00到0x1F范围内的不可打印字符。您可以更改#replaceAll()方法中的第二个参数,以使用您的应用程序所需的字符串替换字符。
你的无效字符是否仅存在于值中而不是标签本身,即XML在概念上符合模式但值未经适当清理?如果是这样,那么考虑覆盖InputStream以创建一个CleansingInputStream,用其XML等效项替换无效字符。
你的问题与XML无关,而是与字符编码有关。归根结底,每个字符串,无论是XML还是其他,都由字节组成,除非告诉你该字符串具有什么字符编码,否则你无法知道这些字节表示哪些字符。例如,如果供应商告诉你它是UTF-8,但实际上是其他编码,那么你就会遇到问题。在最好的情况下,一切正常,但某些字节被转换为“错误”的字符。在最坏的情况下,你会遇到像你遇到的那样的错误。
实际上,你的问题甚至更糟:你的字符串包含不代表任何字符编码中的字符的字节序列。没有文本处理工具,更不用说XML解析器可以帮助你解决这个问题。这需要在字节级别进行清理。