2009-06-29 11 views
5

J'essaye d'écrire une regex qui correspondra à tout MAIS une apostrophe qui n'a pas été échappée. Considérez ce qui suit:Comment regex peut-il ignorer les guillemets échappés lors de la recherche de chaînes?

<?php $s = 'Hi everyone, we\'re ready now.'; ?> 

Mon but est d'écrire une expression régulière qui correspondra essentiellement à la partie de chaîne de ce. Je pense à quelque chose comme

/.*'([^']).*/ 

afin de faire correspondre une chaîne simple, mais je l'ai essayé de comprendre comment obtenir un lookbehind négatif à travailler sur ce pour faire en sorte que apostrophes ne sont pas précédés par un antislash ...

Des idées?

- JMT

Répondre

3
<?php 
$backslash = '\\'; 

$pattern = <<< PATTERN 
#(["'])(?:{$backslash}{$backslash}?+.)*?{$backslash}1# 
PATTERN; 

foreach(array(
    "<?php \$s = 'Hi everyone, we\\'re ready now.'; ?>", 
    '<?php $s = "Hi everyone, we\\"re ready now."; ?>', 
    "xyz'a\\'bc\\d'123", 
    "x = 'My string ends with with a backslash\\\\';" 
    ) as $subject) { 
     preg_match($pattern, $subject, $matches); 
     echo $subject , ' => ', $matches[0], "\n\n"; 
} 

impressions

<?php $s = 'Hi everyone, we\'re ready now.'; ?> => 'Hi everyone, we\'re ready now.' 

<?php $s = "Hi everyone, we\"re ready now."; ?> => "Hi everyone, we\"re ready now." 

xyz'a\'bc\d'123 => 'a\'bc\d' 

x = 'My string ends with with a backslash\\'; => 'My string ends with with a backslash\\' 
+0

Voter parce que vous avez fourni des cas de test. –

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

La partie parenthésée cherche non/barre oblique inverse et apostrophes caractères backslash-échappé. Si seulement certains caractères peuvent être échappés, changez le \\. en \\['\\a-z], ou peu importe.

+0

Très près, mais qui ne gère pas le cas pathologique ... « Ma chaîne se termine par une barre oblique inverse \\ » –

+0

Merci John! Heureusement pour moi, les cas que je vais devoir traiter peuvent être restreints, et n'atteindront jamais le problème décrit par the.jxc. Solution très simple, dont j'aurais vraiment dû penser. Encore merci! :) – JMTyler

0

Via regard négatif derrière:

/ 
.*?'    #Match until ' 
(
.*?    #Lazy match & capture of everything after the first apostrophe 
)  
(?<!(?<!\\)\\)' #Match first apostrophe that isn't preceded by \, but accept \\ 
.*    #Match remaining text 
/
0
Regex reg = new Regex("(?<!\\\\)'(?<string>.*?)(?<!\\\\)'"); 
3

Voici ma solution avec des cas de test:

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

Et mon (Perl, mais je n'utilise pas de fonctionnalités spécifiques à Perl, je ne pense pas) la preuve:

use strict; 
use warnings; 

my %tests =(); 
$tests{'Case 1'} = <<'EOF'; 
$var = 'My string'; 
EOF 

$tests{'Case 2'} = <<'EOF'; 
$var = 'My string has it\'s challenges'; 
EOF 

$tests{'Case 3'} = <<'EOF'; 
$var = 'My string ends with a backslash\\'; 
EOF 

foreach my $key (sort (keys %tests)) { 
    print "$key...\n"; 
    if ($tests{$key} =~ m/.*?'((?:\\\\|\\'|[^'])*+)'/) { 
     print " ... '$1'\n"; 
    } else { 
     print " ... NO MATCH\n"; 
    } 
} 

L'exécution de cette montre:

$ perl a.pl 
Case 1... 
... 'My string' 
Case 2... 
... 'My string has it\'s challenges' 
Case 3... 
... 'My string ends with a backslash\\' 

Notez que le caractère générique initial au Le démarrage doit être non gourmand. Ensuite, j'utilise des correspondances non-backtracking pour engloutir \\ et \ 'et ensuite tout ce qui n'est pas un caractère de guillemets autonome.

Je pense que celui-ci imite probablement l'approche intégrée du compilateur, ce qui devrait le rendre très résistant aux balles.

0

Ceci est pour JavaScript:

/('|")(?:\\\\|\\\1|[\s\S])*?\1/

... il

  • matchs chaînes entre guillemets simples ou doubles
  • matchs des chaînes vides (longueur 0)
  • matchs avec les chaînes espace blanc intégré (\n, \t, etc.)
  • skips guillemets échappés internes (simple ou double)
  • Saute guillemets simples dans des guillemets doubles et vice versa

Seule la première citation est capturé. Vous pouvez capturer la chaîne unquoted en $ 2 avec:

/('|")((?:\\\\|\\\1|[\s\S])*?)\1/