Regex für Zeichenfolge in Anführungszeichen mit Anführungszeichen

120

Wie erhalte ich den Teilstring " It's big \"problem "mit einem regulären Ausdruck?

s = ' function(){  return " It\'s big \"problem  ";  }';     
David
quelle
1
Wie finden Sie "It's" in einer Zeichenfolge, die nur "Is" enthält? Ich würde es für Sie beheben, aber ich weiß nicht, welche Konventionen für einfache Anführungszeichen / Escapezeichen in der von Ihnen verwendeten Sprache gelten.
Jonathan Leffler
1
Duplikat von: PHP: Regex, um
maskierte
2
Wenn ich mir die Daten ansehe, sehe ich, dass die andere Frage ein Duplikat dieser Frage ist. Schauen Sie sich auf jeden Fall meine Antwort an .
Ridgerunner
@ridgerunner: Ich stimme dafür, dies zu schließen, wie Sie vorgeschlagen haben. Es ist wahr, andere Frage ist neuer, aber es ist auch viel besser (hauptsächlich dank Ihrer Antwort).
Alan Moore

Antworten:

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

Arbeitet in The Regex Coach und PCRE Workbench.

Testbeispiel in JavaScript:

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

PhiLho
quelle
23
Macht Sinn. Einfaches Englisch: Zwei Anführungszeichen, die null oder mehr von "einem Zeichen, das kein Anführungszeichen oder ein Backslash ist" oder "einem Backslash, gefolgt von einem Zeichen" umgeben. Ich kann nicht glauben, dass ich nicht daran gedacht habe ...
Ajedi32
7
Ich werde mir antworten. =) (?:...)ist eine passive oder nicht erfassende Gruppe. Dies bedeutet, dass es später nicht mehr referenziert werden kann.
Magras
Nachdem ich viel gesucht und viel getestet habe, ist dies die wirkliche und einzige Lösung, die ich für dieses häufige Problem gefunden habe. Vielen Dank!
Cancerbero
9
Danke dafür. Ich wollte auch einfache Anführungszeichen finden, also passte ich es an /(["'])(?:[^\1\\]|\\.)*?\1/
Leo
Mit var s = ' my \\"new\\" string and \"this should be matched\"';führt dieser Ansatz zu unerwarteten Ergebnissen.
Wiktor Stribiżew
32

Dieser stammt von nanorc.sample, das in vielen Linux-Distributionen erhältlich ist. Es wird zur Syntaxhervorhebung von Zeichenfolgen im C-Stil verwendet

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

quelle
Mit var s = ' my \\"new\\" string and \"this should be matched\"';führt dieser Ansatz zu unerwarteten Ergebnissen.
Wiktor Stribiżew
1
c.nanorc war der erste Ort, an den ich ging. Konnte es nicht als Teil eines C-String-Literals zum " \"(\\\\.|[^\\\"])*\" "
Laufen bringen,
Dies funktioniert mit den Funktionen egrep und re_comp / re_exec von libc.
fk0
19

Wie von ePharaoh bereitgestellt, lautet die Antwort

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

Verwenden Sie diese Option, damit die oben genannten Zeichenfolgen entweder auf einfache oder doppelte Anführungszeichen angewendet werden

/"([^"\\]*(\\.[^"\\]*)*)"|\'([^\'\\]*(\\.[^\'\\]*)*)\'/
Guy Bedford
quelle
2
Dies ist der einzige Satz, der für mich mit einer einzelnen, großen Zeichenfolge in Anführungszeichen von 1,5 KB mit 99 Escapezeichen funktioniert hat. Jeder andere Ausdruck auf dieser Seite ist in meinem Texteditor mit einem Überlauffehler fehlerhaft. Obwohl die meisten hier im Browser arbeiten, sollten Sie nur etwas beachten. Geige: jsfiddle.net/aow20y0L
Beejor
3
Weitere Informationen finden Sie in der Antwort von @ MarcAndrePoulin.
Shaunc
10

Die meisten der hier bereitgestellten Lösungen verwenden alternative Wiederholungspfade, dh (A | B) *.

Bei großen Eingaben können Stapelüberläufe auftreten, da einige Pattern-Compiler dies mithilfe der Rekursion implementieren.

Java zum Beispiel: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6337993

So etwas wie das: "(?:[^"\\]*(?:\\.)?)*"oder das von Guy Bedford bereitgestellte reduziert die Anzahl der Analyseschritte, wodurch die meisten Stapelüberläufe vermieden werden.

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

Durch das Abwechseln von \"und die .Übergänge über maskierte Anführungszeichen wird *?sichergestellt, dass Sie nicht über das Ende der Zeichenfolge in Anführungszeichen hinausgehen. Funktioniert mit .NET Framework RE-Klassen

Tosh Afanasiev
quelle
Aber scheitert mit"\\"
Ian
/"(?:(?:\\"|[^"])*)"/gDies sollte
behoben werden
7
/"(?:[^"\\]++|\\.)*+"/

