我有一些格式错误的XML,我必须解析。无法解决上游问题。
(当前)问题是&符号并不总是正确转义,因此我需要将&
转换为&
如果&
已经存在,我不想将其更改为&
。一般来说,如果任何格式良好的实体已经存在,我不想破坏它。我不认为通常可以知道可能出现在任何特定XML文档中的所有实体,所以我想要一个保留&<characters>;
之类的解决方案。
其中<characters>
是定义初始&
和结束;
之间的实体的一组字符。特别是,<
和>
是不文字,否则将表示XML元素。
现在,在解析时,如果我看到&<characters>
,我不知道我是否会遇到;
,(空格),行尾,或另一个
&
。所以我认为我必须记住<characters>
,因为我展望了一个可以告诉我如何处理原始&
的角色。
我认为我需要Push Down Automaton的强大功能才能做到这一点,我认为有限状态机不会因为我认为是内存需求而有效 - 这是正确的吗?如果我需要PDA,那么调用String.replaceAll(String, String)
时的正则表达式将无效。或者是否有可以解决此问题的Java正则表达式?
请记住:每行可能有多个替换。
(我知道this question,但它没有提供我正在寻找的答案。)
答案 0 :(得分:8)
以下是您要查找的正则表达式:&([^;\\W]*([^;\\w]|$))
,相应的替换字符串为&$1
。它在&
上匹配,后跟零个或多个非分号或分词符号(它需要允许零来匹配独立的&符号),然后是不的分词符号分号(或行结尾)。捕获组允许您使用您正在寻找的&
进行替换。
以下是一些使用它的示例代码:
String s = "& & &nsbp; &tc., &tc. &tc";
final String regex = "&([^;\\W]*([^;\\w]|$))";
final String replacement = "&$1";
final String t = s.replaceAll(regex, replacement);
在沙箱中运行后,我得到以下t:
的结果& & &nsbp; &tc., &tc. &tc
如您所见,原始&
和
保持不变。但是,如果您使用“&amp;&amp;”进行尝试,则会获得&&
,如果您使用“&amp;&amp;&amp;”进行尝试,则会获得&&&
,我将其视为您提到的前瞻性问题的症状。但是,如果您替换该行:
final String t = s.replaceAll(regex, replacement);
使用:
final String t = s.replaceAll(regex, replacement).replaceAll(regex, replacement);
它适用于所有这些字符串以及我能想到的任何其他字符串。 (在完成的产品中,您可能会编写一个例程来执行此replaceAll
次调用。)
答案 1 :(得分:5)
我认为你也可以使用前瞻来查看&
个字符是否后跟字符&amp;分号(例如&(?!\w+;)
)。这是一个例子:
import java.util.*;
import java.util.regex.*;
public class HelloWorld{
private static final Pattern UNESCAPED_AMPERSAND =
Pattern.compile("&(?!(#\\d+|\\w+);)");
public static void main(String []args){
for (String s : Arrays.asList(
"http://www.example.com/?a=1&b=2&c=3/",
"Three in a row: &&&",
"< is <, > is >, ' is ', etc."
)) {
System.out.println(
UNESCAPED_AMPERSAND.matcher(s).replaceAll("&")
);
}
}
}
// Output:
// http://www.example.com/?a=1&b=2&c=3/
// Three in a row: &&&
// < is <, > is >, ' is ', etc.
答案 2 :(得分:2)
首先了解实体周围的语法:http://www.w3.org/TR/xml/#NT-EntityRef
然后查看FilterInputStream
的JavaDoc:http://download.oracle.com/javase/6/docs/api/java/io/FilterInputStream.html
然后实现一个逐字符读取实际输入的。当它看到&符号时,它会切换到“实体模式”并查找有效的实体引用(& Name ;
)。如果它在Name
中不允许的第一个字符之前找到一个,则它会逐字地将其写入输出。否则,它会在&符号之后写入&
后面的所有内容。
答案 3 :(得分:1)
不要试图在所有可能的坏数据上做一些事情,而是一次一个地处理坏数据的出现。有可能生成XML的任何东西都会弄乱一两个字符,但不是所有的字符。这当然是假设。
尝试更换所有&amp;与&amp;除了&amp;之后是amp;。如果您遇到的下一个编码不正确的字符是&lt;,则将它们全部替换为&lt;。保持规则集小而易于管理,只处理你知道错误的事情。
如果您尝试做很多事情,最终可能会更换您不想要的内容并自行处理数据。
我只想指出,最好的解决方案是鼓励生成XML的人在其结束时修复编码。这可能很难问,但如果你专业地向他们解释他们没有生成有效的XML,他们可能愿意修复这些bug。这将有下一个必须消耗它的人的额外好处,不需要做一些疯狂的自定义代码来解决应该在源头解决的问题。至少考虑一下。可能发生的更糟糕的事情是,你问,他们说不,你就在现在的位置。
答案 4 :(得分:0)
很抱歉激起旧帖:
我遇到了同样的问题,我使用的解决方法分为3个步骤:
通过将实体包含在自定义字符序列中来完成隐藏。例如“#||<ENTITY_NAME>||#
”
为了说明,我们说这个带有未转义字符&
的XML代码段:
<NAME>Testname</NAME>
<VALUE>
random words one & two
I am sad&happy; at the same time!
its still < ecstatic
It is two & three words
Short form is 2&three
Now for some invalid entity refs: &, >, and < too.
</VALUE>
<强>步骤1:强>
我们使用正则表达式替换"[&]\(amp|apos|gt|lt|quot\)[;]"
和"#||$1||#"
。这是因为根据W3C的有效XML实体引用是 amp,lt,gt,apos&amp; QUOT 。
字符串现在看起来像这样:
<NAME>Testname</NAME>
<VALUE>
random words one #||amp||# two
I am sad&happy; at the same time!
its still #||lt||# ecstatic
It is two & three words
Short form is 2&three
Now for some invalid entity refs: &, >, and < too.
</VALUE>
只有有效的实体引用隐藏。 &happy;
未受影响。
<强>步骤2:强>
正则表达式是否将"[&]"
替换为"&"
。
字符串现在看起来像这样:
<NAME>Testname</NAME>
<VALUE>
random words one #||amp||# two
I am sad&happy; at the same time!
its still #||lt||# ecstatic
It is two & three words
Short form is 2&three
Now for some invalid entity refs: &amp, &gt, and &lt too.
</VALUE>
<强>步骤3:强>
正则表达式是否将"#\|\|([a-z]+)\|\|#"
替换为"&$1;"
。
最终的 更正后的 字符串现在如下所示:
<NAME>Testname</NAME>
<VALUE>
random words one & two
I am sad&happy; at the same time!
its still < ecstatic
It is two & three words
Short form is 2&three
Now for some invalid entity refs: &amp, &gt, and &lt too.
</VALUE>
的 缺点: 强>
必须仔细选择隐藏有效实体的自定义char序列,以确保没有有效内容偶然包含相同的序列。虽然机会很小,但承认,这不是一个完整的解决方案......
答案 5 :(得分:0)
我使用了上面的UNESCAPED_AMPERSAND
解决方案,但是我不得不将正则表达式更改为
private static final Pattern UNESCAPED_AMPERSAND =
Pattern.compile("&(?!(#\\d+|#x[0-9a-fA-F]+|\\w+);)");
添加|#x[0-9a-fA-F]+
以说明十六进制字符引用。
(我想对此解决方案发表评论,但显然我不能。)