Regex pour la chaîne entre guillemets avec des guillemets d'échappement


122

Comment obtenir la sous-chaîne à l' " It's big \"problem "aide d'une expression régulière?

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

1
Comment trouvez-vous «C'est» dans une chaîne qui ne contient que «Est»? Je le réparerais pour vous, mais je ne sais pas quelles conventions de guillemet simple / d'échappement s'appliquent dans la langue que vous utilisez.
Jonathan Leffler


2
En fait, en regardant les dates, je vois que l'autre question est un double de celle-ci. De toute façon, assurez-vous de vérifier ma réponse .
ridgerunner

@ridgerunner: Je vote pour fermer ceci comme vous l'avez suggéré. C'est vrai qu'une autre question est plus récente, mais c'est aussi beaucoup mieux (grâce surtout à votre réponse).
Alan Moore

Réponses:


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

Fonctionne dans The Regex Coach et PCRE Workbench.

Exemple de test en JavaScript:

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


24
Logique. Anglais simple: deux guillemets entourant zéro ou plus de «tout caractère qui n'est pas une citation ou une barre oblique inverse» ou «une barre oblique inverse suivie de n'importe quel caractère». Je ne peux pas croire que je n'ai pas pensé à faire ça ...
Ajedi32

7
Je vais me répondre. =) (?:...)est un groupe passif ou non capturant. Cela signifie qu'il ne peut pas être référencé ultérieurement.
magras

après avoir beaucoup cherché et testé, c'est la vraie et seule solution que j'ai trouvée à ce problème courant. Merci!
cancerbero

10
Merci pour cela. Je voulais aussi faire correspondre des guillemets simples, alors j'ai fini par l'adapter à ceci:/(["'])(?:[^\1\\]|\\.)*?\1/
leo

Avec var s = ' my \\"new\\" string and \"this should be matched\"';, cette approche conduira à des résultats inattendus.
Wiktor Stribiżew

32

Celui-ci provient de nanorc.sample disponible dans de nombreuses distributions Linux. Il est utilisé pour la coloration syntaxique des chaînes de style C

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

Avec var s = ' my \\"new\\" string and \"this should be matched\"';, cette approche conduira à des résultats inattendus.
Wiktor Stribiżew

1
c.nanorc était le premier endroit où je suis allé. Impossible de le faire fonctionner dans le cadre d'un littéral de chaîne C jusqu'à ce que tout double échappement comme celui-ci" \"(\\\\.|[^\\\"])*\" "
hellork

Cela fonctionne avec les fonctions egrep et re_comp / re_exec de la libc.
fk0

19

Comme fourni par ePharaoh, la réponse est

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

Pour appliquer ce qui précède aux chaînes entre guillemets simples ou doubles, utilisez

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

2
C'est le seul ensemble qui a fonctionné pour moi avec une seule grande chaîne citée de 1,5 Ko contenant 99 échappements. Toutes les autres expressions de cette page ont éclaté dans mon éditeur de texte avec une erreur de débordement. Bien que la plupart ici fonctionnent dans le navigateur, juste quelque chose à garder à l'esprit. Fiddle: jsfiddle.net/aow20y0L
Beejor

3
Voir la réponse de @ MarcAndrePoulin ci-dessous pour une explication.
shaunc

10

La plupart des solutions proposées ici utilisent des chemins de répétition alternatifs, c'est-à-dire (A | B) *.

Vous pouvez rencontrer des débordements de pile sur des entrées volumineuses car certains compilateurs de modèles implémentent cela en utilisant la récursivité.

Java par exemple: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6337993

Quelque chose comme ça: "(?:[^"\\]*(?:\\.)?)*" ou celui fourni par Guy Bedford réduira le nombre d'étapes d'analyse en évitant la plupart des débordements de pile.



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

Tiré directement d' man perlreun système Linux avec Perl 5.22.0 installé. En guise d'optimisation, cette expression régulière utilise la forme «posessive» des deux +et *pour empêcher le retour en arrière, car il est connu à l'avance qu'une chaîne sans guillemets fermants ne correspondrait dans aucun cas.


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

devrait fonctionner avec n'importe quelle chaîne entre guillemets


1
Nice, mais trop flexible pour la demande (correspondra aux guillemets simples ...). Et peut être simplifié en /".*?(?<!\)"/ sauf si je manque quelque chose. Oh, et certains langages (par exemple JavaScript) ne comprennent hélas pas les expressions de regard négatif.
PhiLho

1
@PhiLho, l'utilisation d'un seul (? <! \\) échouerait sur les contre-obliques échappées à la fin de la chaîne. C'est vrai pour les look-behind en JavaScript.
Markus Jarderot

4

Celui-ci fonctionne parfaitement sur PCRE et ne tombe pas avec StackOverflow.

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