Entnommen direkt von man perlreeinem Linux-System mit installiertem Perl 5.22.0. Als Optimierung verwendet dieser reguläre Ausdruck die "positive" Form von beiden +und *um ein Zurückverfolgen zu verhindern, da vorher bekannt ist, dass eine Zeichenfolge ohne abschließendes Anführungszeichen auf keinen Fall übereinstimmen würde.

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

sollte mit jeder Zeichenfolge in Anführungszeichen funktionieren


quelle
1
Schön, aber zu flexibel für die Anfrage (passt zu einfachen Anführungszeichen ...). Und kann zu /".*?(?<!\)"/ vereinfacht werden, es sei denn, ich vermisse etwas. Oh, und einige Sprachen (z. B. JavaScript) verstehen leider keine negativen Lookbehind-Ausdrücke.
PhiLho
1
@PhiLho, nur die Verwendung eines einzelnen (? <! \\) würde bei maskierten Backslashes am Ende der Zeichenfolge fehlschlagen. Dies gilt jedoch für Look-Behinds in JavaScript.
Markus Jarderot
4

Dieser funktioniert perfekt auf PCRE und fällt nicht mit StackOverflow.

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

Erläuterung:

  1. Jede Zeichenfolge in Anführungszeichen beginnt mit Char : ";
  2. Es kann eine beliebige Anzahl beliebiger Zeichen enthalten: .*?{Lazy match}; endet mit einem Nicht-Flucht-Charakter [^\\];
  3. Anweisung (2) ist Lazy (!) Optional, da die Zeichenfolge leer sein kann (""). So:(.*?[^\\])??
  4. Schließlich endet jede Zeichenfolge in Anführungszeichen mit Char ( "), es kann jedoch eine gerade Anzahl von Escape-Zeichenpaaren vorangestellt werden (\\\\)+. und es ist Greedy (!) optional: ((\\\\)+)?+{Greedy Matching}, weil die Zeichenfolge leer sein kann oder ohne Endpaare!
Vadim Sayfi
quelle
Es ist nicht das effizienteste Muster der Welt, aber die Idee ist interessant. Beachten Sie, dass Sie es wie "(.*?[^\\])?(\\\\)*"
Casimir et Hippolyte
2

Hier ist eine, die sowohl mit "als auch mit" funktioniert, und Sie können am Anfang ganz einfach andere hinzufügen.

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

Es verwendet die Rückreferenz (\ 1), die genau mit der ersten Gruppe ("oder ') übereinstimmt.

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

Mathias Hansen
quelle
Dies ist eine sehr gute Lösung, [^\1]sollte aber durch eine ersetzt werden, .da es keine Anti-Back-Referenz gibt und es sowieso keine Rolle spielt. Die erste Bedingung wird immer übereinstimmen, bevor etwas Schlimmes passieren kann.
Seph Reed
@SephReed - Ersetzen [^\1]mit .würde effektiv diese Regex ändern ("|').*?\1und dann würde es passen "foo\"in "foo \" bar". Das heißt, es [^\1]ist schwer, tatsächlich zur Arbeit zu kommen. @ Mathiashansen - Sie sind besser dran mit dem unhandlichen und teuren (?!\1).(so wäre der gesamte Regex mit einer gewissen Effizienzbereinigung (["'])(?:\\.|(?!\1).)*+\1. Das +ist optional, wenn Ihr Motor es nicht unterstützt.
Adam Katz
2

Eine Option, die zuvor noch nicht angesprochen wurde, ist:

  1. Kehren Sie die Zeichenfolge um.
  2. Führen Sie den Abgleich für die umgekehrte Zeichenfolge durch.
  3. Kehren Sie die übereinstimmenden Zeichenfolgen um.

Dies hat den zusätzlichen Vorteil, dass es möglich ist, entkommene offene Tags korrekt abzugleichen.

Nehmen wir an, Sie hatten die folgende Zeichenfolge. String \"this "should" NOT match\" and "this \"should\" match" Hier \"this "should" NOT match\"sollte nicht abgestimmt werden und "should"sollte sein. Darüber hinaus this \"should\" matchsollte abgestimmt werden und \"should\"sollte nicht.

Zuerst ein Beispiel.

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

Okay, jetzt um die RegExp zu erklären. Dies ist der reguläre Ausdruck, der leicht in drei Teile zerlegt werden kann. Wie folgt:

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

Dies ist in Bildform wahrscheinlich viel klarer: Erstellt mit Jex Regulex

Bild auf Github (JavaScript Regular Expression Visualizer). Entschuldigung, ich habe nicht den Ruf, Bilder aufzunehmen, daher ist es vorerst nur ein Link.

Hier ist eine Zusammenfassung einer Beispielfunktion, die dieses etwas fortgeschrittenere Konzept verwendet: https://gist.github.com/scagood/bd99371c072d49a4fee29d193252f5fc#file-matchquotes-js

scagood
quelle
0

Man muss bedenken, dass Regexps keine Silberkugel für alles sind, was man braucht. Einige Dinge sind einfacher mit einem Cursor und linearen, manuellen Suchen zu tun. Eine CFL würde den Trick ziemlich trivial machen, aber es gibt nicht viele CFL-Implementierungen (afaik).

Henrik Paul
quelle
3
Es stimmt, aber dieses Problem liegt innerhalb der Möglichkeiten von Regexes, und es gibt sehr viele Implementierungen davon.
Alan Moore
0

Eine umfangreichere Version von https://stackoverflow.com/a/10786066/1794894

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

Diese Version enthält auch

  1. Mindestangebotslänge von 50
  2. Zusätzliche Art von Anführungszeichen (Öffnen und Schließen )
Rvanlaak
quelle
0

Bei Regexpal herumgespielt und am Ende diesen Regex erhalten: (Frag mich nicht, wie es funktioniert, ich verstehe kaum, obwohl ich es geschrieben habe lol)

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

Wenn es von Anfang an gesucht wird, kann das vielleicht funktionieren?

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

Ich hatte ein ähnliches Problem beim Versuch, Zeichenfolgen in Anführungszeichen zu entfernen, die das Parsen einiger Dateien beeinträchtigen könnten.

Am Ende hatte ich eine zweistufige Lösung, die jeden verschlungenen regulären Ausdruck übertrifft, den Sie sich vorstellen können:

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

Einfacher zu lesen und wahrscheinlich effizienter.

マ ル ち ゃ ん ん だ
quelle
0

Wenn Ihre IDE IntelliJ Idea ist, können Sie all diese Kopfschmerzen vergessen und Ihren regulären Ausdruck in einer String-Variablen speichern. Wenn Sie ihn kopieren und in das doppelte Anführungszeichen einfügen, ändert er sich automatisch in ein für den regulären Ausdruck akzeptables Format.

Beispiel in Java:

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

Jetzt können Sie diese Variable in Ihrem regulären Ausdruck oder überall verwenden.

Aramis NSR
quelle