web-dev-qa-db-fra.com

Regex Unicode; Caractères XML invalides

La liste des caractères XML valides est bien connue, telle que définie par la spécification c'est:

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

Ma question est de savoir s'il est possible ou non de créer une expression régulière PCRE pour cela (ou son inverse) sans coder en dur les points de code, en utilisant des catégories générales Unicode. Un inverse pourrait être quelque chose comme [\ p {Cc}\p {Cs}\p {Cn}], sauf que de façon incorrecte couvre les sauts de ligne et les tabulations et manque quelques autres caractères invalides.

39
Edward Z. Yang

Je sais que ce n'est pas exactement une réponse à votre question, mais il est utile de l'avoir ici:

Expression régulière pour correspondre aux caractères XML valid:

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

Donc, pour supprimer invalid caractères de XML, vous feriez quelque chose comme:

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

J'ai eu notre génie résident regex/XML, il sur 4 400 + post voté , vérifier, et il a signé.

77
Jeff Atwood

Pour les systèmes qui stockent en interne les points de code dans UTF-16 , il est courant d'utiliser des paires de substitution (xD800-xDFFF) pour les points de code supérieurs à 0xFFFF. Dans ces systèmes, vous devez vérifier si vous pouvez réellement utiliser spécifiez cela en tant que paire de substitution. (Je viens de découvrir qu'en C #, vous pouvez utiliser \u1234 (16 bits) et\U00001234 (32 bits) )

Selon Microsoft "la recommandation du W3C n'autorise pas les caractères de substitution à l'intérieur des noms d'élément ou d'attribut". Lors de la recherche sur le site Web de W3, j'ai trouvé C079 et C078 qui pourraient vous intéresser.

6
some

J'ai essayé cela en Java et ça marche:

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

Merci Jeff.

5
Yuval Rimar

Les solutions ci-dessus ne fonctionnaient pas pour moi si le code hexadécimal était présent dans le fichier XML. par exemple. 

<element>&#x8;</element>

Le code suivant serait cassé:

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

Il retourne:

XmlException : '', valeur hexadécimale 0x08, est un caractère non valide . Ligne 1, position 14.

Ce qui suit est la regex améliorée et corrige le problème mentionné ci-dessus:

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

Voici un unit test pour les 300 premiers caractères Unicode et vérifie que seuls les caractères non valides sont supprimés:

[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);
        }
1
Rafi

Un autre moyen de supprimer les caractères XML incorrects dans C # à l'aide de XmlConvert.IsXmlChar, méthode (Disponible depuis .NET Framework 4.0)

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

ou vous pouvez vérifier que tous les caractères sont valides en XML.

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

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

Par exemple, le symbole de tabulation verticale (\ v) n'est pas valide pour XML, il est valide pour UTF-8, mais pas pour XML 1.0, et même de nombreuses bibliothèques (y compris libxml2) ne l'utilisent pas et produisent en mode silencieux un XML non valide.

0
Alex Vazhev