Explication:

  1. Chaque chaîne entre guillemets commence par Char: ";
  2. Il peut contenir n'importe quel nombre de caractères: .*?{Lazy match}; se terminant par un caractère non d'échappement[^\\] ;
  3. L'instruction (2) est facultative Lazy (!) Car la chaîne peut être vide (""). Alors:(.*?[^\\])??
  4. Enfin, chaque chaîne entre guillemets se termine par Char ( "), mais elle peut être précédée d'un nombre pair de paires de signes d'échappement (\\\\)+; et c'est Greedy (!) optionnel: ((\\\\)+)?+{Greedy matching}, la chaîne bacause peut être vide ou sans paires de fin!

Ce n'est pas le modèle le plus efficace du monde, mais l'idée est intéressante. Notez que vous pouvez le raccourcir comme ceci:"(.*?[^\\])?(\\\\)*"
Casimir et Hippolyte

2

en voici un qui fonctionne à la fois avec "et" et vous en ajoutez facilement d'autres au début.

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

il utilise la référence arrière (\ 1) qui correspond exactement à ce qui est dans le premier groupe ("ou ').

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


c'est une très bonne solution, mais [^\1]devrait être remplacée par .car il n'y a pas d'anti-back-reference, et cela n'a pas d'importance de toute façon. la première condition correspondra toujours avant que quelque chose de mauvais puisse arriver.
Seph Reed

@SephReed - remplacement [^\1]avec .aurait pour effet de changer cette expression rationnelle ("|').*?\1et il correspondrait "foo\"à "foo \" bar". Cela dit, il [^\1]est difficile de se mettre au travail. @ Mathiashansen - Vous êtes mieux avec le peu maniable et cher (?!\1).(donc l'ensemble de la regex, avec un certain nettoyage d'efficacité, le serait (["'])(?:\\.|(?!\1).)*+\1. Le +est facultatif si votre moteur ne le prend pas en charge.
Adam Katz

2

Une option qui n'a pas encore été abordée est:

  1. Inversez la corde.
  2. Effectuez la correspondance sur la chaîne inversée.
  3. Re-inversez les chaînes correspondantes.

Cela a l'avantage supplémentaire de pouvoir faire correspondre correctement les balises ouvertes échappées.

Disons que vous avez eu la chaîne suivante; String \"this "should" NOT match\" and "this \"should\" match" Ici, \"this "should" NOT match\"ne devrait pas être apparié et "should"devrait l'être. En plus de cela, il this \"should\" matchdevrait être égalé et \"should\"non.

D'abord un exemple.

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

Bon, maintenant pour expliquer le RegExp. C'est l'expression rationnelle qui peut être facilement divisée en trois morceaux. Comme suit:

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

C'est probablement beaucoup plus clair sous forme d'image: généré à l'aide du Regulex de Jex

Image sur github (Visualiseur d'expressions régulières JavaScript.) Désolé, je n'ai pas une réputation assez élevée pour inclure des images, donc, ce n'est qu'un lien pour le moment.

Voici l'essentiel d'un exemple de fonction utilisant ce concept un peu plus avancé: https://gist.github.com/scagood/bd99371c072d49a4fee29d193252f5fc#file-matchquotes-js


0

Il faut se rappeler que les expressions régulières ne sont pas une solution miracle pour tout ce qui est string-y. Certaines choses sont plus simples à faire avec un curseur et une recherche linéaire, manuelle. Un CFL ferait l'affaire assez trivialement, mais il n'y a pas beaucoup d'implémentations CFL (afaik).


3
C'est vrai, mais ce problème est bien dans les capacités des expressions régulières, et il existe de nombreuses implémentations de celles-ci.
Alan Moore

0

Une version plus complète de https://stackoverflow.com/a/10786066/1794894

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

Cette version contient également

  1. Longueur de devis minimum de 50
  2. Type supplémentaire de citations (ouvrir et fermer )

0

Je me suis trompé sur regexpal et j'ai fini avec cette expression régulière: (Ne me demandez pas comment cela fonctionne, je comprends à peine même si je l'ai écrit lol)

"(([^"\\]?(\\\\)?)|(\\")+)+"

0

Si elle est recherchée depuis le début, peut-être que cela peut fonctionner?

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

0

J'ai rencontré un problème similaire en essayant de supprimer les chaînes entre guillemets qui peuvent interférer avec l'analyse de certains fichiers.

Je me suis retrouvé avec une solution en deux étapes qui surpasse toute expression régulière alambiquée que vous pouvez trouver:

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

Plus facile à lire et probablement plus efficace.


0

Si votre IDE est IntelliJ Idea, vous pouvez oublier tous ces maux de tête et stocker votre expression régulière dans une variable String et lorsque vous la copiez-collez à l'intérieur du guillemet double, elle passera automatiquement à un format acceptable pour les expressions régulières.

exemple en Java:

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

maintenant vous pouvez utiliser cette variable dans votre expression régulière ou n'importe où.

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.