Подтвердить что ты не робот

Невозможно правильно работать с регулярным выражением с помощью многострочного

У меня довольно большой XML-вывод из приложения. Мне нужно обработать его с помощью моей программы, а затем вернуть в исходную программу. В этом XML-фрагменте есть фрагменты, которые нужно заполнить, и мы заменили их. Интересная часть выглядит следующим образом:

<sys:customtag sys:sid="1" sys:type="Processtart" />
    <sys:tag>value</sys:tag>
    here are some other tags
    <sys:tag>value</sys.tag>
<sys:customtag sys:sid="1" sys:type="Procesend" />

и документ содержит несколько таких элементов.

Мне нужно получить все фрагменты XML внутри этих тегов, чтобы иметь возможность вносить в него изменения. Я написал регулярное выражение, чтобы получить эти фрагменты, но он не работает:

XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(@"output.xml");
Regex regExp = new Regex(@"<sys:customtag(.*?)Processtart(.*?)/>(.*?)<sys:customtag (.*?)Procesend(.*?)/>", RegexOptions.Multiline & RegexOptions.IgnorePatternWhitespace & RegexOptions.CultureInvariant);
MatchCollection matches = regExp.Matches(xmlDoc.InnerXml);

Если я оставлю весь материал в одной строке и вызывать это регулярное выражение без многострочной опции, он найдет все вхождения. Если оставить файл как есть и установить многострочный параметр, он не работает. В чем проблема, что я должен изменить? Или есть ли более простой способ получить части XML между этими тегами без регулярного выражения?

4b9b3361

Ответ 1

Я считаю, что опция RegexOptions.Singleline вместо RegexOptions.Multiline (src). позволяющий (.) соответствовать символам новой строки, должен работать в вашем случае.

... режим, в котором точка также соответствует символам новой строки, называется "однолинейным режимом". Это немного неудачно, потому что это легко смешивать с термином "многострочный режим". Многострочный режим влияет только на привязки, а однолинейный режим влияет только на точку... При использовании классов регулярных выражений платформы .NET вы активируете этот режим, указав RegexOptions.Singleline, например, в Regex.Match( "string", "regex", RegexOptions.Singleline).

Ответ 2

RegExp - это плохой инструмент для xml... не могли бы вы его загрузить в XDocument/XmlDocument и использовать xpath? Если вы уточните изменения, которые хотите внести, я ожидаю, что мы сможем заполнить пробелы... пространства имен, вероятно, являются главными, чтобы сделать его сложным в этом случае, поэтому нам просто нужно использовать XmlNamespaceManager.

Вот пример, который является более сложным, чем просто регулярное выражение, однако я бы ожидал, что он будет намного лучше справляться с нюансами xml:

    string xml = @"<foo xmlns:sys=""foobar""><bar/><bar><sys:customtag sys:sid=""1"" sys:type=""Processtart"" />
<sys:tag>value</sys:tag>
here are some other tags
<sys:tag>value</sys:tag>
<sys:customtag sys:sid=""1"" sys:type=""Procesend"" /></bar><bar/></foo>";

    XmlDocument doc = new XmlDocument();
    doc.LoadXml(xml);
    XmlNamespaceManager mgr = new XmlNamespaceManager(new NameTable());
    mgr.AddNamespace("sys", "foobar");
    var matches = doc.SelectNodes("//sys:customtag[@sys:type='Processtart']", mgr);
    foreach (XmlElement start in matches)
    {
        XmlElement end = (XmlElement) start.SelectSingleNode("following-sibling::sys:customtag[@sys:type='Procesend'][1]",mgr);
        XmlNode node = start.NextSibling;
        while (node != null && node != end)
        {
            Console.WriteLine(node.OuterXml);

            node = node.NextSibling;
        }
    }

Ответ 3

Регулярное выражение char "." никогда не соответствует новой строке, даже если установлен параметр MultiLine. вместо этого вы должны использовать [\s\S] или другую комбинацию с чем угодно.

Параметр MultiLine изменяет только поведение ^ (начало строки вместо fo-begin-of-string) и $(конец строки вместо конца строки)

BTW: Действительно, регулярное выражение не является правильным способом сканирования HTML...

Ответ 4

Если у вас все еще есть проблемы с этим, возможно, это связано с тем, что вы используете AND с вашим RegexOptions, а не с OR.

Этот код неверен и будет передавать ноль в качестве второго параметра конструктору:

Regex regExp = new Regex(@"<sys:customtag(.*?)Processtart(.*?)/>(.*?)<sys:customtag (.*?)Procesend(.*?)/>",
RegexOptions.Multiline & RegexOptions.IgnorePatternWhitespace & RegexOptions.CultureInvariant);

Этот код верен (с использованием нескольких флагов RegexOptions):

Regex regExp = new Regex(@"<sys:customtag(.*?)Processtart(.*?)/>(.*?)<sys:customtag (.*?)Procesend(.*?)/>",
RegexOptions.Multiline | RegexOptions.IgnorePatternWhitespace | RegexOptions.CultureInvariant);