RegEx: saisie de valeurs entre guillemets


Réponses:


361

J'utilise les éléments suivants avec beaucoup de succès:

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

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

Pour ceux qui veulent une explication plus approfondie de la façon dont cela fonctionne, voici une explication de l' éphémient utilisateur :

([""'])correspondre à un devis; ((?=(\\?))\2.)si une barre oblique inverse existe, engloutissez-la et, que cela se produise ou non, faites correspondre un caractère; *?correspondre plusieurs fois (sans avidité, pour ne pas manger la citation de clôture); \1correspondre à la même citation qui a été utilisée pour l'ouverture.


6
@ Steve: ce serait également correspondre, à tort, "foo\". L'astuce d'anticipation rend le ?quantificateur possessif (même si la saveur regex ne prend pas en charge la ?+syntaxe ou le groupement atomique)
Robin

1
Avec python, cela génère une erreur: sre_constants.error: ne peut pas faire référence au groupe ouvert
a1an

9
Cela renvoie les valeurs, y compris les guillemets correspondants. N'y a-t-il aucune chance de renvoyer uniquement le contenu entre les devis, comme cela a été demandé?
Martin Schneider

4
Abuser d'une tête de lecture en tant que quantificateur possessif est complètement inutile et déroutant. Il suffit d'utiliser une alternance:(["'])(?:\\.|[^\\])*?\1
Aran-Fey

2
comment éviter les chaînes vides?
Vikas Bansal du

333

En général, le fragment d'expression régulière suivant correspond à ce que vous recherchez:

"(.*?)"

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

En Python, vous pouvez faire:

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

11
C'est bien, mais il ne gère pas les chaînes avec des guillemets échappés. par exemple,"hello \" world"
robbyt

En utilisant la correspondance de JavaScript, cela correspondra également aux guillemets. Cela fonctionnera avec une itération sur exec comme décrit ici: stackoverflow.com/questions/7998180/…
Kiechlus

4
@robbyt Je sais qu'il est un peu tard pour une réponse mais qu'en est-il d'un lookbehind négatif? "(.*?(?<!\\))"
Mateus

4
Merci - c'est plus simple si vous êtes sûr qu'il n'y a pas de citations échappées à traiter.
squarecandy

Un mot. Impressionnant !
Shiva Avula

89

J'irais pour:

"([^"]*)"

Le [^ "] est regex pour tout caractère sauf ' " '.
La raison pour laquelle j'utilise ceci sur l'opérateur many non gourmand, c'est que je dois continuer à chercher cela juste pour m'assurer que je le reçois correctement.


1
Cela se comporte également bien entre les différentes interprétations des expressions rationnelles.
Phil Bennett

5
Cela a sauvé ma raison. Dans l'implémentation RegEx de .NET, "(. *?)" N'a pas l'effet souhaité (il n'agit pas de manière non gourmande), mais "([^"] *) ".
Jens Neubauer

C'est la meilleure réponse imo. Merci
Lmao 123

28

Voyons deux façons efficaces de gérer les guillemets échappés. Ces motifs ne sont pas conçus pour être concis ni esthétiques, mais pour être efficaces.

Ces méthodes utilisent la première discrimination de 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 des guillemets équilibrés, vous pouvez utiliser des quantificateurs possessifs à la place: [^"\\]*+(?:\\.[^"\\]*)*+ou une solution de contournement pour les émuler, pour éviter trop de retour en arrière. Vous pouvez également choisir qu'une partie entre guillemets puisse être une citation d'ouverture jusqu'à la citation suivante (non échappée) 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 par une barre oblique inverse, mais en répétant la citation. Dans ce cas, le sous-modèle de contenu ressemble à ceci:[^"]*(?:""[^"]*)*

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

Perl comme:

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

