RegEx: Werte zwischen Anführungszeichen abrufen

239

Ich habe einen Wert wie diesen:

"Foo Bar" "Another Value" something else

Welche Regex gibt die in Anführungszeichen (z. B. Foo Barund Another Value) eingeschlossenen Werte zurück ?

Deadbug
quelle
Im Zusammenhang mit stackoverflow.com/questions/138552/…
Andrew Edgecombe

Antworten:

360

Ich habe Folgendes mit großem Erfolg verwendet:

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

Es unterstützt auch verschachtelte Anführungszeichen.

Für diejenigen, die eine ausführlichere Erklärung der Funktionsweise wünschen, finden Sie hier eine Erklärung von Benutzer Ephemient :

([""'])ein Zitat abgleichen; ((?=(\\?))\2.)Wenn ein Backslash vorhanden ist, verschlingen Sie ihn und stimmen Sie einem Charakter zu, ob dies passiert oder nicht. *?viele Male übereinstimmen (nicht gierig, um das Schlusszitat nicht zu essen); \1stimmen mit dem gleichen Zitat überein, das zum Öffnen verwendet wurde.

Adam
quelle
6
@steve: das würde auch falsch übereinstimmen , "foo\". Der Look-Ahead-Trick macht den ?Quantifizierer besitzergreifend (auch wenn der Regex-Geschmack die ?+Syntax oder die atomare Gruppierung nicht unterstützt )
Robin
1
Mit Python löst dies einen Fehler aus: sre_constants.error: kann nicht auf offene Gruppe verweisen
a1an
9
Dies gibt die Werte einschließlich der übereinstimmenden Anführungszeichen zurück. Gibt es keine Möglichkeit, nur den Inhalt zwischen den Anführungszeichen zurückzugeben, wie er angefordert wurde?
Martin Schneider
4
Der Missbrauch eines Lookaheads als besitzergreifender Quantifizierer ist völlig unnötig und verwirrend. Verwenden Sie einfach eine Abwechslung:(["'])(?:\\.|[^\\])*?\1
Aran-Fey
2
Wie vermeide ich leere Zeichenketten?
Vikas Bansal
332

Im Allgemeinen suchen Sie nach dem folgenden Fragment für reguläre Ausdrücke:

"(.*?)"

Dies nutzt die nicht gierigen *? Operator, um alles zu erfassen, bis auf das nächste doppelte Anführungszeichen. Anschließend verwenden Sie einen sprachspezifischen Mechanismus, um den übereinstimmenden Text zu extrahieren.

In Python können Sie Folgendes tun:

>>> import re
>>> string = '"Foo Bar" "Another Value"'
>>> print re.findall(r'"(.*?)"', string)
['Foo Bar', 'Another Value']
Greg Hewgill
quelle
11
Dies ist großartig, behandelt jedoch keine Zeichenfolgen mit maskierten Anführungszeichen. zB"hello \" world"
Robbyt
Wenn Sie die Übereinstimmung von JavaScript verwenden, stimmt dies auch mit den Anführungszeichen überein. Es wird mit Iteration über Exec wie hier beschrieben funktionieren
Kiechlus
4
@robbyt Ich weiß, es ist ein bisschen spät für eine Antwort, aber was ist mit einem negativen Lookbehind? "(.*?(?<!\\))"
Mateus
4
Vielen Dank - dies ist einfacher, wenn Sie sicher sind, dass es keine entkommenen Zitate gibt, mit denen Sie sich befassen müssen.
Squarecandy
Ein Wort. Genial !
Shiva Avula
89

Ich würde gehen für:

"([^"]*)"

Das [^ "] ist ein regulärer Ausdruck für jedes Zeichen außer ' " '.
Der Grund , warum ich dies gegenüber dem nicht gierigen Operator "Viele" verwende, ist, dass ich das ständig nachschlagen muss, um sicherzugehen, dass ich es richtig verstehe.

Martin York
quelle
1
Dies verhält sich auch bei verschiedenen Regex-Interpretationen gut.
Phil Bennett
5
Das hat meine geistige Gesundheit gerettet. In der RegEx-Implementierung von .NET hat "(. *?)" Nicht den gewünschten Effekt (es wirkt nicht gierig), aber "([^"] *) ".
Jens Neubauer
Dies ist die beste Antwort imo. Thanks
Lmao 123 vor
28

Sehen wir uns zwei effiziente Möglichkeiten an, mit Escape-Anführungszeichen umzugehen. Diese Muster sind weder prägnant noch ästhetisch, sondern effizient.

Diese Methoden verwenden die Unterscheidung der ersten Zeichen, um schnell Anführungszeichen in der Zeichenfolge zu finden, ohne die Kosten einer Abwechslung. (Die Idee ist, Zeichen, die keine Anführungszeichen sind, schnell zu verwerfen, ohne die beiden Zweige der Abwechslung zu testen.)

Der Inhalt zwischen Anführungszeichen wird mit einer abgewickelten Schleife (anstelle eines wiederholten Wechsels) beschrieben, um auch effizienter zu sein: [^"\\]*(?:\\.[^"\\]*)*

