Rimozione di caratteri XML non validi da una stringa in Java
-
26-09-2019 - |
Domanda
Ciao, vorrei rimuovere tutti i caratteri XML non validi da una stringa. Vorrei usare un'espressione regolare con il metodo String.Replace.
piace
line.replace(regExp,"");
Qual è il regexp giusto da usare?
Il personaggio XML non valido è tutto ciò che non è questo:
[#x1-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
Grazie.
Soluzione
Il regex di Java supporta caratteri supplementari, quindi è possibile specificare quelle gamme alte con due caratteri codificati con UTF-16.
Ecco il modello per rimuovere i personaggi che sono illegali XML 1.0:
// XML 1.0
// #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
String xml10pattern = "[^"
+ "\u0009\r\n"
+ "\u0020-\uD7FF"
+ "\uE000-\uFFFD"
+ "\ud800\udc00-\udbff\udfff"
+ "]";
La maggior parte delle persone vorrà la versione XML 1.0.
Ecco il modello per rimuovere i personaggi che sono illegali XML 1.1:
// XML 1.1
// [#x1-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
String xml11pattern = "[^"
+ "\u0001-\uD7FF"
+ "\uE000-\uFFFD"
+ "\ud800\udc00-\udbff\udfff"
+ "]+";
Dovrai usare String.replaceAll(...)
e non String.replace(...)
.
String illegal = "Hello, World!\0";
String legal = illegal.replaceAll(pattern, "");
Altri suggerimenti
Dovremmo considerare i personaggi surrogati? altrimenti '(corrente> = 0x10000) && (corrente <= 0x10ffff)' non sarà mai vero.
Ha anche testato che il modo di Regex sembra più lento del seguente loop.
if (null == text || text.isEmpty()) {
return text;
}
final int len = text.length();
char current = 0;
int codePoint = 0;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < len; i++) {
current = text.charAt(i);
boolean surrogate = false;
if (Character.isHighSurrogate(current)
&& i + 1 < len && Character.isLowSurrogate(text.charAt(i + 1))) {
surrogate = true;
codePoint = text.codePointAt(i++);
} else {
codePoint = current;
}
if ((codePoint == 0x9) || (codePoint == 0xA) || (codePoint == 0xD)
|| ((codePoint >= 0x20) && (codePoint <= 0xD7FF))
|| ((codePoint >= 0xE000) && (codePoint <= 0xFFFD))
|| ((codePoint >= 0x10000) && (codePoint <= 0x10FFFF))) {
sb.append(current);
if (surrogate) {
sb.append(text.charAt(i));
}
}
}
La soluzione di Jun, semplificata. Usando StringBuffer#appendCodePoint(int)
, Ho bisogno di no char current
o String#charAt(int)
. Posso dire una coppia surrogata controllando se codePoint
è più grande di 0xFFFF
.
(Non è necessario eseguire l'i ++, poiché un surrogato basso non passerebbe il filtro. Ma poi si riutilizzerebbe il codice per diversi punti di codice e fallirebbe. Preferisco la programmazione all'hacking.)
StringBuilder sb = new StringBuilder();
for (int i = 0; i < text.length(); i++) {
int codePoint = text.codePointAt(i);
if (codePoint > 0xFFFF) {
i++;
}
if ((codePoint == 0x9) || (codePoint == 0xA) || (codePoint == 0xD)
|| ((codePoint >= 0x20) && (codePoint <= 0xD7FF))
|| ((codePoint >= 0xE000) && (codePoint <= 0xFFFD))
|| ((codePoint >= 0x10000) && (codePoint <= 0x10FFFF))) {
sb.appendCodePoint(codePoint);
}
}
Tutte queste risposte finora sostituiscono solo i personaggi stessi. Ma a volte un documento XML avrà sequenze di entità XML non valide che si traducono in errori. Ad esempio, se hai 
Nel tuo XML, lancerà un parser Java XML Illegal character entity: expansion character (code 0x2 at ...
.
Ecco un semplice programma Java che può sostituire quelle sequenze di entità non valide.
public final Pattern XML_ENTITY_PATTERN = Pattern.compile("\\&\\#(?:x([0-9a-fA-F]+)|([0-9]+))\\;");
/**
* Remove problematic xml entities from the xml string so that you can parse it with java DOM / SAX libraries.
*/
String getCleanedXml(String xmlString) {
Matcher m = XML_ENTITY_PATTERN.matcher(xmlString);
Set<String> replaceSet = new HashSet<>();
while (m.find()) {
String group = m.group(1);
int val;
if (group != null) {
val = Integer.parseInt(group, 16);
if (isInvalidXmlChar(val)) {
replaceSet.add("&#x" + group + ";");
}
} else if ((group = m.group(2)) != null) {
val = Integer.parseInt(group);
if (isInvalidXmlChar(val)) {
replaceSet.add("&#" + group + ";");
}
}
}
String cleanedXmlString = xmlString;
for (String replacer : replaceSet) {
cleanedXmlString = cleanedXmlString.replaceAll(replacer, "");
}
return cleanedXmlString;
}
private boolean isInvalidXmlChar(int val) {
if (val == 0x9 || val == 0xA || val == 0xD ||
val >= 0x20 && val <= 0xD7FF ||
val >= 0x10000 && val <= 0x10FFFF) {
return false;
}
return true;
}
/**
* This method ensures that the output String has only
* valid XML unicode characters as specified by the
* XML 1.0 standard. For reference, please see
* <a href="http://www.w3.org/TR/2000/REC-xml-20001006#NT-Char">the
* standard</a>. This method will return an empty
* String if the input is null or empty.
*
* @param in The String whose non-valid characters we want to remove.
* @return The in String, stripped of non-valid characters.
*/
public static String stripNonValidXMLCharacters(String in) {
StringBuffer out = new StringBuffer(); // Used to hold the output.
char current; // Used to reference the current character.
if (in == null || ("".equals(in))) return ""; // vacancy test.
for (int i = 0; i < in.length(); i++) {
current = in.charAt(i); // NOTE: No IndexOutOfBoundsException caught here; it should not happen.
if ((current == 0x9) ||
(current == 0xA) ||
(current == 0xD) ||
((current >= 0x20) && (current <= 0xD7FF)) ||
((current >= 0xE000) && (current <= 0xFFFD)) ||
((current >= 0x10000) && (current <= 0x10FFFF)))
out.append(current);
}
return out.toString();
}
Da Il modo migliore per codificare i dati di testo per XML in Java?
String xmlEscapeText(String t) {
StringBuilder sb = new StringBuilder();
for(int i = 0; i < t.length(); i++){
char c = t.charAt(i);
switch(c){
case '<': sb.append("<"); break;
case '>': sb.append(">"); break;
case '\"': sb.append("""); break;
case '&': sb.append("&"); break;
case '\'': sb.append("'"); break;
default:
if(c>0x7e) {
sb.append("&#"+((int)c)+";");
}else
sb.append(c);
}
}
return sb.toString();
}
Se si desidera archiviare elementi di testo con i caratteri proibiti in forma simile a XML, puoi invece usare XPL. Dev -kit fornisce elaborazione XPL a XML e XML concorrente, il che significa che non costano tempo per la traduzione da XPL a XML. Oppure, se non hai bisogno della piena potenza di XML (spazi dei nomi), puoi semplicemente usare XPL.
String xmlData = xmlData.codePoints().filter(c -> isValidXMLChar(c)).collect(StringBuilder::new,
StringBuilder::appendCodePoint, StringBuilder::append).toString();
private boolean isValidXMLChar(int c) {
if((c == 0x9) ||
(c == 0xA) ||
(c == 0xD) ||
((c >= 0x20) && (c <= 0xD7FF)) ||
((c >= 0xE000) && (c <= 0xFFFD)) ||
((c >= 0x10000) && (c <= 0x10FFFF)))
{
return true;
}
return false;
}
Credo che i seguenti articoli possano aiutarti.
http://commons.apache.org/lang/api-2.1/org/apache/commons/lang/stringescapeutils.htmlhttp://www.javapractices.com/topic/topicalction.do?id=96
A poco a poco, prova a utilizzare Stringescapeutils del progetto Jakarta.