2008-12-29 12 views
36

Die Liste der gültigen XML-Zeichen ist allgemein bekannt, wie sie in der Spezifikation definiert es ist:Unicode Regex; Ungültige XML-Zeichen

#x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] 

Meine Frage ist, ob es möglich ist, eine PCRE für reguläre Ausdrücke für diese (oder seine Umkehrung) zu machen ohne die Codepunkte tatsächlich zu codieren, indem Unicode-allgemeine Kategorien verwendet werden. Eine Umkehrung könnte etwas wie [\ p {Cc} \ p {Cs} \ p {Cn}] sein, außer dass Zeilenumbrüche und Tabulatoren falsch behandelt werden und einige andere ungültige Zeichen fehlen.

Antwort

6

Für Systeme, die intern speichert die Codepoints in UTF-16 ist es üblich, Ersatzpaare (xD800-xDFFF) für Codepoints über 0xFFFF und in diesen Systemen zu verwenden, müssen Sie sicherstellen, wenn Sie wirklich zum Beispiel \ u12345 verwenden können, oder angeben müssen, dass als Ersatzpaar. (Ich habe gerade herausgefunden, dass in C# \u1234 (16 bit) and \U000(32-bit))

Nach Microsoft "die W3C-Empfehlung erlaubt keine Ersatzzeichen innerhalb Element oder Attributnamen." Bei der Suche auf der W3s Website habe ich C079 und C078 gefunden, die von Interesse sein könnten.

+0

Während dies ein nützlicher Implementierungstipp ist, beantwortet er meine Frage nicht wirklich. Nehmen wir an, dass die Implementierung eine erstklassige Unterstützung für Nicht-BMP-Zeichen bietet, sodass keine Ersatzzeichen benötigt werden. –

67

Ich weiß, das ist nicht gerade eine Antwort auf Ihre Frage, aber es ist hilfreich, es zu haben hier:

Regular Expression gültig XML-Zeichen übereinstimmen:

[\u0009\u000a\u000d\u0020-\uD7FF\uE000-\uFFFD] 

So zu entfernen ungültige Zeichen von XML, würde tun Sie so etwas wie

// filters control characters but allows only properly-formed surrogate sequences 
private static Regex _invalidXMLChars = new Regex(
    @"(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F\uFEFF\uFFFE\uFFFF]", 
    RegexOptions.Compiled); 

/// <summary> 
/// removes any unusual unicode characters that can't be encoded into XML 
/// </summary> 
public static string RemoveInvalidXMLChars(string text) 
{ 
    if (string.IsNullOrEmpty(text)) return ""; 
    return _invalidXMLChars.Replace(text, ""); 
} 

ich hatte unser Bewohner Regex/XML-Genie, he of the 4,400+ upvoted post, überprüfen Sie das, und er hat sich darauf abgemeldet.

+4

Anstelle von text.IsNullOrEmpty() Ich denke, Sie brauchen string.IsNullOrEmpty (Text) –

+1

Sie sind Jeff Atwood, Sie brauchen nicht wirklich einen Bewohner, um sich auf Ihre Sachen hier abzumelden. – jbnunn

+8

Ich würde empfehlen, ungültige Zeichen nicht zu entfernen, sondern sie durch das Ersatzzeichen (FFFD) zu ersetzen. Das Entfernen ungültiger Zeichen macht das Debuggen schwieriger (Probleme werden unsichtbar) und in einigen Fällen kann es zu Sicherheitslücken kommen. –

4

Ich habe versucht, dies in Java und es funktioniert:

private String filterContent(String content) { 
    return content.replaceAll("[^\\u0009\\u000a\\u000d\\u0020-\\uD7FF\\uE000-\\uFFFD]", ""); 
} 

Sie Jeff danken.

+0

Elegante Ein-Zeilen-Lösung. Danke Yuval. – Dekel

1

Die obigen Lösungen funktionierten nicht für mich, wenn der Hex-Code in der XML vorhanden war. z.B.

<element>&#x8;</element>

Der folgende Code brechen würde:

string xmlFormat = "<element>{0}</element>"; 
string invalid = " &#x8;"; 
string xml = string.Format(xmlFormat, invalid); 
xml = Regex.Replace(xml, @"[\x01-\x08\x0B\x0C\x0E\x0F\u0000-\u0008\u000B\u000C\u000E-\u001F]", ""); 
XDocument.Parse(xml); 

Es gibt:

XmlException: '', hexadezimalen Wert 0x08, ein ungültiges Zeichen ist. Linie 1, Position 14.

Das folgende ist die verbesserte regex und fixiert das oben erwähnte Problem:

& #x ([0-8BCEFbcef] | 1 [0-9A-Fa-f]); | [\ x01- \ x08 \ x0B \ x0c \ X0E \ x0F \ u0000- \ u0008 \ u000B \ u000C \ u000E- \ u001F]

Hier ist eine Testeinheit für die ersten 300 Unicode-Zeichen und überprüft, dass nur ungültige Zeichen werden entfernt:

[Fact] 
     public void validate_that_RemoveInvalidData_only_remove_all_invalid_data() 
     { 
      string xmlFormat = "<element>{0}</element>"; 
      string[] allAscii = (Enumerable.Range('\x1', 300).Select(x => ((char)x).ToString()).ToArray()); 
      string[] allAsciiInHexCode = (Enumerable.Range('\x1', 300).Select(x => "&#x" + (x).ToString("X") + ";").ToArray()); 
      string[] allAsciiInHexCodeLoweCase = (Enumerable.Range('\x1', 300).Select(x => "&#x" + (x).ToString("x") + ";").ToArray()); 

      bool hasParserError = false; 
      IXmlSanitizer sanitizer = new XmlSanitizer(); 

      foreach (var test in allAscii.Concat(allAsciiInHexCode).Concat(allAsciiInHexCodeLoweCase)) 
      { 
       bool shouldBeRemoved = false; 
       string xml = string.Format(xmlFormat, test); 
       try 
       { 
        XDocument.Parse(xml); 
        shouldBeRemoved = false; 
       } 
       catch (Exception e) 
       { 
        if (test != "<" && test != "&") //these char are taken care of automatically by my convertor so don't need to test. You might need to add these. 
        { 
         shouldBeRemoved = true; 
        } 
       } 
       int xmlCurrentLength = xml.Length; 
       int xmlLengthAfterSanitize = Regex.Replace(xml, @"&#x([0-8BCEF]|1[0-9A-F]);|[\u0000-\u0008\u000B\u000C\u000E-\u001F]", "").Length; 
       if ((shouldBeRemoved && xmlCurrentLength == xmlLengthAfterSanitize) //it wasn't properly Removed 
        ||(!shouldBeRemoved && xmlCurrentLength != xmlLengthAfterSanitize)) //it was removed but shouldn't have been 
       { 
        hasParserError = true; 
        Console.WriteLine(test + xml); 
       } 
      } 
      Assert.Equal(false, hasParserError); 
     } 
0

Eine weitere Möglichkeit, mit der Verwendung von XmlConvert.IsXmlChar Method (Verfügbar seit .NET Framework 4,0)

public static string RemoveInvalidXmlChars(string content) 
{ 
    return new string(content.Where(ch => System.Xml.XmlConvert.IsXmlChar(ch)).ToArray()); 
} 

oder Sie können prüfen, ob alle Zeichen sind XML-valid falsche XML Zeichen in C# zu entfernen.

public static bool CheckValidXmlChars(string content) 
{ 
    return content.All(ch => System.Xml.XmlConvert.IsXmlChar(ch)); 
} 

.Net Fiddle - https://dotnetfiddle.net/v1TNus

Zum Beispiel kann die vertikale Tabulatorsymbol (\ v) für XML nicht gültig ist, gilt es UTF-8, aber nicht gültige XML 1.0 und sogar viele Bibliotheken (einschließlich libxml2) vermisse es und gib unbemerkt ungültige XML aus.