web-dev-qa-db-fra.com

RegEx: Saisir des valeurs entre guillemets

J'ai une valeur comme celle-ci:

"Foo Bar" "Another Value" something else

Quelle expression rationnelle renverra les valeurs entre guillemets (par exemple, Foo Bar et Another Value)?

169
deadbug

J'ai utilisé les éléments suivants avec grand succès:

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

Il prend également en charge les citations imbriquées.

Pour ceux qui veulent une explication plus profonde de la façon dont cela fonctionne, voici une explication de l'utilisateur ephemient :

([""']) correspond à un devis; ((?=(\\?))\2.) si la barre oblique inverse existe, avalez-la et, que cela se produise ou non, faites correspondre un caractère; *? correspond plusieurs fois (sans avidité, comme pour ne pas manger le dernier mot); \1 correspond à la même citation utilisée pour l'ouverture.

276
Adam

En général, le fragment d’expression rationnelle suivant est ce que vous recherchez:

"(.*?)"

Cela utilise le non-gourmand *? l’opérateur pour tout saisir jusqu’à la prochaine double citation, sans l’inclure. Ensuite, vous utilisez un mécanisme spécifique à la langue pour extraire le texte correspondant.

En Python, vous pourriez faire:

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

Je voudrais aller pour:

"([^"]*)"

