2008-12-29 38 views
36

la lista de caracteres XML válidos son bien conocidas, como se define en la especificación es:Unicode Regex; caracteres no válidos XML

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

Mi pregunta es si es o no es posible hacer una expresión regular PCRE para este (o su inversa) sin codificar realmente los puntos de código, mediante el uso de categorías generales Unicode. Un inverso puede ser algo así como [\ p {Cc} \ p {Cs} \ p {Cn}], excepto que cubre de forma incorrecta saltos de línea y pestañas y omite algunos otros caracteres no válidos.

Respuesta

6

Para los sistemas que almacenan internamente los puntos de código en UTF-16, es común usar pares de sustitución (xD800-xDFFF) para los puntos de código superiores a 0xFFFF y en esos sistemas debe verificar si realmente puede usar \ u12345 o debe especificar que como un par suplente. (Me acabo de enterar que en C# puede utilizar \u1234 (16 bit) and \U000(32-bit))

Según Microsoft "la recomendación W3C no permite caracteres suplentes dentro elemento o los nombres de atributos." Al buscar el sitio web de W3s, encontré C079 y C078 que pueden ser de su interés.

+0

Si bien esta es una sugerencia de implementación útil, realmente no responde mi pregunta. Supongamos, por los argumentos, que la implementación tiene soporte de primera clase para los caracteres que no son BMP, por lo que los caracteres sustitutos no son necesarios en absoluto. –

67

Sé que esto no es exactamente una respuesta a su pregunta, pero es útil tener aquí:

de expresiones regulares para que coincida con válidos Caracteres XML:

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

Así que para eliminar no válidos caracteres de XML, que harían algo así como

// 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, ""); 
} 

que tenía nuestro genio regex/XML residente, he of the 4,400+ upvoted post, verifique esto, y él lo firmó.

+4

En lugar de text.IsNullOrEmpty() Creo que necesitas string.IsNullOrEmpty (text) –

+1

Eres Jeff Atwood, realmente no necesitas un residente para firmar tus cosas aquí. – jbnunn

+8

Yo recomendaría no eliminar caracteres inválidos, sino reemplazarlos con el carácter de reemplazo (FFFD). La eliminación de caracteres no válidos hace que la depuración sea más difícil (los problemas se vuelven invisibles) y en algunos casos puede generar problemas de seguridad. –

4

He intentado esto en Java y funciona:

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

Gracias Jeff.

+0

Solución elegante de una línea. Gracias Yuval. – Dekel

1

Las soluciones anteriores no me funcionaron si el código hexadecimal estaba presente en el xml. p.ej.

<element>&#x8;</element>

El siguiente código se rompería:

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); 

Devuelve:

XmlException: '', valor hexadecimal 0x08, es un carácter no válido. Línea 1, posición 14.

La siguiente es la expresión regular mejorado y fijado el problema mencionado anteriormente:

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

Aquí está una prueba unidad para los primeros 300 caracteres Unicode y verifica que sólo los caracteres no válidos son eliminados:

[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

Otra manera de eliminar caracteres XML incorrectos en C# con el uso de XmlConvert.IsXmlChar Method (Disponible desde el .NET Framework 4,0)

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

o puede comprobar que todos los personajes son XML válido.

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

.Net violín - https://dotnetfiddle.net/v1TNus

Por ejemplo, el símbolo pestaña vertical (\ v) no es válido para XML, es UTF-8 válidos, pero XML 1.0 no válida, e incluso muchas bibliotecas (incluido libxml2) se pierde y silenciosamente muestra XML no válido.

Cuestiones relacionadas