157 votes

Regex pour chaîne de caractères avec guillemets échappatoires

Comment puis-je obtenir la sous-chaîne " It's big \"problem " en utilisant une expression régulière ?

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

11voto

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 importantes, car certains compilateurs de motifs les mettent en œuvre en utilisant la récursion.

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 syntaxique en évitant la plupart des débordements de pile.

9voto

Tosh Afanasiev Points 61
"(?:\\"|.)*?"

L'alternance de la \" et le . passe sur les guillemets échappés alors que le quantificateur paresseux *? garantit que vous ne dépasserez pas la fin de la chaîne citée. Fonctionne avec les classes .NET Framework RE

8voto

ack Points 582
/"(?:[^"\\]++|\\.)*+"/

Tiré directement de man perlre sur un système Linux avec Perl 5.22.0 installé. Par souci d'optimisation, cette regex utilise la forme "posessive" des deux éléments suivants + y * pour éviter le retour en arrière, car on sait à l'avance qu'une chaîne sans guillemet fermant ne correspondrait pas dans tous les cas.

5voto

Vadim Sayfi Points 51

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 un nombre quelconque de caractères : .*? {Lazy match} ; se terminant par un caractère non échappatoire [^\\] ;
  3. L'instruction (2) est facultative paresseuse ( !) car la chaîne peut être vide (""). Donc : (.*?[^\\])??
  4. Enfin, chaque chaîne citée se termine par Char( " ), mais il peut être précédé d'un nombre pair de paires de signes d'échappement (\\\\)+ ; et il est Greedy( !) facultatif : ((\\\\)+)?+ {Concordance avide}, car la chaîne peut être vide ou sans paires finales !

3voto

scagood Points 615

Une option qui n'a pas été abordée auparavant est la suivante :

  1. Inversez la chaîne.
  2. Effectuez la correspondance sur la chaîne inversée.
  3. Inversez les cordes appariées.

Cette méthode présente l'avantage supplémentaire de pouvoir faire correspondre correctement les balises ouvertes échappées.

Disons que vous avez la chaîne suivante ; String \"this "should" NOT match\" and "this \"should\" match" Ici, \"this "should" NOT match\" ne doivent pas être appariés et "should" devrait être. En plus de cela this \"should\" match doivent être appariés et \"should\" ne devrait pas.

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

Ok, maintenant expliquons le RegExp. L'expression géographique peut être facilement divisée en trois parties. 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 de Regulex de Jex

Image sur github (JavaScript Regular Expression Visualizer.) Désolé, je n'ai pas une réputation assez élevée pour inclure des images, donc, c'est juste 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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X