(notez qu'il (?s:...)s'agit d'un sucre syntaxique pour activer le mode dotall / singleline dans le groupe non capturant. Si cette syntaxe n'est pas prise en charge, vous pouvez facilement activer ce mode pour tout le modèle ou remplacer le point par [\s\S])

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

Script ECMA:

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

POSIX étendu:

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

ou simplement:

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

1
Python accepte le script ECMA avec un format de chaîne brut, c'est-à-dire r "" "Script ECMA" ""
a1an

1
C'est génial, il a été très facile d'adapter votre ECMA pour fonctionner avec les nouveaux retours de ligne et de chariot entre guillemets doubles.
Douglas Gaskell

@ douglasg14b: Merci. Notez que si vous souhaitez l'utiliser en Javascript, il vous suffit d'utiliser la notation littérale /pattern/sans rien échapper (au lieu de la notation objet new RegExp("(?=[\"'])(?:\"[^\"\\\\]*...");)
Casimir et Hippolyte

@ a1an: oui, mais vous pouvez utiliser la version Perl si vous supprimez le sici: (?s:et si vous mettez (?s)quelque part dans le motif.
Casimir et Hippolyte

16

Le RegEx de la réponse acceptée renvoie les valeurs, y compris leurs guillemets environnants: "Foo Bar"et "Another Value"sous forme de correspondances.

Voici RegEx qui ne renvoie que les valeurs entre guillemets (comme l'interrogateur le demandait):

Citations doubles uniquement (utilisez la valeur du groupe de capture n ° 1):

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

Citations simples uniquement (utilisez la valeur du groupe de capture n ° 1):

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

Les deux (utilisez la valeur du groupe de capture n ° 2):

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

-

Toutes les citations échappées et imbriquées de support.


S'il vous plaît, pourquoi cela fonctionne-t-il? J'utilisais src="(.*)"mais évidemment il sélectionnait tout avant le dernier ", votre REGEX, cependant, ne sélectionnait que le contenu src =" ", mais je ne comprenais pas comment?
Lucas Bustamante

J'aime beaucoup celui-ci pour sa simplicité mais il ne gère pas très bien les valeurs vides ou sans valeur entre guillemets comme je l'ai découvert
RedactedProfile

16

Curieusement, aucune de ces réponses ne produit une expression régulière où la correspondance renvoyée est le texte à l'intérieur des guillemets, ce qui est demandé. MA-Madden essaie mais n'obtient que 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 cela 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 regarde derrière le caractère actuel pour vérifier une citation, s'il est trouvé, commencez à partir de là, puis le lookahead vérifie le caractère à venir pour une citation et s'il est trouvé, arrêtez-vous sur ce caractère. Le groupe de recherche (le ["']) est placé entre crochets pour créer un groupe pour la citation trouvée au début, il est ensuite utilisé à la fin de l'anticipation (?=\1)pour s'assurer qu'il ne s'arrête que lorsqu'il trouve la citation correspondante.

La seule autre complication est que, parce que l'antichambre ne consomme pas réellement le guillemet final, il sera retrouvé par le regard de départ qui entraîne la correspondance du texte entre les guillemets de fin et de début sur la même ligne. Mettre une limite de mot sur la citation d'ouverture ( ["']\b) aide à cela, bien que j'aimerais idéalement passer devant l'antichambre, mais je ne pense pas que ce soit possible. Le bit permettant aux personnages échappés au milieu que j'ai pris directement de la réponse d'Adam.



8

Le motif (["'])(?:(?=(\\?))\2.)*?\1ci-dessus fait l'affaire mais je suis préoccupé par ses performances (c'est pas mal mais ça pourrait être mieux). Le mien en dessous est ~ 20% plus rapide.

Le modèle "(.*?)"est juste incomplet. Mon conseil pour tous ceux qui lisent ceci est juste de ne pas l'utiliser !!!

Par exemple, il ne peut pas capturer de nombreuses chaînes (si nécessaire, je peux fournir un cas de test exhaustif) comme celui ci-dessous:

$ string = 'Comment ça va? Je vais \'bien, merci ';

Les autres sont tout aussi "bons" que celui ci-dessus.

Si vous vous souciez vraiment à la fois des performances et de la précision, commencez par celui ci-dessous:

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

Dans mes tests, il a couvert toutes les chaînes que j'ai rencontrées, mais si vous trouvez quelque chose qui ne fonctionne pas, je le mettrais à jour avec plaisir.

Vérifiez mon modèle dans un testeur de regex en ligne .


1
J'aime la simplicité de votre modèle, mais le modèle de Casimir et Hippolyte en termes de performances souffle toutes les solutions étendues hors de l'eau. En outre, il semble que votre modèle ait des problèmes avec les cas de bord étendus comme une citation échappée à la fin de la phrase.
wp78de

7

J'ai aimé la solution d'Eugen Mihailescu pour faire correspondre le contenu entre les citations tout en permettant d'échapper aux citations. Cependant, j'ai découvert quelques problèmes avec l'échappement et j'ai trouvé l'expression régulière suivante pour les résoudre:

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

Il fait l'affaire et est toujours assez simple et facile à entretenir.

Démo (avec quelques cas de test supplémentaires; n'hésitez pas à l'utiliser et à l'étendre).


PS: Si vous voulez simplement le contenu entre guillemets dans le match complet ( $0), et que vous n'avez pas peur de la pénalité de performance, utilisez:

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

Malheureusement, sans les guillemets comme ancres, j'ai dû ajouter une frontière \bqui ne fonctionne pas bien avec des espaces et des caractères de frontière non-mot après la citation de départ.

Vous pouvez également modifier la version initiale en ajoutant simplement un groupe et en extraire la forme de chaîne$2 :

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

PPS: Si vous vous concentrez uniquement sur l'efficacité, optez pour la solution de Casimir et Hippolyte ; c'est un bon.


observation: la deuxième expression régulière manque une valeur avec un signe moins -, comme dans les coordonnées de longitude.
Crowcoder

Je n'ai rien changé. Si vous n'observez pas le problème, c'est peut-être la saveur de l'expression régulière que j'utilise. J'utilisais le regex101site, je pense que le regex de style php.
Crowcoder

Voici la démo de ce dont je parle. Je m'attendais à ce qu'il corresponde à la longitude (-96.74025) mais ce n'est pas le cas.
Crowcoder

@Crowcoder Merci. Oui, cela est causé par la limite de mot qui agit comme une ancre et aide à éviter les correspondances qui se chevauchent, mais ne joue pas bien avec votre entrée. Un groupe supplémentaire est en fait la meilleure option, comme indiqué dans la réponse mise à jour.
wp78de

6

Cette version

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

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

Cela s'étend sur plusieurs chaînes et ne semble pas gérer correctement une double barre oblique inverse, par exemple la chaîne: foo 'stri \\ ng 1' bar 'chaîne 2' et 'chaîne 3' Debuggex Demo
miracle2k

Vous ne pouvez pas utiliser de référence arrière dans une classe de caractères.
HamZa

5

PLUS DE RÉPONSES! Voici la solution que j'ai utilisée

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

TLDR;
remplacez l' icône du mot par ce que vous recherchez dans lesdites citations et le tour est joué!


La façon dont cela fonctionne est qu'il recherche le mot-clé et ne se soucie pas de quoi d'autre entre les guillemets. EG:
id="fb-icon"
id="icon-close"
id="large-icon-close"
le regex cherche un guillemet "
puis il cherche tout groupe de lettres possible qui ne l'est pas "
jusqu'à ce qu'il trouve icon
et tout groupe de lettres possible qui ne l'est pas "
alors il cherche une fermeture"


1
Merci beaucoup. a été en mesure de remplacer chaque occurrence de name="value"par name={"value"}puisque l'expression régulière de cette réponse renvoie icon/ valuecomme deuxième groupe (contrairement à la réponse acceptée). Trouver : =\"([^\"]*?[^\"]*?)\" Remplacer :={"$1"}
Palisand

Voulez-vous expliquer le downvote? cela fonctionne bien dans certaines situations.
James Harrington

Tu me réponds?
Palisand

@Palisand personne n'a voté contre ce post l'autre jour sans explication.
James Harrington

cela semble être la seule réponse qui trouve un texte spécifique à l'intérieur des guillemets
Top-Master

4

J'ai aimé la version plus expansive d'Axeman, mais j'ai eu quelques problèmes avec elle (elle ne correspondait pas par exemple

foo "string \\ string" bar

ou

foo "string1"   bar   "string2"

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

essayez-le, fonctionne comme un charme !!!

\ indique un caractère de saut


Si cette première ligne est le code Python réel, cela va créer la chaîne " foo bar" "loloo". Je pense que vous vouliez dire pour envelopper que dans une chaîne brute comme vous avez fait avec la regex: r'"\" foo bar\" \"loloo\""'. Veuillez utiliser les excellentes capacités de formatage de SO chaque fois que cela est approprié. Ce n'est pas seulement des cosmétiques; nous ne pouvons littéralement pas dire ce que vous essayez de dire si vous ne les utilisez pas. Et bienvenue dans Stack Overflow !
Alan Moore

merci pour les conseils alan, je suis en fait nouveau dans cette communauté, la prochaine fois je garderai sûrement tout cela à l'esprit ... sincères excuses.
mobman

2

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

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

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

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

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


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

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

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


1

De Greg H., j'ai pu créer cette expression régulière pour répondre à mes besoins.

J'avais besoin de faire correspondre une valeur spécifique qualifiée en étant entre guillemets. Il doit s'agir d'une correspondance complète, aucune correspondance partielle ne devrait déclencher un hit

Par exemple, "test" ne peut pas correspondre à "test2".

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

chasseur


1

Si vous essayez de trouver des chaînes qui n'ont qu'un certain suffixe, comme la syntaxe à points, vous pouvez essayer ceci:

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

.localizedest le suffixe.

Exemple:

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

Il capturera "this is something I need to return".localizedet "so is this".localizednon "but this is not".


1

Une réponse supplémentaire pour le sous-ensemble de codeurs Microsoft VBA, un seul utilise la bibliothèque Microsoft VBScript Regular Expressions 5.5et 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

0

Pour moi a travaillé celui-ci:

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

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

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

et cela a très bien fonctionné.


Une faiblesse de cette approche est qu'elle correspondra lorsqu'une chaîne commence par un guillemet simple et se termine par un guillemet double, ou vice versa.
Ghopper21

Il a également des problèmes pour attraper "N'oubliez pas le @" - Il s'arrête après "Don".
Benny Neugebauer

0

Toutes les réponses ci-dessus sont bonnes .... sauf qu'elles ne prennent pas en charge tous les caractères unicode! à ECMA Script (Javascript)

Si vous êtes un utilisateur de nœud, vous souhaiterez peut-être la version modifiée de la réponse acceptée qui prend en charge tous les caractères unicode:

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

Essayez ici .


1
Qu'est-ce qu'un caractère non unicode? AFAIK unicode couvre tous les caractères.
Toto

1
Pourquoi pensez-vous que c'est une question javascript? De plus, lookbehind n'est pas pris en charge dans tous les navigateurs, regex101 jette? The preceding token is not quantifiable
Toto

@Toto, ce que je veux dire, c'est "ne prend pas en charge tous les caractères unicode". Je vous remercie. Bien que la question porte sur l'expression régulière en général, je ne veux pas souligner que l'utilisation d'affirmations de limites de mots provoquerait un comportement indésirable dans Javascript. Et bien sûr, alors que les Javascripts sont généralement pour le navigateur, il y a aussi Node.
Donovan P
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.