Regex para cadena entre comillas con comillas de escape

122

¿Cómo obtengo la subcadena " It's big \"problem "usando una expresión regular?

s = ' function(){  return " It\'s big \"problem  ";  }';     
David
fuente
1
¿Cómo se encuentra "Es" en una cadena que solo contiene "Es"? Lo arreglaría por usted, pero no sé qué convenciones de comillas simples / escape se aplican en el idioma que está utilizando.
Jonathan Leffler
2
De hecho, mirando las fechas, veo que la otra pregunta es un duplicado de esta. De cualquier manera, asegúrese de revisar mi respuesta .
ridgerunner
@ridgerunner: Estoy votando para cerrar esto como sugirió. Es cierto que otra pregunta es más reciente, pero también es mucho mejor (gracias principalmente a tu respuesta).
Alan Moore

Respuestas:

160
/"(?:[^"\\]|\\.)*"/

Funciona en The Regex Coach y PCRE Workbench.

Ejemplo de prueba en JavaScript:

    var s = ' function(){ return " Is big \\"problem\\", \\no? "; }';
    var m = s.match(/"(?:[^"\\]|\\.)*"/);
    if (m != null)
        alert(m);

PhiLho
fuente
24
Tiene sentido. Inglés simple: dos comillas alrededor de cero o más de "cualquier carácter que no sea una cita o una barra invertida" o "una barra invertida seguida de cualquier carácter". No puedo creer que no pensé en hacer eso ...
Ajedi32
7
Yo mismo responderé. =) (?:...)es un grupo pasivo o no capturador. Significa que no se puede volver a referenciar posteriormente.
magras
después de buscar mucho y probar mucho, esta es la única solución real que encontré para este problema común. ¡Gracias!
cancerbero
10
gracias por esto. También quería unir comillas simples, así que terminé adaptándolo a esto:/(["'])(?:[^\1\\]|\\.)*?\1/
leo
Con var s = ' my \\"new\\" string and \"this should be matched\"';, este enfoque conducirá a resultados inesperados.
Wiktor Stribiżew
32

Este proviene de nanorc.sample disponible en muchas distribuciones de Linux. Se utiliza para resaltar la sintaxis de cadenas de estilo C

\"(\\.|[^\"])*\"

fuente
Con var s = ' my \\"new\\" string and \"this should be matched\"';, este enfoque conducirá a resultados inesperados.
Wiktor Stribiżew
1
c.nanorc fue el primer lugar al que fui. No se pudo hacer que funcionara como parte de un literal de cadena C hasta que se escapó doblemente todo como este" \"(\\\\.|[^\\\"])*\" "
hellork
Esto funciona con las funciones egrep y re_comp / re_exec de libc.
fk0
19

Según lo proporcionado por ePharaoh, la respuesta es

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

Para que lo anterior se aplique a cadenas entre comillas simples o dobles, utilice

/"([^"\\]*(\\.[^"\\]*)*)"|\'([^\'\\]*(\\.[^\'\\]*)*)\'/
Guy Bedford
fuente
2
Este es el único conjunto que me funcionó con una única cadena grande entre comillas de 1,5 KB que contiene 99 escapes. Todas las demás expresiones de esta página se rompieron en mi editor de texto con un error de desbordamiento. Aunque la mayoría aquí funciona en el navegador, algo a tener en cuenta. Fiddle: jsfiddle.net/aow20y0L
Beejor
3
Consulte la respuesta de @ MarcAndrePoulin a continuación para obtener una explicación.
Shaunc
10

La mayoría de las soluciones proporcionadas aquí utilizan rutas de repetición alternativas, es decir (A | B) *.

Puede encontrar desbordamientos de pila en entradas grandes, ya que algunos compiladores de patrones implementan esto usando la recursividad.

Java, por ejemplo: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6337993

Algo como esto:, "(?:[^"\\]*(?:\\.)?)*"o el proporcionado por Guy Bedford reducirá la cantidad de pasos de análisis evitando la mayoría de los desbordamientos de pila.

Marc-André Poulin
fuente
9
"(?:\\"|.)*?"

Alternando el \"y el .pasa sobre comillas escapadas mientras que el cuantificador perezoso *?asegura que no se pasa del final de la cadena entre comillas. Funciona con clases RE de .NET Framework

Tosh Afanasiev
fuente
Pero falla con"\\"
Ian
/"(?:(?:\\"|[^"])*)"/gesto debería arreglar
dave
7
/"(?:[^"\\]++|\\.)*+"/

