代码之家  ›  专栏  ›  技术社区  ›  Edward Z. Yang

Unicode正则表达式;无效的XML字符

  •  39
  • Edward Z. Yang  · 技术社区  · 16 年前

    有效XML字符的列表是众所周知的,由规范定义如下:

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

    我的问题是,是否有可能通过使用unicode通用类别,在不实际硬编码编码点的情况下,为此(或其逆)生成pcre正则表达式。逆操作可能类似于[\p{cc}\p{cs}\p{cn}],只是不正确地覆盖换行符和制表符,并且遗漏了一些其他无效字符。

    5 回复  |  直到 7 年前
        1
  •  80
  •   Community CDub    8 年前

    我知道这并不能完全回答你的问题,但在这里有帮助:

    要匹配的正则表达式 有效的 XML字符:

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

    所以要移除 无效 XML中的字符,您可以执行如下操作

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

    我有我们的常驻regex/xml天才, he of the 4,400+ upvoted post ,检查一下,他就签字了。

        2
  •  6
  •   some    16 年前

    对于内部存储代码点的系统 UTF-16 通常使用代理对(XD800 XDFFF)用于0xFFF以上的代码点,在这些系统中,必须验证是否真的可以使用例如U12345,或者必须指定它作为代理对。(我刚刚发现C语言中你可以使用 \u1234 (16 bit) and \U00001234 (32-bit) )

    根据 Microsoft “W3C建议不允许元素或属性名称中包含代理项字符。”在搜索W3S网站时,我发现 C079 C078 那可能很有趣。

        3
  •  5
  •   Yuval Rimar    13 年前

    我在Java中尝试过,它的工作原理是:

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

    谢谢你,杰夫。

        4
  •  1
  •   Rafi    8 年前

    如果十六进制代码存在于XML中,上述解决方案对我不起作用。例如

    <element>&#x8;</element>

    以下代码将中断:

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

    它返回:

    XML异常 :'',十六进制值0x08是无效字符。 1号线,14号位置。

    以下是改进的regex并修复了上述问题:

    &#X([0-8BCEF]1[0-9A-FA-F]);[\x01-\x08\x0B\x0C\x0E\x0F\u0000-\u0008\u000B\u000C\u000E-\u001F]

    这里是一个 单元测试 对于前300个Unicode字符,并验证是否仅删除无效字符:

    [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);
            }
    
        5
  •  0
  •   Alex Vazhev    7 年前

    使用 XmlConvert.IsXmlChar Method (从.NET Framework 4.0开始提供)

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

    或者您可以检查所有字符都是XML有效的。

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

    .NET小提琴- https://dotnetfiddle.net/v1TNus

    例如,垂直制表符(\v)对xml无效,它是有效的utf-8,但不是有效的xml 1.0,甚至许多库(包括libxml2)都没有找到它并静默输出无效的xml。