special regular escape characters c# regex quotes escaping

escape - Encontrar cadenas entre comillas escapadas en C#usando una expresión regular



regular expression backslash (10)

Estoy tratando de encontrar todo el texto citado en una sola línea.

Ejemplo:

"Some Text" "Some more Text" "Even more text about /"this text/""

Necesito conseguir:

  • "Some Text"
  • "Some more Text"
  • "Even more text about /"this text/""

/"[^/"/r]*/" me da todo excepto el último, debido a las comillas que se han escapado.

He leído sobre /"[^/"//]*(?://.[^/"//]*)*/" funcionando, pero recibo un error en tiempo de ejecución:

parsing ""[^"/]*(?:/.[^"/]*)*"" - Unterminated [] set.

¿Cómo puedo solucionar esto?


Bueno, la respuesta de Alan Moore es buena, pero la modificaría un poco para hacerla más compacta. Para el compilador de expresiones regulares:

"([^"//]*(//.)*)*"

Compare con la expresión de Alan Moore:

"[^"//]*(//.[^"//]*)*"

La explicación es muy similar a la de Alan Moore:

La primera parte " coincide con una comilla.

La segunda parte [^"//]* coincide con cero o más caracteres que no sean comillas o barras diagonales inversas.

Y la última parte (//.)* Coincide con la barra diagonal inversa y con el carácter individual que le sigue. Preste atención al *, diciendo que este grupo es opcional.

Las partes descritas, junto con la final " (es decir, "[^"//]*(//.)*" ) Coincidirán:" Some Text "y" Even more Text / "", pero no coincidirán: " Aún más texto sobre / "este texto /" ".

Para hacerlo posible, necesitamos la parte: [^"//]*(//.)* repite tantas veces como sea necesario hasta que aparezca una comilla no guardada (o llega al final de la cadena y al intento de coincidencia) falla). Así que envolví esa parte entre corchetes y agregué un asterisco. Ahora coincide: "Some Text", "Aún más Text /" "," Más texto sobre / "este texto /" "y" Hello // " .

En el código C # se verá así:

var r = new Regex("/"([^/"////]*(////.)*)*/"");

Por cierto, el orden de las dos partes principales: [^"//]* y (//.)* No importa. Usted puede escribir:

"([^"//]*(//.)*)*"

o

"((//.)*[^"//]*)*"

El resultado será el mismo.

Ahora tenemos que resolver otro problema: /"foo/"-"bar" . La expresión actual coincidirá con "foo/"-" , pero queremos que coincida con "bar" . No sé

¿Por qué habría escapadas de citas fuera de las cadenas entre comillas?

pero podemos implementarlo fácilmente agregando la siguiente parte al principio: (/G|[^//]) . Dice que queremos que el partido comience en el punto donde terminó la partida anterior o después de cualquier personaje, excepto la barra invertida. ¿Por qué necesitamos /G ? Esto es para el siguiente caso, por ejemplo: "a""b" .

Tenga en cuenta que (/G|[^//])"([^"//]*(//.)*)*" Coincide con -"bar" en /"foo/"-"bar" . Entonces, para obtener solo "bar" , necesitamos especificar el grupo y opcionalmente darle un nombre, por ejemplo "Mi Grupo". Entonces el código de C # se verá así:

[TestMethod] public void RegExTest() { //Regex compiler: (?:/G|[^//])(?<MyGroup>"(?:[^"//]*(?:/.)*)*") string pattern = "(?://G|[^////])(?<MyGroup>/"(?:[^/"////]*(?:////.)*)*/")"; var r = new Regex(pattern, RegexOptions.IgnoreCase); //Human readable form: "Some Text" and "Even more Text/"" "Even more text about /"this text/"" "Hello//" /"foo/" - "bar" "a" "b" c "d" string inputWithQuotedText = "/"Some Text/" and /"Even more Text///"/" /"Even more text about ///"this text///"/" /"Hello/////" ///"foo///"-/"bar/" /"a/"/"b/"c/"d/""; var quotedList = new List<string>(); for (Match m = r.Match(inputWithQuotedText); m.Success; m = m.NextMatch()) quotedList.Add(m.Groups["MyGroup"].Value); Assert.AreEqual(8, quotedList.Count); Assert.AreEqual("/"Some Text/"", quotedList[0]); Assert.AreEqual("/"Even more Text///"/"", quotedList[1]); Assert.AreEqual("/"Even more text about ///"this text///"/"", quotedList[2]); Assert.AreEqual("/"Hello/////"", quotedList[3]); Assert.AreEqual("/"bar/"", quotedList[4]); Assert.AreEqual("/"a/"", quotedList[5]); Assert.AreEqual("/"b/"", quotedList[6]); Assert.AreEqual("/"d/"", quotedList[7]); }


Cualquier oportunidad que necesites hacer: /"[^/"////]*(?://.[^/"////]*)*/"


La expresión regular

(?<!//)".*?(?<!//)"

también manejará el texto que comienza con una cita escapada:

/"Some Text/" Some Text "Some Text", and "Some more Text" an""d "Even more text about /"this text/""


Lo que tienes allí es un ejemplo de la técnica de "ciclo desenrollado" de Friedl, pero parece que tienes cierta confusión sobre cómo expresarlo como un literal de cadena. Así es como debería verse el compilador de expresiones regulares:

"[^"//]*(?://.[^"//]*)*"

La inicial "[^"//]* coincide con una comilla seguida de cero o más caracteres que no sean comillas o barras diagonales inversas. Esa parte sola, junto con la final " , coincidirá con una cadena simple citada sin secuencias de escape integradas, como "this" o "" .

Si encuentra una barra invertida, //. consume la barra diagonal inversa y lo que le sigue, y [^"//]* (nuevamente) consume todo hasta la siguiente barra diagonal inversa o comilla. Esa parte se repite tantas veces como sea necesario hasta que aparezca una comilla sin sombrear (o alcanza el final de la cadena y el intento de coincidencia falla).

Tenga en cuenta que esto coincidirá con "foo/"- en /"foo/"-"bar" . Eso puede parecer que expone un defecto en la expresión regular, pero no lo hace; es la entrada que no es válida. El objetivo era hacer coincidir las cadenas entrecomilladas, que contenían opcionalmente comillas escapatorias, incrustadas en otro texto. ¿Por qué habría comillas escapadas fuera de las cadenas entrecomilladas? Si realmente necesita apoyar eso, tiene un problema mucho más complejo, que requiere un enfoque muy diferente.

Como dije, lo anterior es cómo debe mirar la expresión regular al compilador de expresiones regulares. Pero lo está escribiendo en forma de cadena literal, y esos tienden a tratar ciertos caracteres especialmente, es decir, barras diagonales inversas y comillas. Afortunadamente, las cadenas textuales de C # le ahorran la molestia de tener que hacer doble escape de las barras diagonales inversas; solo tiene que escapar de cada comilla con otra comilla:

Regex r = new Regex(@"""[^""//]*(?://.[^""//]*)*""");

Por lo tanto, la regla es comillas dobles para el compilador de C # y dobles barras diagonales inversas para el compilador de expresiones regulares, agradable y fácil. Esta expresión regular particular puede parecer un poco incómoda, con las tres comillas en cada extremo, pero considere la alternativa:

Regex r = new Regex("/"[^/"////]*(?:////.[^/"////]*)*/"");

En Java, siempre tienes que escribirlos de esa manera. :-(


Recomiendo obtener RegexBuddy . Te permite jugar con él hasta que te asegures de que todo en tu conjunto de prueba coincida.

En cuanto a su problema, probaría cuatro / ''s en lugar de dos:

/"[^/"////]*(?://.[^/"////]*)*/"


Regex para capturar cadenas (con / para escapar de caracteres), para el motor .NET:

(?>(?(STR)(?(ESC).(?<-ESC>)|//(?<ESC>))|(?!))|(?(STR)"(?<-STR>)|"(?<STR>))|(?(STR).|(?!)))+

Aquí, una versión "amigable":

(?> | especify nonbacktracking (?(STR) | if (STRING MODE) then (?(ESC) | if (ESCAPE MODE) then .(?<-ESC>) | match any char and exits escape mode (pop ESC) | | else //(?<ESC>) | match ''/' and enters escape mode (push ESC) ) | endif | | else (?!) | do nothing (NOP) ) | endif | | -- OR (?(STR) | if (STRING MODE) then "(?<-STR>) | match ''"'' and exits string mode (pop STR) | | else "(?<STR>) | match ''"'' and enters string mode (push STR) ) | endif | | -- OR (?(STR) | if (STRING MODE) then . | matches any character | | else (?!) | do nothing (NOP) ) | endif )+ | REPEATS FOR EVERY CHARACTER

Basado en http://tomkaminski.com/conditional-constructs-net-regular-expressions examples. Se basa en el equilibrio de las citas. Lo uso con gran éxito. Úselo con la bandera de línea Singleline .

Para jugar con expresiones regulares, recomiendo Rad Software Regular Expression Designer , que tiene una buena pestaña de "Elementos de idioma" con acceso rápido a algunas instrucciones básicas. Está basado en el motor de expresiones regulares de .NET.


Sé que este no es el método más limpio, pero con tu ejemplo verificaría el carácter antes del " para ver si es un / . Si lo es, ignoraría la cita.


Similar a RegexBuddy publicado por @Blankasaurus, RegexMagic también ayuda.


Una respuesta simple, sin el uso de ? , es

"([^//"]*(//")*)*/"

o, como una cadena literal

@"^""([^//""]*(//"")*(//[^""])*)*"""

Simplemente significa:

  • encuentra el primero "
  • encuentre cualquier cantidad de caracteres que no sean / o "
  • encuentra cualquier cantidad de citas escapadas /"
  • encuentre cualquier cantidad de caracteres escapados, que no son citas
  • repite los últimos tres comandos hasta que encuentres "

Creo que funciona tan bien como la respuesta de @Alan Moore, pero, para mí, es más fácil de entender. También acepta citas sin igual ("desequilibradas").


"(//"|////|[^"//])*"

Deberia trabajar. Haga coincidir una comilla escapada, una barra diagonal escapada o cualquier otro carácter, excepto una comilla o una barra inclinada invertida. Repetir.

Cª#:

StringCollection resultList = new StringCollection(); Regex regexObj = new Regex(@"""(//""|////|[^""//])*"""); Match matchResult = regexObj.Match(subjectString); while (matchResult.Success) { resultList.Add(matchResult.Value); matchResult = matchResult.NextMatch(); }

Editar: se agregó una barra invertida escapada a la lista para manejar correctamente "This is a test//" .

Explicación:

En primer lugar, un personaje de cita.

Luego, las alternativas se evalúan de izquierda a derecha. El motor primero intenta hacer coincidir una cita escapada. Si eso no coincide, intenta una barra invertida escapada. De esta forma, se puede distinguir entre "Hello /" string continues" y "String ends here //" .

Si ninguno de los dos coincide, se permite todo lo demás excepto un carácter de comillas o barras invertidas. Entonces repite.

Finalmente, haga coincidir la cita de cierre.