Tomado directamente de man perlreun sistema Linux con Perl 5.22.0 instalado. Como optimización, esta expresión regular usa la forma 'posesiva' de ambos +y *para evitar el retroceso, ya que se sabe de antemano que una cadena sin una cita de cierre no coincidiría en ningún caso.

ack
fuente
4
/(["\']).*?(?<!\\)(\\\\)*\1/is

debería funcionar con cualquier cadena entre comillas


fuente
1
Agradable, pero demasiado flexible para la solicitud (coincidirá con comillas simples ...). Y se puede simplificar a /".*?(?<!\)"/ a menos que me pierda algo. Ah, y algunos lenguajes (por ejemplo, JavaScript) lamentablemente no entienden las expresiones de búsqueda atrás negativas.
PhiLho
1
@PhiLho, solo usar un solo (? <! \\) fallaría en las barras invertidas escapadas al final de la cadena. Sin embargo, es cierto acerca de las búsquedas retrospectivas en JavaScript.
Markus Jarderot
4

Este funciona perfectamente en PCRE y no cae con StackOverflow.

"(.*?[^\\])??((\\\\)+)?+"

Explicación:

  1. Cada cadena entre comillas se inicia con Char: ";
  2. Puede contener cualquier número de caracteres: .*?{Lazy match}; terminando con carácter sin escape [^\\];
  3. La declaración (2) es Lazy (!) Opcional porque la cadena puede estar vacía (""). Entonces:(.*?[^\\])??
  4. Finalmente, cada cadena entre comillas termina con Char ( "), pero puede ir precedida de un número par de pares de signos de escape (\\\\)+; y es Codicioso (!) opcional: ((\\\\)+)?+{Coincidencia codiciosa}, ¡porque la cadena puede estar vacía o sin pares finales!
Vadim Sayfi
fuente
No es el patrón más eficiente del mundo, pero la idea es interesante. Tenga en cuenta que puede acortarlo así:"(.*?[^\\])?(\\\\)*"
Casimir et Hippolyte
2

aquí hay uno que funciona con "y" y puede agregar fácilmente otros al principio.

("| ') (?: \\\ 1 | [^ \ 1]) *? \ 1

utiliza la referencia inversa (\ 1) que coincide exactamente con lo que está en el primer grupo ("o ').

http://www.regular-expressions.info/backref.html

Mathias Hansen
fuente
esta es una muy buena solución, pero [^\1]debería ser reemplazada .porque no existe tal cosa como una referencia anti-retroceso, y de todos modos no importa. la primera condición siempre coincidirá antes de que suceda algo malo.
Seph Reed
@SephReed - reemplazando [^\1]con .cambiaría de manera efectiva esta expresión regular para ("|').*?\1y luego se igualaría "foo\"en "foo \" bar". Dicho esto, llegar [^\1]a trabajar realmente es difícil. @ Mathiashansen - Está mejor con lo difícil de manejar y caro (?!\1).(por lo que toda la expresión regular, con un poco de limpieza de eficiencia, sería (["'])(?:\\.|(?!\1).)*+\1. +Es opcional si su motor no lo admite.
Adam Katz
2

Una opción que no se ha tocado antes es:

  1. Invierta la cuerda.
  2. Realice la coincidencia en la cuerda invertida.
  3. Vuelva a invertir las cadenas coincidentes.

Esto tiene la ventaja adicional de poder hacer coincidir correctamente las etiquetas abiertas escapadas.

Digamos que tienes la siguiente cadena; String \"this "should" NOT match\" and "this \"should\" match" Aquí, \"this "should" NOT match\"no debe coincidir y "should"debe ser. Además de eso, this \"should\" matchdebería coincidir y \"should\"no debería.

Primero un ejemplo.

// The input string.
const myString = 'String \\"this "should" NOT match\\" and "this \\"should\\" match"';

// The RegExp.
const regExp = new RegExp(
    // Match close
    '([\'"])(?!(?:[\\\\]{2})*[\\\\](?![\\\\]))' +
    '((?:' +
        // Match escaped close quote
        '(?:\\1(?=(?:[\\\\]{2})*[\\\\](?![\\\\])))|' +
        // Match everything thats not the close quote
        '(?:(?!\\1).)' +
    '){0,})' +
    // Match open
    '(\\1)(?!(?:[\\\\]{2})*[\\\\](?![\\\\]))',
    'g'
);

// Reverse the matched strings.
matches = myString
    // Reverse the string.
    .split('').reverse().join('')
    // '"hctam "\dluohs"\ siht" dna "\hctam TON "dluohs" siht"\ gnirtS'

    // Match the quoted
    .match(regExp)
    // ['"hctam "\dluohs"\ siht"', '"dluohs"']

    // Reverse the matches
    .map(x => x.split('').reverse().join(''))
    // ['"this \"should\" match"', '"should"']

    // Re order the matches
    .reverse();
    // ['"should"', '"this \"should\" match"']

Bien, ahora para explicar la RegExp. Esta es la expresión regular que se puede dividir fácilmente en tres partes. Como sigue:

# Part 1
(['"])         # Match a closing quotation mark " or '
(?!            # As long as it's not followed by
  (?:[\\]{2})* # A pair of escape characters
  [\\]         # and a single escape
  (?![\\])     # As long as that's not followed by an escape
)
# Part 2
((?:          # Match inside the quotes
(?:           # Match option 1:
  \1          # Match the closing quote
  (?=         # As long as it's followed by
    (?:\\\\)* # A pair of escape characters
    \\        # 
    (?![\\])  # As long as that's not followed by an escape
  )           # and a single escape
)|            # OR
(?:           # Match option 2:
  (?!\1).     # Any character that isn't the closing quote
)
)*)           # Match the group 0 or more times
# Part 3
(\1)           # Match an open quotation mark that is the same as the closing one
(?!            # As long as it's not followed by
  (?:[\\]{2})* # A pair of escape characters
  [\\]         # and a single escape
  (?![\\])     # As long as that's not followed by an escape
)

Esto es probablemente mucho más claro en forma de imagen: generado usando Regulex de Jex

Imagen en github (Visualizador de expresiones regulares de JavaScript). Lo siento, no tengo una reputación lo suficientemente alta como para incluir imágenes, por lo que es solo un enlace por ahora.

Aquí hay un resumen de una función de ejemplo que utiliza este concepto que es un poco más avanzado: https://gist.github.com/scagood/bd99371c072d49a4fee29d193252f5fc#file-matchquotes-js

scagood
fuente
0

Hay que recordar que las expresiones regulares no son una solución milagrosa para todo lo relacionado con las cadenas. Algunas cosas son más sencillas de hacer con un cursor y de búsqueda lineal, manual. Una CFL haría el truco de manera bastante trivial, pero no hay muchas implementaciones de CFL (afaik).

Henrik Paul
fuente
3
Es cierto, pero este problema está dentro de las capacidades de las expresiones regulares, y hay muchas implementaciones de ellas.
Alan Moore
0

Una versión más extensa de https://stackoverflow.com/a/10786066/1794894

/"([^"\\]{50,}(\\.[^"\\]*)*)"|\'[^\'\\]{50,}(\\.[^\'\\]*)*\'|“[^”\\]{50,}(\\.[^“\\]*)*”/   

Esta versión también contiene

  1. Longitud mínima de cotización de 50
  2. Tipo extra de cotizaciones (abrir y cerrar )
Rvanlaak
fuente
0

Me equivoqué en regexpal y terminé con esta expresión regular: (No me preguntes cómo funciona, apenas lo entiendo aunque lo escribí jajaja)

"(([^"\\]?(\\\\)?)|(\\")+)+"
Petter Thowsen
fuente
0

Si se busca desde el principio, ¿quizás esto funcione?

\"((\\\")|[^\\])*\"
usuario2267983
fuente
0

Enfrenté un problema similar al intentar eliminar cadenas entre comillas que pueden interferir con el análisis de algunos archivos.

Terminé con una solución de dos pasos que supera cualquier expresión regular intrincada que se te ocurra:

 line = line.replace("\\\"","\'"); // Replace escaped quotes with something easier to handle
 line = line.replaceAll("\"([^\"]*)\"","\"x\""); // Simple is beautiful

Más fácil de leer y probablemente más eficiente.

マ ル ち ゃ ん だ よ
fuente
0

Si su IDE es IntelliJ Idea, puede olvidar todos estos dolores de cabeza y almacenar su expresión regular en una variable de cadena y, a medida que la copia y pega dentro de la comilla doble, automáticamente cambiará a un formato de expresión regular aceptable.

ejemplo en Java:

String s = "\"en_usa\":[^\\,\\}]+";

ahora puedes usar esta variable en tu expresión regular o en cualquier lugar.

Aramis NSR
fuente