Regex para string entre aspas com aspas escapadas

120

Como obtenho a substring " It's big \"problem "usando uma expressão regular?

s = ' function(){  return " It\'s big \"problem  ";  }';     
David
fonte
1
Como você encontra "It" em uma string que contém apenas "Is"? Eu o corrigi, mas não sei quais convenções de aspas simples / de escape se aplicam no idioma que você está usando.
Jonathan Leffler
2
Na verdade, olhando as datas, vejo que a outra pergunta é uma duplicata desta. De qualquer maneira, verifique minha resposta .
Ridgerunner
@ridgerunner: Estou votando para fechar isso como você sugeriu. É verdade que outra pergunta é mais recente, mas também é muito melhor (graças principalmente à sua resposta).
Alan Moore

Respostas:

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

Trabalha no The Regex Coach e no PCRE Workbench.

Exemplo de teste em JavaScript:

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

PhiLho
fonte
23
Faz sentido. Inglês simples: duas aspas envolvendo zero ou mais de "qualquer caractere que não seja uma citação ou uma barra invertida" ou "uma barra invertida seguida por qualquer caractere". Eu não posso acreditar que eu não acho que para fazer isso ...
Ajedi32
7
Eu vou me responder. =) (?:...)é um grupo passivo ou não captador. Isso significa que não pode ser referenciado posteriormente mais tarde.
Magras
depois de pesquisar bastante e testar bastante, essa é a única e real solução que encontrei para esse problema comum. Obrigado!
Cancerbero
9
obrigado por isso. eu queria para combinar aspas simples, bem assim i acabou adaptando-a isto:/(["'])(?:[^\1\\]|\\.)*?\1/
leo
Com var s = ' my \\"new\\" string and \"this should be matched\"';, essa abordagem levará a resultados inesperados.
Wiktor Stribiżew
32

Este é um exemplo de nanorc.sample disponível em muitas distribuições linux. É usado para destacar a sintaxe de seqüências de caracteres de estilo C

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

fonte
Com var s = ' my \\"new\\" string and \"this should be matched\"';, essa abordagem levará a resultados inesperados.
Wiktor Stribiżew
1
c.nanorc foi o primeiro lugar que eu fui. Não foi possível obtê-lo para o trabalho como parte de um literal de cadeia C até duas vezes escapando tudo como esta" \"(\\\\.|[^\\\"])*\" "
hellork
Isso funciona com as funções egrep e re_comp / re_exec da libc.
Fk0
19

Conforme fornecido pelo ePharaoh, a resposta é

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

Para que o descrito acima se aplique a cadeias simples ou duplas, use

/"([^"\\]*(\\.[^"\\]*)*)"|\'([^\'\\]*(\\.[^\'\\]*)*)\'/
Guy Bedford
fonte
2
Este é o único conjunto que funcionou para mim com uma única e grande seqüência de caracteres citada de 1,5 KB contendo 99 escapes. Todas as outras expressões nesta página foram interrompidas no meu editor de texto com um erro de estouro. Embora a maioria aqui funcione no navegador, apenas algo a ter em mente. Fiddle: jsfiddle.net/aow20y0L
Beejor
3
Veja a resposta de @ MarcAndrePoulin abaixo para explicação.
shaunc
10

A maioria das soluções fornecidas aqui usa caminhos alternativos de repetição, como (A | B) *.

Você pode encontrar estouros de pilha em entradas grandes, pois algum compilador de padrões implementa isso usando recursão.

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

Algo assim: "(?:[^"\\]*(?:\\.)?)*"ou o fornecido por Guy Bedford reduzirá a quantidade de etapas de análise, evitando a maioria dos estouros de pilha.

Marc-André Poulin
fonte
7
/"(?:[^"\\]++|\\.)*+"/

Retirado diretamente de man perlreum sistema Linux com o Perl 5.22.0 instalado. Como uma otimização, esse regex usa a forma 'posessiva' de ambos +e *para impedir o retorno, pois é sabido de antemão que uma sequência sem uma citação de fechamento não corresponderia em nenhum caso.

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

deve funcionar com qualquer string entre aspas


fonte
1
Agradável, mas flexível demais para a solicitação (corresponderá aspas simples ...). E pode ser simplificado para /".*?(?<!\)"/, a menos que eu perca alguma coisa. Ah, e algumas linguagens (por exemplo, JavaScript) infelizmente não entendem expressões negativas.
PhiLho
1
@ PhiLho, apenas o uso de um único (? <! \\) falharia nas barras invertidas escapadas no final da string. Porém, é verdade sobre os atrasos no JavaScript.
Markus Jarderot
4

Este funciona perfeitamente no PCRE e não cai com o StackOverflow.

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

