RegEx: valores de agarre entre comillas


Respuestas:


361

He estado usando lo siguiente con gran éxito:

(["'])(?:(?=(\\?))\2.)*?\1

También admite comillas anidadas.

Para aquellos que desean una explicación más profunda de cómo funciona esto, aquí hay una explicación del usuario ephemient :

([""'])hacer coincidir una cita; ((?=(\\?))\2.)si existe una barra invertida, engánchela y, si eso sucede o no, haga coincidir un personaje; *?coincidir muchas veces (sin avaricia, como para no comer la cita de cierre); \1coincide con la misma cita que se usó para la apertura.


66
@steve: esto también coincidiría, de forma incorrecta, "foo\". El truco de mirar hacia adelante hace que el ?cuantificador sea posesivo (incluso si el sabor de expresiones regulares no es compatible con la ?+sintaxis o la agrupación atómica)
Robin

1
Con python esto genera un error: sre_constants.error: no se puede referir al grupo abierto
a1an

99
Esto devuelve los valores, incluidas las comillas coincidentes. ¿No hay posibilidad de devolver solo el contenido entre las comillas, como se solicitó?
Martin Schneider

44
Abusar de una anticipación como cuantificador posesivo es completamente innecesario y confuso. Solo use una alternancia:(["'])(?:\\.|[^\\])*?\1
Aran-Fey

2
¿Cómo evitar cadenas vacías?
Vikas Bansal

333

En general, el siguiente fragmento de expresión regular es lo que está buscando:

"(.*?)"

Esto utiliza el no codicioso *? operador para capturar todo hasta, pero sin incluir la próxima comilla doble. Luego, utiliza un mecanismo específico del idioma para extraer el texto coincidente.

En Python, podrías hacer:

>>> import re
>>> string = '"Foo Bar" "Another Value"'
>>> print re.findall(r'"(.*?)"', string)
['Foo Bar', 'Another Value']

11
Esto es genial, sin embargo, no maneja cadenas con comillas escapadas. por ejemplo,"hello \" world"
robbyt

Usando la coincidencia de JavaScript, esto también coincidirá con las comillas. Funcionará con iteración sobre exec como se describe aquí: stackoverflow.com/questions/7998180/…
Kiechlus

44
@robbyt Sé que es un poco tarde para una respuesta, pero ¿qué pasa con una mirada negativa hacia atrás? "(.*?(?<!\\))"
Mateus

44
Gracias, esto es más simple si está seguro de que no hay comillas escapadas para tratar.
squarecandy

Una palabra. Increíble !
Shiva Avula

89

Yo iría por:

"([^"]*)"

El [^ "] es una expresión regular para cualquier carácter, excepto ' ' '.
La razón por la que uso esto sobre el operador no codicioso es que tengo que seguir buscando eso solo para asegurarme de que estoy correcto.


1
Esto también se comporta bien entre las diferentes interpretaciones de expresiones regulares.
Phil Bennett

55
Esto me ha salvado la cordura. En la implementación RegEx de .NET, "(. *?)" No tiene el efecto deseado (no actúa de forma no codiciosa), pero "([^"] *) "sí.
Jens Neubauer

Esta es la mejor respuesta de la OMI. Gracias
Lmao 123

28

Veamos dos formas eficientes de lidiar con las comillas escapadas. Estos patrones no están diseñados para ser concisos ni estéticos, sino para ser eficientes.

Estas formas usan la discriminación de primer carácter para encontrar rápidamente comillas en la cadena sin el costo de una alternancia. (La idea es descartar rápidamente los caracteres que no son comillas sin probar las dos ramas de la alternancia).

El contenido entre comillas se describe con un bucle desenrollado (en lugar de una alternancia repetida) para ser más eficiente también: [^"\\]*(?:\\.[^"\\]*)*

Obviamente, para lidiar con cadenas que no tienen comillas balanceadas, puede usar cuantificadores posesivos en su lugar: [^"\\]*+(?:\\.[^"\\]*)*+o una solución alternativa para emularlos, para evitar demasiado retroceso. También puede elegir que una parte entre comillas pueda ser una cotización de apertura hasta la próxima cotización (sin escape) o el final de la cadena. En este caso, no es necesario utilizar cuantificadores posesivos, solo debe hacer que la última cita sea opcional.

Aviso: a veces las citas no se escapan con una barra diagonal inversa, sino repitiendo la cita. En este caso, el subpatrón de contenido se ve así:[^"]*(?:""[^"]*)*

