RegEx: agarrando valores entre aspas


Respostas:


361

Eu tenho usado o seguinte com grande sucesso:

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

Ele também suporta aspas aninhadas.

Para aqueles que desejam uma explicação mais profunda de como isso funciona, aqui está uma explicação do efemiente do usuário :

([""'])corresponder a uma cotação; ((?=(\\?))\2.)se a barra invertida existir, engula e, se isso acontece ou não, combine um caractere; *?corresponder muitas vezes (sem avidez, para não comer a citação de fechamento); \1corresponder à mesma citação usada para a abertura.


6
@ Steve: isso também corresponderia, incorretamente "foo\",. O truque frente olhar faz com que o ?possessivo quantificador (mesmo se o sabor de regex não suporta a ?+sintaxe ou atômica agrupamento)
Robin

1
Com python, isso gera um erro: sre_constants.error: não pode se referir ao grupo aberto
a1an

9
Isso retorna os valores, incluindo as aspas correspondentes. Não há chance de retornar apenas o conteúdo entre as aspas, conforme solicitado?
Martin Schneider

4
Abusar de um lookahead como um quantificador possessivo é completamente desnecessário e confuso. Basta usar uma alternância:(["'])(?:\\.|[^\\])*?\1
Aran-Fey

2
como evitar seqüências de caracteres vazias?
Vikas Bansal

333

Em geral, o seguinte fragmento de expressão regular é o que você está procurando:

"(.*?)"

Isso usa o não-ganancioso *? operador para capturar tudo, mas sem incluir a próxima cotação dupla. Em seguida, você usa um mecanismo específico do idioma para extrair o texto correspondente.

No Python, você pode fazer:

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

11
Isso é ótimo, no entanto, ele não manipula seqüências de caracteres com aspas escapadas. por exemplo,"hello \" world"
robbyt 5/02

Usando a correspondência do JavaScript, isso também corresponderá às aspas. Ele vai trabalhar com iteração sobre exec como descrito aqui: stackoverflow.com/questions/7998180/...
Kiechlus

4
@robbyt Eu sei que é um pouco tarde para uma resposta, mas que tal um olhar negativo por trás? "(.*?(?<!\\))"
Mateus

4
Obrigado - isso é mais simples se você tiver certeza de que não há aspas escapadas para lidar.
Squarecandy

Uma palavra. Impressionante !
Shiva Avula

89

Eu iria para:

"([^"]*)"

O [^ "] é regex para qualquer caractere, exceto ' " '
A razão pela qual eu uso isso em muitos operadores não gananciosos é que tenho que continuar pesquisando isso apenas para ter certeza de que está correto.


1
Isso também se comporta bem entre diferentes interpretações de expressões regulares.
Phil Bennett

5
Isso salvou minha sanidade. Na implementação do RegEx do .NET, "(. *?)" Não tem o efeito desejado (não age de maneira não gananciosa), mas "([^"] *) "possui.
Jens Neubauer

Esta é a melhor resposta imo. Obrigado
Lmao 123

28

Vamos ver duas maneiras eficientes de lidar com aspas escapadas. Esses padrões não são projetados para serem concisos nem estéticos, mas para serem eficientes.

Essas maneiras usam a discriminação do primeiro caractere para encontrar rapidamente aspas na string sem o custo de uma alternância. (A idéia é descartar rapidamente caracteres que não são aspas sem testar os dois ramos da alternância.)

O conteúdo entre aspas é descrito com um loop desenrolado (em vez de uma alternância repetida) para ser mais eficiente também: [^"\\]*(?:\\.[^"\\]*)*

Obviamente, para lidar com seqüências de caracteres que não possuem aspas balanceadas, você pode usar quantificadores possessivos: [^"\\]*+(?:\\.[^"\\]*)*+ou uma solução alternativa para emulá-los, para evitar muitos retrocessos. Você também pode escolher que uma parte entre aspas possa ser uma cotação de abertura até a próxima cotação (sem escape) ou o final da sequência. Nesse caso, não há necessidade de usar quantificadores possessivos, você só precisa tornar a última cotação opcional.

