[英]Fixing unescaped XML entities in Java with Regex?
我有一些格式錯誤的 XML 必須解析。 無法在上游修復問題。
(當前)問題是&符號並不總是正確轉義,所以我需要將&
轉換為&
如果&
已經存在,我不想將其更改為&
. 一般來說,如果任何結構良好的實體已經存在,我不想破壞它。 一般來說,我認為不可能知道可能出現在任何特定 XML 文檔中的所有實體,所以我想要一個解決方案,其中包含&<characters>;
之類的任何東西。 被保留。
其中<characters>
是一些字符集,定義了初始&
和結束之間的實體;
. 特別是, <
和>
不是文字,否則將表示 XML 元素。
現在,在解析時,如果我看到&<characters>
我不知道我是否會遇到;
, 一個 (空格)、行尾或另一個
&
。 所以我認為我必須記住<characters>
,因為我期待一個可以告訴我如何處理原始&
的字符。
我認為我需要下推自動機的力量來做到這一點,我認為有限的 State 機器不會工作,因為我認為是 memory 要求 - 對嗎? 如果我需要 PDA,那么調用String.replaceAll(String, String)
中的正則表達式將不起作用。 或者有沒有可以解決這個問題的 Java 正則表達式?
請記住:每行可能有多個替換。
(我知道這個問題,但它沒有提供我正在尋找的答案。)
這是您要查找的正則表達式: &([^;\\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
如您所見,原來的&
和
維持不變。 但是,如果你用 "&&" 試試,你會得到&&
,如果你用 "&&&" 試試,你會得到&&&
,我認為這是您所暗示的前瞻問題的症狀。 但是,如果您替換該行:
final String t = s.replaceAll(regex, replacement);
和:
final String t = s.replaceAll(regex, replacement).replaceAll(regex, replacement);
它適用於所有這些字符串以及我能想到的任何其他字符串。 (在成品中,您可能會編寫一個執行此雙重replaceAll
調用的例程。)
我認為您還可以使用前瞻來查看&
字符是否后跟字符 & 分號(例如&(?;\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.
首先了解實體的語法: http://www.w3.org/TR/xml/#NT-EntityRef
然后查看FilterInputStream
的 JavaDoc: http://download.oracle.com/javase/6/docs/api/java/io/FilterInputStream.ZFC35FDC70D5FC69D269883A822E11BF8607170EC9A4B8Z.com/javase/6/docs/api/java/io/FilterInputStream.ZFC35FDC70D5FC69D269883A822E7A53
然后實現一個逐字符讀取實際輸入的方法。 當它看到一個 & 符號時,它會切換到“實體模式”並尋找一個有效的實體引用 ( & Name;
)。 如果它在Name
中不允許的第一個字符之前找到一個,則將其逐字寫入 output。 否則它寫&
緊隨其后的是&符號之后的所有內容。
與其嘗試對所有可能的不良數據進行一般性的處理,只需處理一次出現的不良數據。 有可能生成 XML 的東西會弄亂一兩個字符,但不是全部。 這當然是一個假設。
嘗試將所有 & 替換為 & 除了 & 后跟 amp; 時。 如果您遇到的下一個編碼不正確的字符是 <,則將它們全部替換為 <。 保持規則集小而易於管理,只處理你知道是錯誤的事情。
如果您嘗試做很多事情,最終可能會替換您不打算做的事情並自己弄亂數據。
我只想指出,最好的解決方案是鼓勵生產 XML 的人最終修復編碼。 問這個問題可能很尷尬,但如果你專業地向他們解釋他們沒有生成有效的 XML,他們可能願意修復錯誤。 這將為下一個必須使用它的人帶來額外的好處,不需要做一些瘋狂的自定義代碼來解決應該從源頭解決的問題。 至少考慮一下。 可能發生的更糟糕的事情是你問,他們說不,你就在你現在的位置。
很抱歉激起了一個舊線程:
我遇到了同樣的問題,我使用的解決方法分為 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,’ & 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>
第三步:
正則表達式是否將"#\|\|([az]+)\|\|#"
替換為"&$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>
缺點:必須仔細選擇隱藏有效實體的自定義字符序列,以確保沒有任何有效內容會偶然包含相同的序列。 雖然機會很小,但承認,這不是一個完全可靠的解決方案......
我使用了上面的UNESCAPED_AMPERSAND
解決方案,但我不得不將正則表達式更改為
private static final Pattern UNESCAPED_AMPERSAND =
Pattern.compile("&(?!(#\\d+|#x[0-9a-fA-F]+|\\w+);)");
添加|#x[0-9a-fA-F]+
以說明十六進制字符引用。
(我想評論那個解決方案,但顯然我不能。)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.