Regex: Apparier par exclusion, sans anticipation - est-ce possible?
-
19-08-2019 - |
Question
Dans certains types d’expression regex, les assertions [négatives] de largeur nulle (recherche future / anticipée) ne sont pas prises en charge.
Cela rend extrêmement difficile (impossible?) d’énoncer une exclusion. Par exemple, "chaque ligne dont ne dispose pas " n'a pas "foo". dessus ", comme ceci:
^((?!foo).)*$
Peut-on obtenir le même résultat sans recourir à une simple recherche (problèmes de complexité et de performances laissés de côté pour le moment)?
La solution
UPDATE: Il échoue "avec deux ff avant oo". comme @Ciantic a souligné dans les commentaires .
^(f(o[^o]|[^o])|[^f])*$
REMARQUE: il est beaucoup plus facile de nier une correspondance côté client au lieu d'utiliser la regex ci-dessus.
Les expressions rationnelles supposent que chaque ligne se termine par un caractère de nouvelle ligne si ce n'est pas le cas, consultez les expressions rationnelles de C ++ et de grep.
Les exemples de programmes en Perl, Python, C ++ et grep
donnent tous le même résultat.
-
#!/usr/bin/perl -wn print if /^(f(o[^o]|[^o])|[^f])*$/;
-
#!/usr/bin/env python import fileinput, re, sys from itertools import ifilter re_not_foo = re.compile(r"^(f(o[^o]|[^o])|[^f])*
quot;) for line in ifilter(re_not_foo.match, fileinput.input()): sys.stdout.write(line)#include <iostream> #include <string> #include <boost/regex.hpp> int main() { boost::regex re("^(f(o([^o]|$)|([^o]|$))|[^f])*
quot;); //NOTE: "|$ grep "^\(f\(o\([^o]\|$\)\|\([^o]\|$\)\)\|[^f]\)*
quot; in.txtfoo 'foo' abdfoode abdfode abdfde abcde f fo foo fooo ofooa ofo ofoo
quot;s are there due to `getline()` strips newline char std::string line; while (std::getline(std::cin, line)) if (boost::regex_match(line, re)) std::cout << line << std::endl; }abdfode abdfde abcde f fo ofo
-
c ++
<*> - <*>
Exemple de fichier:
<*>Sortie:
<*>Autres conseils
Je suis tombé sur cette question et avons pris le fait qu’il n’y avait pas de regex pleinement fonctionnel comme un défi personnel. Je pense avoir réussi à créer une expression rationnelle qui fonctionne pour toutes les entrées - à condition que vous puissiez utiliser groupement atomique / quantificateurs possessifs .
Bien sûr, je ne suis pas sûr s'il existe des variantes qui permettent le groupement atomique mais non la recherche, mais la question demandait s'il était possible dans une regex d'énoncer une exclusion sans recherche, et elle < em> est techniquement possible:
\A(?:$|[^f]++|f++(?:[^o]|$)|(?:f++o)*+(?:[^o]|$))*\Z
Explication:
\A #Start of string
(?: #Non-capturing group
$ #Consume end-of-line. We're not in foo-mode.
|[^f]++ #Consume every non-'f'. We're not in foo-mode.
|f++(?:[^o]|$) #Enter foo-mode with an 'f'. Consume all 'f's, but only exit foo-mode if 'o' is not the next character. Thus, 'f' is valid but 'fo' is invalid.
|(?:f++o)*+(?:[^o]|$) #Enter foo-mode with an 'f'. Consume all 'f's, followed by a single 'o'. Repeat, since '(f+o)*' by itself cannot contain 'foo'. Only exit foo-mode if 'o' is not the next character following (f+o). Thus, 'fo' is valid but 'foo' is invalid.
)* #Repeat the non-capturing group
\Z #End of string. Note that this regex only works in flavours that can match $\Z
Si, pour une raison quelconque, vous pouvez utiliser le groupement atomique mais pas les quantificateurs possessifs ni les comparaisons, vous pouvez utiliser:
\A(?:$|(?>[^f]+)|(?>f+)(?:[^o]|$)|(?>(?:(?>f+)o)*)(?:[^o]|$))*\Z
Comme d'autres l'ont déjà fait remarquer, il est probablement plus pratique de simplement nier une correspondance par d'autres moyens.
Vous pouvez généralement rechercher foo et inverser le résultat de la correspondance d'expression régulière à partir du code client.
Pour un exemple simple, supposons que vous vouliez vérifier qu'une chaîne de caractères ne contient que certains caractères.
Vous pouvez écrire cela comme ceci:
^ [A-Za-z0-9. $ -] * $
et acceptez un résultat true
comme valide ou comme ceci:
[^ A-Za-z0-9. $ -]
et acceptez le résultat false
comme valide.
Bien sûr, ce n’est pas toujours une option: il vous suffit parfois de placer l’expression dans un fichier de configuration ou de la transmettre à un autre programme, par exemple. Mais ça vaut la peine de s'en souvenir. Votre problème spécifique, par exemple, l’expression est beaucoup beaucoup plus simple si vous pouvez utiliser la négation de la sorte.
Je suis tombé sur cette question à la recherche de ma propre solution d’exclusion de regex, dans laquelle j’essaie d’exclure une séquence au sein de mon regex.
Ma première réaction à cette situation: Par exemple, "toutes les lignes sans" foo ". sur celui-ci "" consistait simplement à utiliser l'option de correspondance de sens -v inverse dans grep.
grep -v foo
ceci renvoie toutes les lignes d'un fichier qui ne correspondent pas à "foo"
C’est tellement simple que j’ai le sentiment que je viens de mal interpréter votre question ....