Aviso: às vezes as aspas não são escapadas com uma barra invertida, mas repetindo-a. Nesse caso, o subpadrão de conteúdo fica assim:[^"]*(?:""[^"]*)*

Os padrões evitam o uso de um grupo de captura e uma referência anterior (quero dizer algo como (["']).....\1) e usam uma alternância simples, mas com ["']no início, em fator.

Perl gosta:

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

(observe que (?s:...)é um açúcar sintático para ativar o modo dotall / linha única dentro do grupo que não captura. Se essa sintaxe não for suportada, você poderá facilmente ativar esse modo para todo o padrão ou substituir o ponto [\s\S])

(A maneira como esse padrão é escrito é totalmente "manual" e não leva em consideração as eventuais otimizações internas do mecanismo)

Script ECMA:

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

POSIX estendido:

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

ou simplesmente:

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

1
O Python aceita o script ECMA com formato de string bruto, ou seja, r "" "ECMA script" ""
a1an

1
Isso é brilhante, foi muito fácil adaptar seu ECMA para trabalhar com escapamentos de nova linha e retornos de carro entre aspas duplas.
Douglas Gaskell

@ douglasg14b: Obrigado. Note que se você quiser usá-lo em Javascript, você só precisa usar a notação literal /pattern/sem escapar nada (em vez da notação de objeto new RegExp("(?=[\"'])(?:\"[^\"\\\\]*...");)
Casimir et Hippolyte

@ a1an: yes, mas você pode usar a versão Perl se você remover o shere: (?s:e se você colocar (?s)em algum lugar do padrão.
Casimir et Hippolyte

16

O RegEx da resposta aceita retorna os valores, incluindo suas aspas: "Foo Bar"e "Another Value"como correspondências.

Aqui estão RegEx que retornam apenas os valores entre aspas (como o questionador estava pedindo):

Somente aspas duplas (use o valor do grupo de captura nº 1):

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

Somente aspas simples (use o valor do grupo de captura nº 1):

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

Ambos (use o valor do grupo de captura nº 2):

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

-

Todo o suporte escapou e citações aninhadas.


Por favor, por que isso funciona? Eu estava usando src="(.*)", mas, obviamente, ele estava selecionando tudo antes da última", o seu REGEX, porém, selecionado apenas o src = '' conteúdo, mas eu não entendia como?
Lucas Bustamante

I como este muito por sua simplicidade, mas ele não controla vazia ou nenhum valor entre aspas muito bem como eu descobri
RedactedProfile

16

Curiosamente, nenhuma dessas respostas produz um regex em que a correspondência retornada é o texto dentro das aspas, o que é solicitado. MA-Madden tenta, mas apenas recebe a partida interna como um grupo capturado, e não a partida inteira. Uma maneira de fazer isso seria:

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

Exemplos para isso podem ser vistos nesta demonstração https://regex101.com/r/Hbj8aP/1

A chave aqui é o olhar positivo por trás no início (o ?<=) e o olhar positivo no final (o ?=). O lookbehind está olhando por trás do caractere atual para procurar uma cotação, se encontrado, em seguida, começa a partir daí e o lookahead verifica o personagem à frente para obter uma cotação e, se encontrado, interrompe esse caractere. O grupo lookbehind (the ["']) é colocado entre colchetes para criar um grupo para a citação que foi encontrada no início, depois é usada no final da lookahead (?=\1)para garantir que só pare quando encontrar a citação correspondente.

A única outra complicação é que, como a cabeça de impressão realmente não consome a citação final, ela será encontrada novamente pela aparência inicial, que faz com que o texto entre aspas finais e iniciais na mesma linha seja correspondido. Colocar um limite de palavras na citação de abertura ( ["']\b) ajuda com isso, embora, idealmente, eu gostaria de ir além da aparência, mas não acho que isso seja possível. A parte que permite caracteres de escape no meio, tirei diretamente da resposta de Adam.



8

O padrão (["'])(?:(?=(\\?))\2.)*?\1acima faz o trabalho, mas estou preocupado com o desempenho (não é ruim, mas poderia ser melhor). Mina abaixo é ~ 20% mais rápido.

O padrão "(.*?)"está incompleto. Meu conselho para todos que estão lendo isso é apenas NÃO O USE !!!

Por exemplo, ele não pode capturar muitas strings (se necessário, posso fornecer um caso de teste exaustivo) como o abaixo:

$ string = 'Como você está? Estou \'bem, obrigado ';

O resto deles é tão "bom" quanto o acima.

Se você realmente se importa com desempenho e precisão, comece com o abaixo:

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

Nos meus testes, abrangia todas as strings que conheci, mas se você encontrar algo que não funcione, eu o atualizaria com prazer.

Verifique meu padrão em um testador de regex online .


1
Gosto da simplicidade do seu padrão, no entanto, o padrão de Casimir et Hippolyte em termos de desempenho tira todas as soluções estendidas da água. Além disso, parece que seu padrão tem problemas com casos extremos estendidos, como uma citação escapada no final da frase.
Wp78de 13/0518

7

Gostei da solução de Eugen Mihailescu para combinar o conteúdo entre aspas, permitindo escapar das aspas. No entanto, descobri alguns problemas com o escape e criei o seguinte regex para corrigi-los:

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

Ele faz o truque e ainda é bastante simples e fácil de manter.

Demonstração (com mais alguns casos de teste; fique à vontade para usá-lo e expandi-lo).


PS: Se você deseja apenas o conteúdo entre aspas na correspondência completa ( $0) e não tem medo da penalidade de desempenho, use:

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

Infelizmente, sem as aspas como âncoras, tive que adicionar um limite \bque não funcione bem com espaços e caracteres de limite que não sejam palavras após a citação inicial.

Como alternativa, modifique a versão inicial simplesmente adicionando um grupo e extraia o formulário da string$2 :

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

PPS: Se o seu foco é exclusivamente a eficiência, siga a solução de Casimir et Hippolyte ; é um bom.


observação: o segundo regex perde um valor com um sinal de menos -, como nas coordenadas de longitude.
Crowcoder

Eu não mudei nada. Se você não observar o problema, talvez seja o sabor do regex que estou usando. Eu estava usando o regex101site, acho que o regex estilo php.
Crowcoder

Aqui está a demonstração do que estou falando. Eu esperava que correspondesse à longitude (-96,74025), mas não corresponde.
Crowcoder

@Crowcoder Obrigado. Sim, isso é causado pelo limite da palavra que atua como uma âncora e ajuda a evitar correspondências sobrepostas, mas não é agradável com sua entrada. Um grupo adicional é realmente a melhor opção, conforme observado na resposta atualizada.
wp78de

6

Esta versão

  • contas de cotações de escape
  • controla o retorno

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

Isso abrange várias seqüências de caracteres e parece não manipular uma barra invertida dupla corretamente, por exemplo, a string: foo 'stri \\ ng 1' bar 'string 2' e 'string 3' Debuggex Demo
miracle2k

Você não pode usar uma referência anterior em uma classe de personagem.
Hamza

5

MAIS RESPOSTAS! Aqui está a solução que eu usei

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

TLDR;
substitua o ícone da palavra pelo que você está procurando nas citações e pronto!


A maneira como isso funciona é a procura pela palavra-chave e não se importa com o que mais há entre as aspas. EG:
id="fb-icon"
id="icon-close"
id="large-icon-close"
o regex procura uma marca de citação "
e, em seguida, procura qualquer grupo possível de letras que não seja "
até encontrar icon
e qualquer grupo possível de letras que não "
seja, então busca um fechamento"


1
Muito obrigado. foi capaz de substituir todas as ocorrências de name="value"com, name={"value"}já que o regex dessa resposta retorna icon/ valuecomo o segundo grupo (diferentemente da resposta aceita). Encontre : =\"([^\"]*?[^\"]*?)\" Substitua :={"$1"}
Palisand

Se importa em explicar o voto negativo? funciona bem em algumas situações.
James Harrington

Você está me respondendo?
Palisand

@Palis e ninguém votou negativamente neste post no outro dia, sem explicação.
James Harrington

esta parece ser a única resposta que encontra um texto específico dentro de aspas
Top-Master

4

Eu gostei da versão mais abrangente do Axeman, mas tive alguns problemas com ela (não combinava, por exemplo,

foo "string \\ string" bar

ou

foo "string1"   bar   "string2"

corretamente, então tentei corrigi-lo:

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

apenas tente isso, funciona como um encanto !!!

\ indica pular caractere


Se essa primeira linha for o código Python real, ele criará a string " foo bar" "loloo". Eu suspeito que você significou para dispor que em uma corda crua como você fez com o regex: r'"\" foo bar\" \"loloo\""'. Utilize os excelentes recursos de formatação do SO sempre que apropriado. Não são apenas cosméticos; literalmente, não podemos dizer o que você está tentando dizer se não os usar. E bem-vindo ao Stack Overflow !
Alan Moore

obrigado pelo conselho alan, sou realmente novo nessa comunidade, da próxima vez certamente vou manter tudo isso em mente ... desculpas sinceras.
mobman

2

Diferentemente da resposta de Adam, eu tenho uma simples, mas que funcionou:

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

E adicione parênteses se quiser obter conteúdo entre aspas como este:

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

Em seguida, $1corresponde ao caractere de cotação e à $2sequência de caracteres do conteúdo.


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

Isso resultará em:> Foo Bar <> <> mas isso <

Aqui eu mostrei a sequência de resultados entre> <'s para maior clareza, também usando a versão não gananciosa com este comando sed, nós jogamos fora o lixo antes e depois dos ""' s e depois substituí-lo pela parte entre "" e envolva-o com> <'s.


1

De Greg H. eu pude criar esse regex para atender às minhas necessidades.

Eu precisava corresponder a um valor específico qualificado por estar entre aspas. Deve ser uma correspondência completa, nenhuma correspondência parcial pode causar um acerto

por exemplo, "teste" não pode corresponder a "teste2".

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

Caçador


1

Se você estiver tentando encontrar seqüências que possuem apenas um sufixo, como sintaxe de ponto, tente:

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

Onde .localizedestá o sufixo?

Exemplo:

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

Ele irá capturar "this is something I need to return".localizede "so is this".localizedmas não "but this is not".


1

Uma resposta suplementar para o subconjunto de codificadores Microsoft VBA, apenas um usa a biblioteca Microsoft VBScript Regular Expressions 5.5e isso fornece o seguinte 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 mim trabalhou este:

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

Eu usei em uma frase como esta:

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

e funcionou muito bem.


Uma fraqueza dessa abordagem é que ela corresponderá quando uma sequência começar com uma aspas simples e terminar com aspas duplas ou vice-versa.
Ghopper21

Ele também tem problemas para capturar "Não esqueça o @" - para depois de "Don".
Benny Neugebauer

0

Todas as respostas acima são boas .... exceto que NÃO suportam todos os caracteres unicode! em ECMA Script (Javascript)

Se você é um usuário do Nó, convém a versão modificada da resposta aceita que suporte todos os caracteres unicode:

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

Tente aqui .


1
O que é um caractere não unicode? O unicode AFAIK abrange todos os caracteres.
Toto

1
Por que você acha que é uma pergunta javascript? Além disso, o lookbehind não é suportado em todos os navegadores, lança o regex101? The preceding token is not quantifiable
Toto

@Toto, o que quero dizer é "não suporta todos os caracteres unicode". Obrigado. Embora a questão seja sobre regex em geral, não quero enfatizar que o uso de asserções de limite de palavras causaria comportamento indesejado no Javascript. E, é claro, embora os Javascripts sejam geralmente para navegador, também há Node.
Donovan P
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.