Regex per la stringa tra virgolette con virgolette di escape


122

Come ottengo la sottostringa " It's big \"problem "utilizzando un'espressione regolare?

s = ' function(){  return " It\'s big \"problem  ";  }';     

1
Come si fa a trovare "È" in una stringa che contiene solo "È"? Lo aggiusterei per te, ma non so quali convenzioni di virgolette singole / escape si applicano nella lingua che stai utilizzando.
Jonathan Leffler


2
Effettivamente, guardando le date, vedo che l'altra domanda è un duplicato di questa. Ad ogni modo, assicurati di controllare la mia risposta .
ridgerunner

@ridgerunner: voto per chiuderlo come hai suggerito. È vero che l'altra domanda è più recente, ma è anche molto meglio (grazie soprattutto alla tua risposta).
Alan Moore

Risposte:


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

Funziona in The Regex Coach e PCRE Workbench.

Esempio di test in JavaScript:

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


24
Ha senso. Inglese semplice: due virgolette che racchiudono zero o più di "qualsiasi carattere che non sia una citazione o una barra rovesciata" o "una barra rovesciata seguita da qualsiasi carattere". Non posso credere di non aver pensato di farlo ...
Ajedi32

7
Mi risponderò. =) (?:...)è un gruppo passivo o non di acquisizione. Significa che non è possibile fare riferimento indietro in seguito.
magras

dopo aver cercato e testato molto questa è la vera e unica soluzione che ho trovato a questo problema comune. Grazie!
cancerbero

10
grazie per questo. volevo abbinare anche le virgolette singole, quindi ho finito per adattarlo a questo:/(["'])(?:[^\1\\]|\\.)*?\1/
leo

Con var s = ' my \\"new\\" string and \"this should be matched\"';, questo approccio porterà a risultati inaspettati.
Wiktor Stribiżew


19

Come fornito da ePharaoh, la risposta è

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

Per fare in modo che quanto sopra si applichi a stringhe tra virgolette singole o doppie, utilizzare

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

2
Questo è l'unico set che ha funzionato per me con una singola, grande stringa quotata da 1,5 KB contenente 99 escape. Ogni altra espressione in questa pagina è stata interrotta nel mio editor di testo con un errore di overflow. Anche se la maggior parte qui funziona nel browser, è solo qualcosa da tenere a mente. Fiddle: jsfiddle.net/aow20y0L
Beejor

3
Vedi la risposta di @ MarcAndrePoulin di seguito per la spiegazione.
Shaunc

10

La maggior parte delle soluzioni fornite qui utilizza percorsi di ripetizione alternativi, ad esempio (A | B) *.

È possibile riscontrare overflow dello stack su input di grandi dimensioni poiché alcuni compilatori di pattern lo implementano utilizzando la ricorsione.

Java ad esempio: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6337993

Qualcosa del genere: "(?:[^"\\]*(?:\\.)?)*"o quello fornito da Guy Bedford ridurrà la quantità di passaggi di analisi evitando la maggior parte degli overflow dello stack.



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

Preso direttamente da man perlreun sistema Linux con Perl 5.22.0 installato. Come ottimizzazione, questa regex utilizza la forma "positiva" di entrambi +e *per impedire il backtracking, poiché è noto in anticipo che una stringa senza virgolette di chiusura non corrisponderebbe in ogni caso.


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

dovrebbe funzionare con qualsiasi stringa tra virgolette


1
Bello, ma troppo flessibile per la richiesta (corrisponderà alle virgolette singole ...). E può essere semplificato in /".*?(?<!\)"/ a meno che non mi perda qualcosa. Oh, e alcuni linguaggi (es. JavaScript) purtroppo non capiscono le espressioni di lookbehind negative.
PhiLho

1
@PhiLho, usando solo un singolo (? <! \\) fallirebbe con i backslash con escape alla fine della stringa. Tuttavia, è vero per i look-behind in JavaScript.
Markus Jarderot

4

Questo funziona perfettamente su PCRE e non cade con StackOverflow.

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

