.NET Regex parse markup para valores repetidos en cierta sección pero no en otras

Necesito usar expresiones regulares .NET para descartar algunos valores entre <value> etiquetas de un archivo de marcado como este (copiar \ extracto pegado):

<Title>Section1</Title>

<attributeArray><name>Name1</name><value>Value1</value></attributeArray>

<attributeArray><name>Name2</name><value>Value2</value></attributeArray>

<attributeArray><name>Name3</name><value>Value3</value></attributeArray>

<attributeArray><name>Name4</name><value>Value4</value></attributeArray>

<Title>Section2</Title>

<attributeArray><name>Name1</name><value>Value1</value></attributeArray>

<attributeArray><name>Name2</name><value>Value2</value></attributeArray>

<attributeArray><name>Name3</name><value>Value3</value></attributeArray>

<attributeArray><name>Name4</name><value>Value4</value></attributeArray>

</node>

El texto actual pasa a incluir 6 secciones. el problema que tengo es que todos los nombres de etiquetas para cada sección son idénticos y solo necesito extraer los valores de, por ejemplo, Section2 (sin incluir 1, 3,4,5,6, XNUMX, XNUMX, XNUMX).

He luchado con esto durante un par de días y probé varias expresiones condicionales que eran nuevas para mí como esta:

(?(<node>Section2)(.*?<value>(?<Value>.*?)<\/value>.*?))

Si es la Sección 2, analice las claves de valor, pero solo extrae el primer valor, no itera a través de cada <value> del marcado. y el marcado generalmente tiene alrededor de 10 valores que necesito extraer (abreviado en el ejemplo anterior).

Esto no se hace en código, por lo que no tengo la libertad de usar un analizador XML.

Cualquier sugerencia sería muy apreciada, o si puedo aclarar más, hágamelo saber.

Una idea tardía: si hay una manera de incluir el texto del título con cada coincidencia de valor, entonces podría analizar las 6 secciones, pero luego podría filtrar el resultado en función de la sección que busco.

ejemplo:

match1
group1 = Section2
group2 = Value1

match2
group1 = Section2
group2 = Value2

match3
group1 = Section2
group2 = Value3

match4
group1 = Section2
group2 = Value4

¡Gracias!

preguntado el 28 de agosto de 11 a las 03:08

¿Qué es? </node> al final, y el <node> elemento en el patrón? Además, ¿puede ejecutar cualquier código al analizar los datos? (y si es así, ¿por qué no un analizador XML? Lo pregunto porque usted dice que enlatado filtrar los resultados, pero no puede hacer otras cosas) -

@Kobi El OP dijo que un analizador XML no es una opción. -

@Null - Lo sé, pero el OP también dijo "pero luego podría filtrar el resultado en función de la sección que busco". Me parece curioso que el OP pueda funcionar algo código, y estoy interesado en sus limitaciones. Por lo general, cuando necesita una expresión regular estricta, no tiene esa opción. -

Los ' 'es solo un extracto de copiar \ pegar y no tomé la coincidencia de nivel superior' '. Esta expresión regular es una entrada a una aplicación de software. La aplicación admitiría el filtrado según los resultados que describí anteriormente. Solo para reiterar que no tengo acceso al código para la respuesta sugerida sobre CaptureCollection: este es un campo de entrada para una aplicación que no controlo. Gracias. -

2 Respuestas

Aquí tienes una opción:

(?:
   <Title>Section2</Title>    # Match the header
   |                          # or
   \G(?!\A)                   # Match where the previous match ended
)\s*
<attributeArray>
    <name>(?<name>[^<]*)</name>
    <value>(?<value>[^<]*)</value>
</attributeArray>

La primera coincidencia incluye el encabezado, y las siguientes coincidencias deben comenzar donde terminó la anterior.
Ejemplo de trabajo: http://regexhero.net/tester/?id=321ce843-923d-4556-9b99-dbb72175929a


Tenga en cuenta que lo anterior fallará si tiene otros elementos que no mencionó entre los valores o el título. Puede evitar eso con un patrón probablemente menos eficiente, utilizando el hecho de que las expresiones regulares .Net pueden tener búsquedas atrás de longitud variable:

(?<=                          # lookbehind - check that before the current position
   <Title>Section2</Title>    #  we can see the wanted title,
   (?:(?!<Title>).)*          #  followed by no more title between it and here.
)
<attributeArray>
    <name>(?<name>[^<]*)</name>
    <value>(?<value>[^<]*)</value>
</attributeArray>

Ejemplo: http://regexhero.net/tester/?id=743c4de6-1b8a-48a4-a69b-63f3624de594

Si lo desea, puede cambiar el título a <Title>(?<title>[^<]*)</Title>, capture todos los valores en el archivo y filtre por el título deseado; se agregará a cada coincidencia.


Por último, aquí hay un enfoque similar que funcionará en otros sabores: captura pares clave / valor antes el título Section3, asumiendo que está bien ordenado:

<attributeArray>
    <name>(?<name>[^<]*)</name>
    <value>(?<value>[^<]*)</value>
</attributeArray>
(?=
   (?:(?!<Title>).)*
   <Title>Section3</Title>
)

Ejemplo: http://regexhero.net/tester/?id=8d8ae0e8-5f10-439f-a5a5-50d0b4e73bd2

Respondido 29 ago 11, 08:08

¡Muy agradable! Regresé aquí pensando que debería agregar una solución no basada en capturas para mostrar que esto se podría hacer en versiones distintas de .NET, solo para descubrir que ya se ha manejado. :D - Alan Moore

Recomiendo usar CaptureCollection:

string s = @"<Title>Section1</Title>
<attributeArray><name>Name1</name><value>Value1-1</value></attributeArray>
<attributeArray><name>Name2</name><value>Value1-2</value></attributeArray>
<attributeArray><name>Name3</name><value>Value1-3</value></attributeArray>
<attributeArray><name>Name4</name><value>Value1-4</value></attributeArray>

<Title>Section2</Title>
<attributeArray><name>Name1</name><value>Value2-1</value></attributeArray>
<attributeArray><name>Name2</name><value>Value2-2</value></attributeArray>
<attributeArray><name>Name3</name><value>Value2-3</value></attributeArray>
<attributeArray><name>Name4</name><value>Value2-4</value></attributeArray>

<Title>Section3</Title>
<attributeArray><name>Name1</name><value>Value3-1</value></attributeArray>
<attributeArray><name>Name2</name><value>Value3-2</value></attributeArray>
<attributeArray><name>Name3</name><value>Value3-3</value></attributeArray>
<attributeArray><name>Name4</name><value>Value3-4</value></attributeArray>";

Regex r = new Regex(
  @"<Title>(Section2)</Title>(?:\s*<attributeArray>.*?<value>(.*?)</value></attributeArray>)+");
Match m = r.Match(s);
if (m.Success)
{
  string section = m.Groups[1].Value;
  int i = 0;
  foreach (Capture c in m.Groups[2].Captures)
  {
    Console.WriteLine("match{0}\ngroup1 = {1}\ngroup2 = {2}\n",
                      ++i, section, c.Value);
  }
}

m.Groups[2].Value volvería Value2-4,la pasado cosa que se capturará en el grupo # 2. Pero todas las capturas intermedias se conservan y se puede acceder a ellas a través del Captures propiedad.

Respondido 28 ago 11, 12:08

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.