Explicação:

  1. Cada string começa com Char: ";
  2. Pode conter qualquer número de caracteres: .*?{Lazy match}; terminando com caractere sem escape [^\\];
  3. A instrução (2) é preguiçosa (!) Opcional porque a cadeia pode estar vazia (""). Assim:(.*?[^\\])??
  4. Finalmente, toda string citada termina com Char ( "), mas pode ser precedida de um número par de pares de sinais de escape (\\\\)+; e é Greedy (!) opcional: ((\\\\)+)?+{Correspondência Greedy}, a seqüência de caracteres pode estar vazia ou sem pares finais!
Vadim Sayfi
fonte
Não é o padrão mais eficiente do mundo, mas a ideia é interessante. Observe que você pode reduzi-lo assim:"(.*?[^\\])?(\\\\)*"
Casimir et Hippolyte
2

aqui está um que funciona com "e" e você adiciona outros facilmente no início.

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

ele usa a referência anterior (\ 1) corresponde exatamente ao que está no primeiro grupo ("ou ').

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

mathias hansen
fonte
Esta é uma solução muito boa, mas [^\1]deve ser substituída por, .porque não existe uma referência anti-retorno e isso não importa. a primeira condição sempre corresponderá antes que algo ruim possa acontecer.
Seph Reed
@SephReed - substituindo [^\1]com .efetivamente mudar esta regex para ("|').*?\1e em seguida, ele iria corresponder "foo\"no "foo \" bar". Dito isto, [^\1]é realmente difícil trabalhar. @ Mathiashansen - Você é melhor fora com o complicado e caro (?!\1).(para o todo regex, com alguma limpeza eficiência, seria (["'])(?:\\.|(?!\1).)*+\1A. +É opcional se o seu motor não apoiá-lo.
Adam Katz
2

Uma opção que não foi abordada antes é:

  1. Inverta a string.
  2. Faça a correspondência na sequência invertida.
  3. Reverta as seqüências correspondentes.

Isso tem o bônus adicional de poder corresponder corretamente as tags abertas com escape.

Digamos que você tenha a seguinte string; String \"this "should" NOT match\" and "this \"should\" match" Aqui, \"this "should" NOT match\"não deve ser correspondido e "should"deve ser. Além disso this \"should\" match, isso deve corresponder e \"should\"não.

Primeiro um exemplo.

// 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"']

Ok, agora para explicar o RegExp. Este é o regexp pode ser facilmente dividido em três partes. Do seguinte modo:

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

Provavelmente, isso é muito mais claro na forma de imagem: gerado usando o Regulex de Jex

Imagem no github (JavaScript Regular Expression Visualizer.) Desculpe, não tenho uma reputação suficientemente alta para incluir imagens; portanto, é apenas um link no momento.

Aqui está um exemplo de uma função de exemplo usando esse conceito um pouco mais avançado: https://gist.github.com/scagood/bd99371c072d49a4fee29d193252f5fc#file-matchquotes-js

scagood
fonte
0

É preciso lembrar que os regexps não são uma bala de prata para tudo que é y. Algumas coisas são mais simples de fazer com um cursor e linear, manual, buscando. Uma CFL faria o truque de maneira bastante trivial, mas não há muitas implementações de CFL (afaik).

Henrik Paul
fonte
3
É verdade, mas esse problema está dentro dos recursos das regexes e existem muitas implementações delas.
Alan Moore
0

Uma versão mais extensa do https://stackoverflow.com/a/10786066/1794894

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

Esta versão também contém

  1. Comprimento mínimo da cotação de 50
  2. Tipo de cotação extra (abrir e fechar )
Rvanlaak
fonte
0

Mexi no regexpal e acabei com este regex: (Não me pergunte como funciona, eu mal entendo mesmo que eu o tenha escrito lol)

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

Se for pesquisado desde o início, talvez isso possa funcionar?

\"((\\\")|[^\\])*\"
user2267983
fonte
0

Eu enfrentei um problema semelhante ao tentar remover as seqüências de caracteres citadas que podem interferir na análise de alguns arquivos.

Acabei com uma solução em duas etapas que supera qualquer regex complicado que você possa criar:

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

Mais fácil de ler e provavelmente mais eficiente.

マ ル ち ゃ ん だ よ
fonte
0

Se o seu IDE for IntelliJ Idea, você poderá esquecer todas essas dores de cabeça e armazenar sua regex em uma variável String e, ao copiar e colar dentro da aspas duplas, ela mudará automaticamente para um formato aceitável para regex.

exemplo em Java:

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

agora você pode usar essa variável no seu regexp ou em qualquer outro lugar.

Aramis NSR
fonte