Le [^ "] est regex pour n'importe quel caractère sauf '"'
La raison pour laquelle j’utilise ceci sur un grand nombre d’opérateurs non avides, c’est que je dois continuer à regarder cela juste pour être sûr de bien comprendre.

75
Martin York

Permet de voir deux manières efficaces de traiter les citations échappées. Ces modèles ne sont pas conçus pour être concis ni esthétiques, mais pour être efficaces.

Ces méthodes utilisent la discrimination de premier caractère pour trouver rapidement des guillemets dans la chaîne sans le coût d'une alternance. (L'idée est de supprimer rapidement les caractères qui ne sont pas des guillemets sans tester les deux branches de l'alternance.)

Le contenu entre guillemets est décrit avec une boucle déroulée (au lieu d'une alternance répétée) pour être plus efficace aussi: [^"\\]*(?:\\.[^"\\]*)*

Évidemment, pour traiter les chaînes qui n'ont pas de guillemets équilibrés, vous pouvez utiliser des quantificateurs possessifs: [^"\\]*+(?:\\.[^"\\]*)*+ ou une solution de contournement pour les imiter, afin d'éviter de trop revenir en arrière. Vous pouvez également choisir qu'une pièce citée puisse être un devis d'ouverture jusqu'au prochain devis (non échappé) ou à la fin de la chaîne. Dans ce cas, il n'est pas nécessaire d'utiliser des quantificateurs possessifs, il vous suffit de rendre la dernière citation facultative.

Remarque: parfois, les guillemets ne sont pas échappés avec une barre oblique inverse, mais en répétant la citation. Dans ce cas, le sous-motif de contenu ressemble à ceci: [^"]*(?:""[^"]*)*

Les modèles évitent l'utilisation d'un groupe de capture et d'un backreference (je veux dire quelque chose comme (["']).....\1) et utilisent une simple alternance mais avec ["'] au début, en facteur.

Perl comme:

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

(notez que (?s:...) est un sucre syntaxique pour activer le mode dotall/singleline au sein du groupe non capturant. Si cette syntaxe n'est pas prise en charge, vous pouvez facilement activer ce mode pour tout le motif ou remplacer le point par [\s\S])

(La façon dont ce modèle est écrit est totalement "pilotée à la main" et ne tient pas compte des optimisations internes éventuelles du moteur)

Script ECMA:

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

POSIX étendu:

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

ou simplement:

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

Curieusement, aucune de ces réponses ne produit une regex où la correspondance renvoyée est le texte à l'intérieur des guillemets, ce qui est demandé. MA-Madden essaie mais obtient seulement le match intérieur en tant que groupe capturé plutôt que le match entier. Une façon de le faire serait:

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

Des exemples de ceci peuvent être vus dans cette démo https://regex101.com/r/Hbj8aP/1

La clé ici est le lookbehind positif au début (le ?<=) et le lookahead positif à la fin (le ?=). Le lookbehind cherche derrière le personnage en cours pour rechercher une citation; s'il est trouvé, commencez à partir de là et ensuite, le lookahead vérifie si le personnage se trouve devant lui pour une citation et s'il est trouvé, arrêtez-le. Le groupe lookbehind (le ["']) est placé entre crochets pour créer un groupe dans la citation trouvée au début. Il est ensuite utilisé à la fin de lookahead (?=\1) pour s’assurer qu’il ne s’arrête que lorsque la citation correspondante est trouvée. 

La seule autre complication est que, comme le préfixe d'anticipation ne consomme pas le guillemet final, il sera retrouvé par le sélecteur de début, ce qui fera correspondre le texte entre les guillemets de fin et de début sur la même ligne. Il est utile d’ajouter une limite de Word au premier guillemet (["']\b), mais dans l’idéal, j’aimerais aller au-delà du regard mais je ne pense pas que ce soit possible. La partie permettant les caractères échappés au milieu, je l'ai directement tirée de la réponse d'Adam.

10
IrishDubGuy

Une réponse très tardive, mais j'aime bien répondre

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

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

Cette version

  • comptes pour les citations échappées
  • contrôle les retours en arrière

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

J'ai aimé la solution d'Eugen Mihailescu faire correspondre le contenu entre guillemets tout en permettant d'échapper aux guillemets. Cependant, j'ai découvert quelques problèmes avec échapper et ai proposé la regex suivante pour les résoudre:

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

Il fait le tour et reste assez simple et facile à maintenir.

Demo (avec quelques tests supplémentaires; n'hésitez pas à les utiliser et à les développer).


PS: Si vous voulez juste le contenu entre les guillemets dans la correspondance complète ($0) et que vous n'ayez pas peur de la pénalité de performance, utilisez:

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

PPS: Si votre objectif est uniquement l’efficacité, optez pour La solution de Casimir et Hippolyte ; c'est un bon.

4
wp78de

J'aimais la version plus expansive d'Axeman, mais j'éprouvais quelques difficultés

foo "string \\ string" bar

ou

foo "string1"   bar   "string2"

correctement, alors j'ai essayé de le réparer:

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

juste essayer ceci, fonctionne comme un charme !!!

\ indique le caractère de saut

2
mobman

Contrairement à la réponse d'Adam, j'en ai une simple mais travaillée:

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

Et ajoutez simplement des parenthèses si vous souhaitez obtenir du contenu entre guillemets comme celui-ci:

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

Alors $1 correspond au caractère de citation et $2 à la chaîne de contenu.

1
lon

De Greg H., j'ai pu créer cette regex pour répondre à mes besoins.

Je devais faire correspondre une valeur spécifique qualifiée par des guillemets intérieurs. Ce doit être une correspondance complète, aucune correspondance partielle ne devrait déclencher un hit 

par exemple. "test" n'a pas pu correspondre à "test2".

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

Chasseur

1
motoprog

Réponse complémentaire pour le sous-ensemble de Codeurs Microsoft VBA uniquement one utilise la bibliothèque Microsoft VBScript Regular Expressions 5.5 et cela donne le code suivant

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

Cela se traduira par:> Foo Bar <> <> mais ceci <

Ici, j’ai montré la chaîne de résultats entre> <pour plus de clarté, en utilisant également la version non-gloutonne avec cette commande sed, nous jetons d’abord les fichiers indésirables avant et après les "", puis nous les remplaçons par la partie entre "" et entourez ceci par> <. 

0
amo-ej1

Pour moi a travaillé celui-ci: 

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

J'ai utilisé une phrase comme celle-ci:

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

et cela a très bien fonctionné.

0