Um mit Zeichenfolgen umzugehen, die keine ausgewogenen Anführungszeichen haben, können Sie stattdessen Possessivquantifizierer verwenden: [^"\\]*+(?:\\.[^"\\]*)*+oder eine Problemumgehung, um sie zu emulieren und zu viel Backtracking zu verhindern. Sie können auch festlegen, dass ein Teil in Anführungszeichen ein Eröffnungszitat bis zum nächsten (nicht maskierten) Anführungszeichen oder dem Ende der Zeichenfolge sein kann. In diesem Fall müssen keine Possessivquantifizierer verwendet werden. Sie müssen nur das letzte Anführungszeichen optional machen.

Hinweis: Manchmal werden Anführungszeichen nicht mit einem Backslash maskiert, sondern durch Wiederholen des Anführungszeichens. In diesem Fall sieht das Inhaltsuntermuster folgendermaßen aus:[^"]*(?:""[^"]*)*

Die Muster vermeiden die Verwendung einer Erfassungsgruppe und einer Rückreferenz (ich meine so etwas wie (["']).....\1) und verwenden eine einfache Abwechslung, jedoch mit ["']dem Faktor am Anfang.

Perl wie:

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

(Beachten Sie, dass dies (?s:...)ein syntaktischer Zucker ist, um den Dotall / Singleline-Modus innerhalb der nicht erfassenden Gruppe einzuschalten. Wenn diese Syntax nicht unterstützt wird, können Sie diesen Modus für alle Muster einfach einschalten oder den Punkt durch ersetzen. [\s\S])

(Die Art und Weise, wie dieses Muster geschrieben wird, ist vollständig "handgesteuert" und berücksichtigt eventuelle motorinterne Optimierungen nicht.)

ECMA-Skript:

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

POSIX erweitert:

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

oder einfach:

"([^"\\]|\\.|\\\n)*"|'([^'\\]|\\.|\\\n)*'
Casimir et Hippolyte
quelle
1
Python akzeptiert das ECMA-Skript im Raw-String-Format, dh das ECMA-Skript
a1an
1
Dies ist brillant. Es war sehr einfach, Ihre ECMA-Version so anzupassen, dass neue Zeilen- und Wagenrückläufe in doppelten Anführungszeichen angezeigt werden.
Douglas Gaskell
@ Douglasg14b: Danke. Beachten Sie, dass Sie, wenn Sie es in Javascript verwenden möchten, nur die Literalnotation verwenden müssen, /pattern/ohne sich etwas zu entziehen (anstelle der Objektnotation new RegExp("(?=[\"'])(?:\"[^\"\\\\]*...");)
Casimir et Hippolyte
@ a1an: Ja, aber Sie können die Perl-Version verwenden, wenn Sie die shier entfernen : (?s:und wenn Sie (?s)irgendwo in das Muster setzen.
Casimir et Hippolyte
16

Der RegEx der akzeptierten Antwort gibt die Werte einschließlich der umgebenden Anführungszeichen zurück: "Foo Bar"und "Another Value"als Übereinstimmungen.

Hier sind RegEx, die nur die Werte zwischen Anführungszeichen zurückgeben (wie vom Fragesteller verlangt):

Nur doppelte Anführungszeichen (Wert der Erfassungsgruppe 1 verwenden):

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

Nur einfache Anführungszeichen (Wert der Erfassungsgruppe 1 verwenden):

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

Beides (Wert der Erfassungsgruppe 2 verwenden):

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

- -

Alle Unterstützung entkam und verschachtelte Anführungszeichen.

Martin Schneider
quelle
Bitte, warum funktioniert das? Ich habe verwendet, src="(.*)"aber offensichtlich hat es alles vor dem letzten ausgewählt ", Ihr REGEX hat jedoch nur den Inhalt von src =" "ausgewählt, aber ich habe nicht verstanden, wie?
Lucas Bustamante
Ich mag dieses sehr wegen seiner Einfachheit, aber es behandelt leere oder keine Werte zwischen Anführungszeichen nicht sehr gut, wie ich herausgefunden habe
RedactedProfile
16

Insbesondere erzeugt keine dieser Antworten einen regulären Ausdruck, bei dem die zurückgegebene Übereinstimmung der Text in den Anführungszeichen ist, nach dem gefragt wird. MA-Madden versucht es, bekommt aber nur das Insider-Match als gefangene Gruppe und nicht das ganze Match. Ein Weg, dies tatsächlich zu tun, wäre:

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

Beispiele hierfür finden Sie in dieser Demo unter https://regex101.com/r/Hbj8aP/1

Der Schlüssel hier ist das positive Aussehen am Anfang (das ?<=) und das positive Aussehen am Ende (das ?=). Das Lookbehind schaut hinter das aktuelle Zeichen, um nach einem Zitat zu suchen. Wenn es gefunden wird, beginnen Sie von dort aus, und der Lookahead überprüft das vorausschauende Zeichen auf ein Zitat. Wenn es gefunden wird, stoppen Sie dieses Zeichen. Die Lookbehind-Gruppe (the ["']) wird in Klammern gesetzt, um eine Gruppe für das am Anfang gefundene Zitat zu erstellen. Diese wird dann am Ende des Lookaheads verwendet (?=\1), um sicherzustellen, dass sie nur stoppt, wenn das entsprechende Zitat gefunden wird.

Die einzige andere Komplikation besteht darin, dass der Lookahead das Endzitat nicht tatsächlich verbraucht und vom Start-Lookbehind wiedergefunden wird, wodurch der Text zwischen End- und Startzitaten in derselben Zeile übereinstimmt. Das Einfügen einer Wortgrenze in das Eröffnungszitat ( ["']\b) hilft dabei, obwohl ich im Idealfall gerne am Lookahead vorbeikommen würde, aber ich denke nicht, dass dies möglich ist. Das Bit, das entkommene Zeichen in der Mitte zulässt, habe ich direkt aus Adams Antwort entnommen.

IrishDubGuy
quelle
11

Eine sehr späte Antwort, aber ich antworte gerne

(\"[\w\s]+\")

http://regex101.com/r/cB0kB8/1

Suganthan Madhavan Pillai
quelle
Funktioniert gut in PHP.
Parapluie
Die einzige Antwort für die Erfassung von "HomePage" in: localize ["Homepage"] localize ["Homepage"]
jBelanger
8

Das (["'])(?:(?=(\\?))\2.)*?\1obige Muster macht den Job, aber ich bin besorgt über seine Leistungen (es ist nicht schlecht, könnte aber besser sein). Meins darunter ist ~ 20% schneller.

Das Muster "(.*?)"ist nur unvollständig. Mein Rat für alle, die dies lesen, ist, ES NICHT ZU VERWENDEN !!!

Zum Beispiel kann es nicht viele Zeichenfolgen erfassen (bei Bedarf kann ich einen ausführlichen Testfall bereitstellen), wie den folgenden:

$ string = 'Wie geht es dir? Mir geht es \'gut, danke ';

Der Rest von ihnen ist genauso "gut" wie der oben.

Wenn Ihnen Leistung und Präzision wirklich am Herzen liegen, beginnen Sie mit dem folgenden:

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

In meinen Tests wurde jeder String abgedeckt, den ich getroffen habe. Wenn Sie jedoch etwas finden, das nicht funktioniert, würde ich es gerne für Sie aktualisieren.

Überprüfen Sie mein Muster in einem Online-Regex-Tester .

Eugen Mihailescu
quelle
1
Ich mag die Einfachheit Ihres Musters, aber in Bezug auf die Leistung bläst das Muster von Casimir et Hippolyte alle erweiterten Lösungen aus dem Wasser. Außerdem sieht es so aus, als hätte Ihr Muster Probleme mit erweiterten Randfällen wie einem maskierten Zitat am Ende des Satzes.
wp78de
7

Ich mochte Eugen Mihailescus Lösung , um den Inhalt zwischen Anführungszeichen abzugleichen und gleichzeitig Anführungszeichen zu vermeiden. Ich habe jedoch einige Probleme beim Entkommen entdeckt und mir den folgenden regulären Ausdruck ausgedacht, um sie zu beheben:

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

Es macht den Trick und ist immer noch ziemlich einfach und leicht zu warten.

Demo (mit einigen weiteren Testfällen; Sie können sie gerne verwenden und erweitern).


PS: Wenn Sie nur den Inhalt zwischen Anführungszeichen in der vollständigen Übereinstimmung ( $0) möchten und keine Angst vor der Leistungsstrafe haben, verwenden Sie:

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

Leider musste ich ohne die Anführungszeichen als Anker eine Begrenzung hinzufügen, \bdie mit Leerzeichen und Nicht-Wort-Begrenzungszeichen nach dem Startzitat nicht gut funktioniert.

Alternativ können Sie die ursprüngliche Version ändern, indem Sie einfach eine Gruppe$2 hinzufügen und das Zeichenfolgenformular extrahieren :

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

PPS: Wenn Sie sich ausschließlich auf Effizienz konzentrieren, entscheiden Sie sich für die Lösung von Casimir et Hippolyte . Das ist ein guter.

wp78de
quelle
Beobachtung: Der zweite reguläre Ausdruck verfehlt einen Wert mit einem Minuszeichen -, wie in Längengradkoordinaten.
Crowcoder
Ich habe nichts geändert. Wenn Sie das Problem nicht beobachten, ist es vielleicht der Geschmack von Regex, den ich verwende. Ich habe die Regex101-Site verwendet, ich denke, Regex im PHP-Stil.
Crowcoder
Hier ist die Demo von dem, worüber ich spreche. Ich hatte erwartet, dass es dem Längengrad (-96.74025) entspricht, aber das tut es nicht.
Crowcoder
@ Crowcoder Danke. Ja, dies wird durch die Wortgrenze verursacht, die als Anker fungiert und dabei hilft, überlappende Übereinstimmungen zu vermeiden, aber nicht gut mit Ihrer Eingabe spielt. Eine zusätzliche Gruppe ist tatsächlich die bessere Option, wie in der aktualisierten Antwort angegeben.
wp78de
6

Diese Version

  • Konten für Escape-Anführungszeichen
  • steuert das Backtracking

    /(["'])((?:(?!\1)[^\\]|(?:\\\\)*\\[^\\])*)\1/
Axeman
quelle
Dies umfasst mehrere Zeichenfolgen und scheint einen doppelten Backslash nicht korrekt zu handhaben, z. B. die Zeichenfolge: foo 'stri \\ ng 1' bar 'string 2' und 'string 3' Debuggex Demo
miracle2k
Sie können keine Rückreferenz in einer Zeichenklasse verwenden.
HamZa
5

MEHR ANTWORTEN! Hier ist die Lösung, die ich verwendet habe

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

TLDR;
ersetzen Sie das Wort Symbol mit , was Sie suchen in den Zitaten und voila!


Dies funktioniert so, dass nach dem Schlüsselwort gesucht wird und es egal ist, was sich sonst noch zwischen den Anführungszeichen befindet. EG:
id="fb-icon"
id="icon-close"
id="large-icon-close"
Der Regex sucht nach einem Anführungszeichen, "
dann nach einer möglichen Buchstabengruppe, die "
erst gefunden wird, icon
und nach einer möglichen Buchstabengruppe, die es nicht ist "
, sucht er nach einem Abschluss"

James Harrington
quelle
1
Vielen Dank. konnte jedes Vorkommen von name="value"durch ersetzen, name={"value"}da der reguläre Ausdruck dieser Antwort icon/ valueals zweite Gruppe zurückgibt (im Gegensatz zur akzeptierten Antwort). Finden : =\"([^\"]*?[^\"]*?)\" Ersetzen :={"$1"}
Palisand
Hast du etwas dagegen, die Ablehnung zu erklären? es funktioniert gut aus einigen Situationen.
James Harrington
Antwortest du mir
Palisand
@Palisand niemand hat diesen Beitrag neulich ohne Erklärung abgelehnt.
James Harrington
Dies scheint die einzige Antwort zu sein, die einen bestimmten Text in Anführungszeichen findet
Top-Master
4

Ich mochte Axemans expansivere Version, hatte aber einige Probleme damit (sie passte zum Beispiel nicht zusammen

foo "string \\ string" bar

oder

foo "string1"   bar   "string2"

richtig, also habe ich versucht, es zu beheben:

# 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
miracle2k
quelle
3
string = "\" foo bar\" \"loloo\""
print re.findall(r'"(.*?)"',string)

Probieren Sie es einfach aus, funktioniert wie ein Zauber !!!

\ zeigt das Sprungzeichen an

Mobman
quelle
Wenn diese erste Zeile der eigentliche Python-Code ist, wird die Zeichenfolge erstellt " foo bar" "loloo". Ich vermute, Sie wollten das in eine rohe Zeichenfolge einwickeln, wie Sie es mit dem regulären Ausdruck getan haben : r'"\" foo bar\" \"loloo\""'. Bitte nutzen Sie die hervorragenden Formatierungsfunktionen von SO, wann immer dies angemessen ist. Es ist nicht nur Kosmetik; Wir können buchstäblich nicht sagen, was Sie sagen wollen, wenn Sie sie nicht verwenden. Und willkommen bei Stack Overflow !
Alan Moore
danke für den rat alan, ich bin eigentlich neu in dieser community, das nächste mal werde ich mir das sicher merken ... aufrichtige entschuldigung.
Mobman
2

Im Gegensatz zu Adams Antwort habe ich eine einfache, aber funktionierende:

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

Fügen Sie einfach Klammern hinzu, wenn Sie Inhalte in Anführungszeichen wie diesen erhalten möchten:

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

Dann $1passt Gänsefüsschen und $2passt Inhalt String.

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

Dies führt zu:> Foo Bar <> <> aber dies <

Hier habe ich der Übersichtlichkeit halber die Ergebniszeichenfolge zwischen> <gezeigt, wobei wir auch die nicht gierige Version mit diesem sed-Befehl verwenden. Wir werfen zuerst den Müll davor und danach weg und ersetzen ihn dann durch den Teil zwischen den "". 's und umgeben dies mit> <' s.

amo-ej1
quelle
1

Von Greg H. konnte ich diesen regulären Ausdruck erstellen, der meinen Bedürfnissen entspricht.

Ich musste einem bestimmten Wert entsprechen, der durch Anführungszeichen qualifiziert wurde. Es muss eine vollständige Übereinstimmung sein, keine teilweise Übereinstimmung sollte einen Treffer auslösen

zB "test" konnte nicht mit "test2" übereinstimmen.

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

Jäger

Motoprog
quelle
1

Wenn Sie versuchen, Zeichenfolgen zu finden, die nur ein bestimmtes Suffix haben, z. B. die Punktsyntax, können Sie Folgendes versuchen:

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

Wo .localizedist das Suffix?

Beispiel:

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

Es wird erfassen "this is something I need to return".localizedund "so is this".localizedaber nicht "but this is not".

OffensivBad
quelle
1

Eine ergänzende Antwort für die Teilmenge der Microsoft VBA-Codierer verwendet nur einer die Bibliothek, Microsoft VBScript Regular Expressions 5.5und dies ergibt den folgenden Code

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
S Meaden
quelle
0

Für mich hat das funktioniert:

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

Ich habe in einem Satz wie diesem verwendet:

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

und es hat super funktioniert.

Alexandru Furculita
quelle
Eine Schwäche dieses Ansatzes besteht darin, dass er übereinstimmt, wenn eine Zeichenfolge mit einem einfachen Anführungszeichen beginnt und mit einem doppelten Anführungszeichen endet oder umgekehrt.
Ghopper21
Es hat auch Probleme, "Vergiss das @ nicht" zu fangen - es stoppt nach "Don".
Benny Neugebauer
0

Alle obigen Antworten sind gut ... außer dass sie NICHT alle Unicode-Zeichen unterstützen! bei ECMA Script (Javascript)

Wenn Sie ein Knotenbenutzer sind, möchten Sie möglicherweise die geänderte Version der akzeptierten Antwort, die alle Unicode-Zeichen unterstützt:

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

Versuchen Sie es hier .

Donovan P.
quelle
1
Was ist ein Nicht-Unicode-Zeichen? AFAIK Unicode deckt alle Zeichen ab.
Toto
1
Warum ist es wohl eine Javascript-Frage? Darüber hinaus wird Lookbehind nicht in allen Browsern unterstützt, regex101 wirft? The preceding token is not quantifiable
Toto
@Toto, ich meine "unterstützt nicht alle Unicode-Zeichen". Danke dir. Während es sich bei der Frage um Regex im Allgemeinen handelt, möchte ich nicht betonen, dass die Verwendung von Aussagen zu Wortgrenzen zu unerwünschtem Verhalten im Javascript führen würde. Und während Javascripts im Allgemeinen für Browser sind, gibt es natürlich auch Node.
Donovan P