No se puede lograr que la expresión regular funcione correctamente con líneas múltiples

StackOverflow https://stackoverflow.com/questions/289440

  •  08-07-2019
  •  | 
  •  

Pregunta

Tengo una salida XML bastante grande de una aplicación. Necesito procesarlo con mi programa y luego enviarlo al programa original. Hay piezas en este XML que deben completarse para reemplazarlas. La parte interesante se ve así:

<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" />

y el documento contiene varias piezas como esta.

Necesito obtener todas las piezas XML dentro de estas etiquetas para poder realizar modificaciones en ellas. Escribí una expresión regular para obtener esas piezas pero no funciona:

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

Si dejo todo el material en una línea y llamo a esto regexp sin la opción multilínea, encuentra cada ocurrencia. Al dejar el archivo como está y configurar la opción multilínea, no funciona. ¿Cuál es el problema, qué debo cambiar? ¿O hay alguna forma más fácil de obtener las partes XML entre estas etiquetas sin regexp?

¿Fue útil?

Solución

creo que la opción de usar es RegexOptions.Singleline en lugar de RegexOptions.Multiline ( src ). permitir que (.) coincida con las nuevas líneas debería funcionar en su caso.

  

... el modo donde el punto también coincide con las nuevas líneas se llama " modo de línea única " ;. Esto es un poco desafortunado, porque es fácil mezclar este término con "modo multilínea". El modo de varias líneas solo afecta a las anclas, y el modo de una sola línea solo afecta al punto ... Cuando se utilizan las clases de expresiones regulares del marco .NET, se activa este modo especificando RegexOptions.Singleline, como en Regex.Match (" string " ;, " regex " ;, RegexOptions.Singleline).

Otros consejos

RegExp es una herramienta pobre para xml ... ¿no podría cargarlo en un XDocument / XmlDocument y usar xpath? Si aclara las modificaciones que desea realizar, espero que podamos completar los espacios en blanco ... los espacios de nombres son probablemente lo principal para hacerlo complejo en este caso, por lo que solo necesitamos usar un XmlNamespaceManager .

Aquí hay un ejemplo que, por supuesto, es más complejo que solo una expresión regular; sin embargo, esperaría que se adapte mucho mejor a los matices de 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;
        }
    }

El regex char ". " nunca coincide con una nueva línea, incluso con la opción MultiLine configurada. en su lugar, debe usar [\ s \ S] u otra combinación con coincidencias con cualquier cosa.

La opción MultiLine solo modifica el comportamiento de ^ (comienzo de línea en lugar de principio de cadena) y $ (fin de línea en lugar de fin de cadena)

Por cierto: Regex no es la forma correcta de escanear un HTML ...

Si todavía tiene problemas con esto, puede ser porque está usando AND con sus RegexOptions en lugar de OR.

Este código es incorrecto y pasará cero como segundo parámetro al constructor:

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

Este código es correcto (en cuanto al uso de varios indicadores RegexOptions):

Regex regExp = new Regex(@"<sys:customtag(.*?)Processtart(.*?)/>(.*?)<sys:customtag (.*?)Procesend(.*?)/>",
RegexOptions.Multiline | RegexOptions.IgnorePatternWhitespace | RegexOptions.CultureInvariant);
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top