Spiegazione:

  1. Ogni stringa tra virgolette inizia con Char: " ;
  2. Può contenere un numero qualsiasi di caratteri: .*?{Lazy match}; che termina con un carattere non di escape[^\\] ;
  3. L'istruzione (2) è Lazy (!) Opzionale perché la stringa può essere vuota (""). Così:(.*?[^\\])??
  4. Infine, ogni stringa tra virgolette termina con Char ( "), ma può essere preceduta da un numero pari di coppie di segni di escape (\\\\)+; ed è Greedy (!) opzionale: ((\\\\)+)?+{Greedy matching}, perché la stringa può essere vuota o senza coppie finali!

Non è il modello più efficiente del mondo, ma l'idea è interessante. Nota che puoi accorciarlo in questo modo:"(.*?[^\\])?(\\\\)*"
Casimir et Hippolyte

2

eccone uno che funziona con entrambi "e 'e ne aggiungi facilmente altri all'inizio.

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

usa il backreference (\ 1) che corrisponde esattamente a ciò che è nel primo gruppo ("o ').

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


questa è un'ottima soluzione, ma [^\1]dovrebbe essere sostituita con .perché non esiste un anti-back-reference, e comunque non ha importanza. la prima condizione corrisponderà sempre prima che possa accadere qualcosa di brutto.
Seph Reed

@SephReed - sostituendo [^\1]con .cambierebbe in modo efficace questa regex per ("|').*?\1e poi sarebbe partita "foo\"in "foo \" bar". Detto questo, mettersi [^\1]al lavoro è davvero difficile. @ Mathiashansen - Stai meglio con quelli ingombranti e costosi (?!\1).(quindi l'intera regex, con un po 'di pulizia dell'efficienza, sarebbe (["'])(?:\\.|(?!\1).)*+\1. +È opzionale se il tuo motore non lo supporta.
Adam Katz

2

Un'opzione che non è stata toccata prima è:

  1. Inverti la stringa.
  2. Eseguire la corrispondenza sulla stringa invertita.
  3. Invertire nuovamente le stringhe abbinate.

Questo ha il vantaggio aggiuntivo di poter abbinare correttamente i tag aperti con escape.

Diciamo che hai avuto la seguente stringa; String \"this "should" NOT match\" and "this \"should\" match" Qui, \"this "should" NOT match\"non dovrebbe essere abbinato e "should"dovrebbe essere. Inoltre this \"should\" matchdovrebbe essere abbinato e \"should\"non dovrebbe.

Prima un esempio.

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

Va bene, ora per spiegare la RegExp. Questa è la regexp che può essere facilmente suddivisa in tre parti. Come segue:

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

Questo è probabilmente molto più chiaro nella forma dell'immagine: generata usando Regulex di Jex

Immagine su github (JavaScript Regular Expression Visualizer.) Spiacente, non ho una reputazione abbastanza alta per includere immagini, quindi per ora è solo un collegamento.

Ecco una sintesi di una funzione di esempio che utilizza questo concetto che è un po 'più avanzato: https://gist.github.com/scagood/bd99371c072d49a4fee29d193252f5fc#file-matchquotes-js


0

Bisogna ricordare che le espressioni regolari non sono un proiettile d'argento per tutto ciò che è stringa-y. Alcune cose sono più semplici da fare con un cursore e una ricerca lineare, manuale. Una CFL farebbe il trucco abbastanza banalmente, ma non ci sono molte implementazioni CFL (afaik).


3
È vero, ma questo problema rientra nelle capacità delle espressioni regolari e ci sono molte implementazioni di queste.
Alan Moore

0

Una versione più estesa di https://stackoverflow.com/a/10786066/1794894

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

Questa versione contiene anche

  1. Lunghezza minima della citazione di 50
  2. Tipo extra di preventivi (apertura e chiusura )


0

Se viene cercato dall'inizio, forse può funzionare?

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

0

Ho riscontrato un problema simile cercando di rimuovere le stringhe tra virgolette che potrebbero interferire con l'analisi di alcuni file.

Ho finito con una soluzione in due passaggi che batte qualsiasi regex contorto che puoi trovare:

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

Più facile da leggere e probabilmente più efficiente.


0

Se il tuo IDE è IntelliJ Idea, puoi dimenticare tutti questi mal di testa e memorizzare la tua regex in una variabile String e mentre la copi e incolli all'interno delle virgolette, cambierà automaticamente in un formato accettabile di regex.

esempio in Java:

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

ora puoi usare questa variabile nella tua regexp o ovunque.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.