Los patrones evitan el uso de un grupo de captura y una referencia inversa (quiero decir algo así (["']).....\1) y usan una alternancia simple pero ["']al principio, en factor.

Perl como:

["'](?:(?<=")[^"\\]*(?s:\\.[^"\\]*)*"|(?<=')[^'\\]*(?s:\\.[^'\\]*)*')

(tenga en cuenta que (?s:...)es un azúcar sintáctico para activar el modo dotall / singleline dentro del grupo sin captura. Si esta sintaxis no es compatible, puede activar fácilmente este modo para todo el patrón o reemplazar el punto con [\s\S])

(La forma en que se escribe este patrón es totalmente "manual" y no tiene en cuenta las eventuales optimizaciones internas del motor)

Script de ECMA:

(?=["'])(?:"[^"\\]*(?:\\[\s\S][^"\\]*)*"|'[^'\\]*(?:\\[\s\S][^'\\]*)*')

POSIX extendido:

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

o simplemente:

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

1
Python acepta el script ECMA con formato de cadena sin formato, es decir, r "" "Script ECMA" ""
a1an

1
Esto es genial, fue muy fácil adaptar su ECMA para trabajar con escapes de línea nueva y retornos de carro dentro de comillas dobles.
Douglas Gaskell

@ douglasg14b: Gracias. Tenga en cuenta que si desea usarlo en Javascript, solo necesita usar la notación literal /pattern/sin escapar de nada (en lugar de la notación de objeto new RegExp("(?=[\"'])(?:\"[^\"\\\\]*...");)
Casimir et Hippolyte

@ a1an: sí, pero puede usar la versión de Perl si elimina el saquí: (?s:y si coloca (?s)algún lugar en el patrón.
Casimir et Hippolyte

16

El RegEx de respuesta aceptada devuelve los valores, incluidas las comillas circundantes: "Foo Bar"y "Another Value"como coincidencias.

Aquí hay RegEx que devuelve solo los valores entre comillas (como estaba pidiendo el interrogador):

Solo comillas dobles (use el valor del grupo de captura # 1):

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

Solo comillas simples (use el valor del grupo de captura # 1):

'(.*?[^\\])'

Ambos (use el valor del grupo de captura # 2):

(["'])(.*?[^\\])\1

-

Todo el apoyo escapó y cotizaciones anidadas.


Por favor, ¿por qué esto funciona? Estaba usando, src="(.*)"pero obviamente estaba seleccionando todo antes del último ", su REGEX, sin embargo, seleccionó solo el contenido src =" ", pero no entendí cómo.
Lucas Bustamante

Yo como éste mucho por su simplicidad, pero no se ocupa de vacío o ningún valor entre comillas muy bien como descubrí
RedactedProfile

16

Curiosamente, ninguna de estas respuestas produce una expresión regular donde la coincidencia devuelta es el texto dentro de las comillas, que es lo que se solicita. MA-Madden lo intenta pero solo obtiene la partida interna como un grupo capturado en lugar de la partida completa. Una forma de hacerlo sería:

(?<=(["']\b))(?:(?=(\\?))\2.)*?(?=\1)

Se pueden ver ejemplos de esto en esta demostración https://regex101.com/r/Hbj8aP/1

La clave aquí es la mirada hacia atrás positiva al comienzo (la ?<=) y la mirada hacia adelante positiva al final (la ?=). La mirada hacia atrás está mirando hacia atrás del personaje actual para verificar una cita, si se encuentra, comience desde allí y luego la búsqueda anticipada está verificando una cita para el personaje que está adelante y si se encuentra, deténgase en ese personaje. El grupo de retrospectiva (el ["']) se envuelve entre paréntesis para crear un grupo para cualquier cotización que se encuentre al principio, luego se usa al final.(?=\1) para asegurarse de que solo se detiene cuando encuentra la cita correspondiente.

La única otra complicación es que debido a que la búsqueda anticipada en realidad no consume la cita final, se encontrará nuevamente por la búsqueda inicial que hace que coincida el texto entre las citas finales y las iniciales en la misma línea. Poner un límite de palabras en la cita de apertura ( ["']\b) ayuda con esto, aunque idealmente me gustaría pasar de la búsqueda anticipada, pero no creo que sea posible. El bit que permite caracteres escapados en el medio lo tomé directamente de la respuesta de Adam.



8

El patrón (["'])(?:(?=(\\?))\2.)*?\1anterior hace el trabajo, pero estoy preocupado por su rendimiento (no está mal, pero podría ser mejor). La mía debajo es ~ 20% más rápido.

El patrón "(.*?)"es simplemente incompleto. Mi consejo para todos los que lean esto es ¡NO LO USE!

Por ejemplo, no puede capturar muchas cadenas (si es necesario, puedo proporcionar un caso de prueba exhaustivo) como el siguiente:

$ string = '¿Cómo estás? Estoy \'bien, gracias ';

El resto de ellos son tan "buenos" como el de arriba.

Si realmente te importa tanto el rendimiento como la precisión, comienza con el siguiente:

/(['"])((\\\1|.)*?)\1/gm

En mis pruebas, cubrió cada cadena que conocí, pero si encuentra algo que no funciona, con gusto lo actualizaría por usted.

Comprueba mi patrón en un probador de expresiones regulares en línea .


1
Me gusta la simplicidad de su patrón, sin embargo, el patrón de Casimir et Hippolyte en cuanto al rendimiento elimina todas las soluciones extendidas fuera del agua. Además, parece que su patrón tiene problemas con los casos de borde extendido, como una cita escapada al final de la oración.
wp78de

7

Me gustó la solución de Eugen Mihailescu para unir el contenido entre comillas y al mismo tiempo escapar de las comillas. Sin embargo, descubrí algunos problemas para escapar y se me ocurrió la siguiente expresión regular para solucionarlos:

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

Hace el truco y sigue siendo bastante simple y fácil de mantener.

Demostración (con algunos casos de prueba más; no dude en usarlo y ampliarlo).


PD: si solo quieres el contenido entre comillas en la partida completa ( $0), y no temes el uso de penalización de rendimiento:

(?<=(['"])\b)(?:(?!\1|\\).|\\.)*(?=\1)

Desafortunadamente, sin las comillas como anclas, tuve que agregar un límite \bque no funciona bien con espacios y caracteres de límite sin palabras después de la cita inicial.

Alternativamente, modifique la versión inicial simplemente agregando un grupo y extraiga la forma de cadena$2 :

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

PPS: Si su enfoque es únicamente en la eficiencia, elija la solución de Casimir et Hippolyte ; es una buena.


observación: la segunda expresión regular pierde un valor con un signo menos -, como en las coordenadas de longitud.
Crowcoder

No cambié nada. Si no observa el problema, tal vez sea el sabor de la expresión regular que estoy usando. Estaba usando el sitio regex101, creo que el estilo php regex.
Crowcoder

Aquí está la demostración de lo que estoy hablando. Esperaba que coincidiera con la longitud (-96.74025) pero no lo hace.
Crowcoder

@Crowcoder Gracias. Sí, esto es causado por el límite de la palabra que actúa como un ancla y ayuda a evitar coincidencias superpuestas, pero no juega bien con su entrada. Un grupo adicional es en realidad la mejor opción como se indica en la respuesta actualizada.
wp78de

6

Esta versión

  • representa las cotizaciones escapadas
  • controla el retroceso

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

Esto abarca varias cadenas y no parece manejar una barra invertida doble correctamente, por ejemplo, la cadena: foo 'stri \\ ng 1' bar 'string 2' y 'string 3' Debuggex Demo
miracle2k

No puede usar una referencia inversa en una clase de caracteres.
HamZa

5

¡MÁS RESPUESTAS! Aquí está la solución que usé

\"([^\"]*?icon[^\"]*?)\"

TLDR;
reemplace el icono de la palabra con lo que está buscando en dichas citas y listo!


La forma en que esto funciona es que busca la palabra clave y no le importa qué más entre comillas. Por ejemplo:
id="fb-icon"
id="icon-close"
id="large-icon-close"
la expresión regular busca una comilla, "
luego busca cualquier grupo posible de letras que no sea "
hasta que encuentre icon
y cualquier grupo posible de letras que no "
sea, luego busca un cierre"


1
Muchas gracias. fue capaz de reemplazar cada aparición de name="value"con name={"value"}ya que la expresión regular de esta respuesta regresa icon/ valuecomo el segundo grupo (a diferencia de la respuesta aceptada). Buscar : =\"([^\"]*?[^\"]*?)\" Reemplazar :={"$1"}
Palisand

¿Te importaría explicar el voto negativo? funciona bien desde algunas situaciones.
James Harrington

Me estas respondiendo?
Palisand

@Palisand nadie votó esta publicación el otro día sin ninguna explicación.
James Harrington

esta parece ser la única respuesta que encuentra un texto específico entre comillas
Top-Master

4

Me gustó la versión más expansiva de Axeman, pero tuve algunos problemas con ella (por ejemplo, no coincidía

foo "string \\ string" bar

o

foo "string1"   bar   "string2"

correctamente, así que intenté arreglarlo:

# opening quote
(["'])
   (
     # repeat (non-greedy, so we don't span multiple strings)
     (?:
       # anything, except not the opening quote, and not 
       # a backslash, which are handled separately.
       (?!\1)[^\\]
       |
       # consume any double backslash (unnecessary?)
       (?:\\\\)*       
       |
       # Allow backslash to escape characters
       \\.
     )*?
   )
# same character as opening quote
\1

3
string = "\" foo bar\" \"loloo\""
print re.findall(r'"(.*?)"',string)

solo prueba esto, funciona como un encanto !!!

\ indica salto de caracteres


Si esa primera línea es el código real de Python, creará la cadena " foo bar" "loloo". Sospecho que quería decir que para envolver en una cadena de texto, como lo hizo con la expresión regular: r'"\" foo bar\" \"loloo\""'. Utilice las excelentes capacidades de formato de SO siempre que sea apropiado. No se trata solo de cosméticos; Literalmente, no podemos decir qué está tratando de decir si no los usa. ¡Y bienvenido a Stack Overflow !
Alan Moore

gracias por el consejo alan, en realidad soy nuevo en esta comunidad, la próxima vez seguramente tendré en cuenta todo esto ... sinceras disculpas.
mobman

2

A diferencia de la respuesta de Adam, tengo una simple pero trabajada:

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

Y solo agregue paréntesis si desea obtener contenido entre comillas como esta:

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

Luego hace $1coincidir el carácter entre comillas y la $2cadena de contenido.


1
echo 'junk "Foo Bar" not empty one "" this "but this" and this neither' | sed 's/[^\"]*\"\([^\"]*\)\"[^\"]*/>\1</g'

Esto resultará en:> Foo Bar <> <> pero esto <

Aquí mostré la cadena de resultados entre> <'s para mayor claridad, también usando la versión no codiciosa con este comando sed primero tiramos la basura antes y después de ese ""' s y luego reemplazamos esto con la parte entre el "" 's y rodear esto por> <' s.


1

De Greg H. pude crear esta expresión regular para satisfacer mis necesidades.

Necesitaba hacer coincidir un valor específico que se calificaba al estar entre comillas. Debe ser una coincidencia completa, ninguna coincidencia parcial podría desencadenar un golpe

por ejemplo, "prueba" no pudo coincidir con "prueba2".

reg = r"""(['"])(%s)\1"""
if re.search(reg%(needle), haystack, re.IGNORECASE):
    print "winning..."

Cazador


1

Si está tratando de encontrar cadenas que solo tengan un sufijo determinado, como la sintaxis de puntos, puede intentar esto:

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

¿Dónde .localizedestá el sufijo?

Ejemplo:

print("this is something I need to return".localized + "so is this".localized + "but this is not")

Capturará "this is something I need to return".localizedy "so is this".localizedno "but this is not".


1

Una respuesta complementaria para el subconjunto de codificadores de Microsoft VBA solo uno usa la biblioteca Microsoft VBScript Regular Expressions 5.5y esto proporciona el siguiente código

Sub TestRegularExpression()

    Dim oRE As VBScript_RegExp_55.RegExp    '* Tools->References: Microsoft VBScript Regular Expressions 5.5
    Set oRE = New VBScript_RegExp_55.RegExp

    oRE.Pattern = """([^""]*)"""


    oRE.Global = True

    Dim sTest As String
    sTest = """Foo Bar"" ""Another Value"" something else"

    Debug.Assert oRE.test(sTest)

    Dim oMatchCol As VBScript_RegExp_55.MatchCollection
    Set oMatchCol = oRE.Execute(sTest)
    Debug.Assert oMatchCol.Count = 2

    Dim oMatch As Match
    For Each oMatch In oMatchCol
        Debug.Print oMatch.SubMatches(0)

    Next oMatch

End Sub

0

Para mí trabajó este:

|([\'"])(.*?)\1|i

Lo he usado en una oración como esta:

preg_match_all('|([\'"])(.*?)\1|i', $cont, $matches);

y funcionó muy bien


Una debilidad de este enfoque es que coincidirá cuando una cadena comienza con una comilla simple y termina con una comilla doble, o viceversa.
Ghopper21

También tiene problemas para atrapar "No olvides la @" - Se detiene después de "Don".
Benny Neugebauer

0

Todas las respuestas anteriores son buenas ... ¡excepto que NO admiten todos los caracteres Unicode! en ECMA Script (Javascript)

Si es un usuario de Node, es posible que desee la versión modificada de la respuesta aceptada que admite todos los caracteres unicode:

/(?<=((?<=[\s,.:;"']|^)["']))(?:(?=(\\?))\2.)*?(?=\1)/gmu

Tratar aquí .


1
¿Qué es un personaje no unicode? AFAIK unicode cubre todos los personajes.
Toto

1
¿Por qué supones que es una pregunta de JavaScript? Además, mirar hacia atrás no es compatible con todos los navegadores, regex101 tiros? The preceding token is not quantifiable
Toto

@Toto, lo que quiero decir es que "no admite todos los caracteres Unicode". Gracias. Si bien la pregunta es sobre la expresión regular en general, no quiero enfatizar que el uso de aserciones de límites de palabras causaría un comportamiento no deseado en el Javascript. Y, por supuesto, aunque los Javascripts son generalmente para el navegador, también hay Nodo.
Donovan P
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.