--Modifier-- Les réponses actuelles ont quelques idées utiles mais je veux quelque chose de plus complet que je puisse comprendre et réutiliser à 100%; c'est pourquoi j'ai mis une prime. De plus, les idées qui fonctionnent partout sont meilleures pour moi que la syntaxe standard comme\K
Cette question porte sur la façon dont je peux faire correspondre un modèle, sauf dans certaines situations s1 s2 s3. Je donne un exemple spécifique pour montrer ma signification mais je préfère une réponse générale que je peux comprendre à 100% pour pouvoir la réutiliser dans d'autres situations.
Exemple
Je veux faire correspondre cinq chiffres en utilisant \b\d{5}\b
mais pas dans trois situations s1 s2 s3:
s1: Pas sur une ligne qui se termine par un point comme cette phrase.
s2: Pas n'importe où dans les parens.
s3: pas dans un bloc qui commence par if(
et se termine par//endif
Je sais comment résoudre n'importe lequel des s1 s2 s3 avec une anticipation et un regard en arrière, en particulier en C # lookbehind ou \K
en PHP.
Par exemple
s1 (?m)(?!\d+.*?\.$)\d+
s3 avec C # lookbehind (?<!if\(\D*(?=\d+.*?//endif))\b\d+\b
s3 avec PHP \ K (?:(?:if\(.*?//endif)\D*)*\K\d+
Mais le mélange des conditions fait exploser ma tête. Encore plus de mauvaises nouvelles, c'est que je devrai peut-être ajouter d'autres conditions s4 s5 à un autre moment.
La bonne nouvelle est que je m'en fiche si je traite les fichiers en utilisant les langages les plus courants comme PHP, C #, Python ou la machine à laver de mon voisin. :) Je suis à peu près un débutant en Python et Java mais intéressé à savoir s'il a une solution.
Je suis donc venu ici pour voir si quelqu'un pense à une recette flexible.
Les indices sont corrects: vous n'avez pas besoin de me donner le code complet. :)
Je vous remercie.
\K
Il n'y a pas de syntaxe php spéciale. Veuillez préciser et clarifier ce que vous voulez dire. Si vous souhaitez nous dire que vous n'avez pas besoin d'une solution «compliquée», vous devez dire ce qui est compliqué pour vous et pourquoi."if("
open paren soit fermé, non pas avec a")"
, mais plutôt avec a"//endif"
:? Et si pour s3 vous vouliez vraiment dire que la clause if doit être fermée avec"//endif)"
:, alors l'exigence s3 est un sous-ensemble de s2.especially in C# lookbehind or \K in PHP
... Mais C # regarde en arrière pas seulement C # c'est .NET donc vous pouvez vous plaindre aussi je dis C # pas .NET :) Et en réponse je dis Ruby pas Onigurama c'est mauvais aussi ... Y a-t-il un autre langage qui utilise PCRE? Ne pas parler de Notepad ++ ou des outils de serveur, c'est une question sur l'utilisation de la fonctionnalité dans le langage, j'espère l'expliquer et désolé si cela semble fauxRéponses:
Hans, je vais prendre l'appât et étoffer ma réponse précédente. Vous avez dit que vous vouliez «quelque chose de plus complet», alors j'espère que la longue réponse ne vous dérangera pas - juste essayer de plaire. Commençons par un peu de contexte.
Tout d'abord, c'est une excellente question. Il y a souvent des questions sur la correspondance de certains modèles, sauf dans certains contextes (par exemple, dans un bloc de code ou entre parenthèses). Ces questions donnent souvent lieu à des solutions assez délicates. Votre question sur plusieurs contextes est donc un défi particulier.
Surprise
De manière surprenante, il existe au moins une solution efficace, générale, facile à mettre en œuvre et agréable à entretenir. Il fonctionne avec toutes les versions de regex qui vous permettent d'inspecter les groupes de capture dans votre code. Et il se trouve qu'il répond à un certain nombre de questions courantes qui peuvent au premier abord sembler différentes des vôtres: "correspond à tout sauf Donuts", "remplace tout sauf ...", "correspond à tous les mots sauf ceux de la liste noire de ma mère", "ignorer balises "," correspondent à la température sauf en italique "...
Malheureusement, la technique n'est pas bien connue: j'estime que sur vingt questions SO qui pourraient l'utiliser, une seule a une réponse qui la mentionne - ce qui signifie peut-être une réponse sur cinquante ou soixante. Voir mon échange avec Kobi dans les commentaires. La technique est décrite en détail dans cet article qui l'appelle (avec optimisme) le "meilleur truc de regex jamais créé". Sans entrer dans les détails, je vais essayer de vous donner une idée précise du fonctionnement de la technique. Pour plus de détails et des exemples de code dans différentes langues, je vous encourage à consulter cette ressource.
Une variante mieux connue
Il existe une variante utilisant la syntaxe spécifique à Perl et PHP qui accomplit la même chose. Vous le verrez sur SO entre les mains de maîtres regex tels que CasimiretHippolyte et HamZa . Je vous en dirai plus à ce sujet ci-dessous, mais je me concentre ici sur la solution générale qui fonctionne avec toutes les saveurs de regex (tant que vous pouvez inspecter les groupes de capture dans votre code).
Fait clé
En fait, l'astuce est de faire correspondre les différents contextes que nous ne voulons pas (enchaîner ces contextes à l'aide du
|
OU / alternance) pour les «neutraliser». Après avoir établi tous les contextes indésirables, la dernière partie de l'alternance correspond à ce que nous ne voulons et le capture au Groupe 1.La recette générale est
Cela correspondra
Not_this_context
, mais dans un sens, cette correspondance va dans une poubelle, car nous ne regarderons pas les correspondances globales: nous ne regardons que les captures du groupe 1.Dans votre cas, avec vos chiffres et vos trois contextes à ignorer, nous pouvons faire:
Notez que parce que nous faisons correspondre s1, s2 et s3 au lieu d'essayer de les éviter avec des lookarounds, les expressions individuelles pour s1, s2 et s3 peuvent rester claires comme le jour. (Ce sont les sous-expressions de chaque côté de a
|
)L'expression entière peut être écrite comme ceci:
Regardez cette démo (mais concentrez-vous sur les groupes de capture dans le volet inférieur droit.)
Si vous essayez mentalement de diviser cette expression régulière à chaque
|
délimiteur, il ne s'agit en fait que d'une série de quatre expressions très simples.Pour les saveurs qui prennent en charge l'espacement libre, cela se lit particulièrement bien.
Ceci est exceptionnellement facile à lire et à maintenir.
Extension de l'expression régulière
Lorsque vous souhaitez ignorer plus de situations s4 et s5, vous les ajoutez en plusieurs alternances sur la gauche:
Comment cela marche-t-il?
Les contextes que vous ne voulez pas sont ajoutés à une liste d'alternatives sur la gauche: ils correspondent, mais ces correspondances globales ne sont jamais examinées, donc les faire correspondre est un moyen de les mettre dans une "poubelle".
Cependant, le contenu souhaité est capturé dans le groupe 1. Vous devez ensuite vérifier par programme que le groupe 1 est défini et non vide. C'est une tâche de programmation triviale (et nous parlerons plus tard de la façon dont cela est fait), d'autant plus qu'elle vous laisse avec une simple expression régulière que vous pouvez comprendre en un coup d'œil et réviser ou étendre si nécessaire.
Je ne suis pas toujours fan des visualisations, mais celle-ci montre à quel point la méthode est simple. Chaque «ligne» correspond à une correspondance potentielle, mais seule la ligne du bas est capturée dans le groupe 1.
Démo Debuggex
Variation Perl / PCRE
Contrairement à la solution générale ci-dessus, il existe une variation pour Perl et PCRE qui est souvent vue sur SO, au moins entre les mains de regex Gods tels que @CasimiretHippolyte et @HamZa. C'est:
Dans ton cas:
Cette variante est un peu plus facile à utiliser car le contenu correspondant dans les contextes s1, s2 et s3 est simplement ignoré, vous n'avez donc pas besoin d'inspecter les captures du groupe 1 (notez que les parenthèses ont disparu). Les matchs ne contiennent que
whatYouWant
Notez que
(*F)
,(*FAIL)
et(?!)
sont tous la même chose. Si vous vouliez être plus obscur, vous pourriez utiliser(*SKIP)(?!)
démo pour cette version
Applications
Voici quelques problèmes courants que cette technique peut souvent résoudre facilement. Vous remarquerez que le choix des mots peut faire que certains de ces problèmes sonnent différemment alors qu'ils sont pratiquement identiques.
<a stuff...>...</a>
?<i>
balise ou un extrait de code javascript (plus de conditions)?Comment programmer les captures du groupe 1
Vous n'avez pas comme pour le code, mais, pour la complétion ... Le code à inspecter dans le groupe 1 dépendra évidemment de la langue de votre choix. En tout cas, il ne devrait pas ajouter plus de quelques lignes au code que vous utiliseriez pour inspecter les correspondances.
En cas de doute, je vous recommande de consulter la section des exemples de code de l'article mentionné précédemment, qui présente du code pour un certain nombre de langues.
Alternatives
En fonction de la complexité de la question et du moteur regex utilisé, il existe plusieurs alternatives. Voici les deux qui peuvent s'appliquer à la plupart des situations, y compris plusieurs conditions. À mon avis, ni l'un ni l'autre n'est aussi attrayant que la
s1|s2|s3|(whatYouWant)
recette, ne serait-ce que parce que la clarté l'emporte toujours.1. Remplacez puis Match.
Une bonne solution qui semble piratée mais qui fonctionne bien dans de nombreux environnements est de travailler en deux étapes. Une première regex neutralise le contexte que vous souhaitez ignorer en remplaçant les chaînes potentiellement conflictuelles. Si vous souhaitez uniquement faire correspondre, vous pouvez remplacer par une chaîne vide, puis exécuter votre correspondance dans la deuxième étape. Si vous souhaitez remplacer, vous pouvez d'abord remplacer les chaînes à ignorer par quelque chose de distinctif, par exemple en entourant vos chiffres d'une chaîne de largeur fixe de
@@@
. Après ce remplacement, vous êtes libre de remplacer ce que vous vouliez vraiment, puis vous devrez rétablir vos@@@
chaînes distinctives .2. Lookarounds.
Votre message d'origine montrait que vous compreniez comment exclure une seule condition à l'aide de méthodes d'analyse. Vous avez dit que C # est parfait pour cela, et vous avez raison, mais ce n'est pas la seule option. Les saveurs .NET regex trouvées dans C #, VB.NET et Visual C ++ par exemple, ainsi que le
regex
module encore expérimental à remplacerre
en Python, sont les deux seuls moteurs que je connais qui prennent en charge le lookbehind de largeur infinie. Avec ces outils, une condition dans un regard en arrière peut prendre en charge non seulement de regarder en arrière, mais aussi le match et au-delà du match, évitant ainsi le besoin de se coordonner avec une anticipation. Plus de conditions? Plus de lookarounds.En recyclant le regex que vous aviez pour s3 en C #, tout le modèle ressemblerait à ceci.
Mais maintenant vous savez que je ne recommande pas cela, non?
Suppressions
@HamZa et @Jerry ont suggéré de mentionner une astuce supplémentaire pour les cas où vous cherchez simplement à supprimer
WhatYouWant
. Vous vous souvenez que la recette à associerWhatYouWant
(la capturer dans le groupe 1) étaits1|s2|s3|(WhatYouWant)
, non? Pour supprimer toutes les instances deWhatYouWant
, vous changez l'expression régulière enPour la chaîne de remplacement, vous utilisez
$1
. Ce qui se passe ici, c'est que pour chaque instance des1|s2|s3
cette correspondance, le remplacement$1
remplace cette instance par lui-même (référencé par$1
). En revanche, quandWhatYouWant
est mis en correspondance, il est remplacé par un groupe vide et rien d'autre - et donc supprimé. Regardez cette démo , merci @HamZa et @Jerry d'avoir suggéré ce merveilleux ajout.Remplaçants
Cela nous amène aux remplacements, sur lesquels je vais aborder brièvement.
(*SKIP)(*F)
variante mentionnée ci-dessus pour correspondre exactement à ce que vous voulez et effectuez un remplacement direct.S'amuser!
Non, attendez, il y a plus!
Ah, non, je garde ça pour mes mémoires en vingt volumes, à paraître au printemps prochain.
la source
Tarzan
, mais pas à l’intérieur de guillemets doubles. Le/no|no|(yes)/
regex : trick serait quelque chose comme:/"[^"]*"|Tarzan/
(ignorant les caractères échappés). Cela fonctionne pour de nombreux cas, mais a complètement échoué lorsqu'il est appliqué au texte JavaScript valide suivant:var bug1 = 'One " quote here. Should match this Tarzan'; var bug2 = "Should not match this Tarzan";
. L'astuce de Rex ne fonctionne que lorsque TOUTES les structures possibles sont mises en correspondance - en d'autres termes - vous devez analyser complètement le texte pour garantir une précision de 100%.var bug1 = /"[^"]*"|(Tarzan)/gi;
et avoir le même effet (et ce deuxième exemple n'est certainement pas un cas limite). Je pourrais citer de nombreux autres exemples où cette technique ne fonctionne pas de manière fiable.(?<!\\)"(?:\\"|[^"\r\n])*+"
Vous ne tirez pas les gros canons à moins que vous n'ayez une raison. Le principe de la solution est toujours valable. Si nous ne sommes pas en mesure d'exprimer un motif à mettre sur le côté gauche, c'est une autre histoire, nous avons besoin d'une solution différente. Mais la solution fait ce qu'elle annonce.Faites trois correspondances différentes et gérez la combinaison des trois situations à l'aide de la logique conditionnelle dans le programme. Vous n'avez pas besoin de tout gérer dans une seule regex géante.
EDIT: laissez-moi développer un peu car la question est devenue plus intéressante :-)
L'idée générale que vous essayez de capturer ici est de faire correspondre un certain modèle d'expression régulière, mais pas quand il y a certains autres modèles (qui pourraient être n'importe quel nombre) présents dans la chaîne de test. Heureusement, vous pouvez tirer parti de votre langage de programmation: gardez les expressions régulières simples et utilisez simplement un conditionnel composé. Une meilleure pratique serait de capturer cette idée dans un composant réutilisable, alors créons une classe et une méthode qui l'implémentent:
Donc ci-dessus, nous avons configuré la chaîne de recherche (les cinq chiffres), plusieurs chaînes d'exception (vos s1 , s2 et s3 ), puis essayons de faire correspondre plusieurs chaînes de test. Les résultats imprimés doivent être comme indiqué dans les commentaires à côté de chaque chaîne de test.
la source
Votre exigence selon laquelle ce n'est pas à l'intérieur des parens est impossible à satisfaire dans tous les cas. À savoir, si vous pouvez d'une manière ou d'une autre trouver un
(
à gauche et)
à droite, cela ne signifie pas toujours que vous êtes à l'intérieur des parens. Par exemple.(....) + 55555 + (.....)
- pas encore à l'intérieur des parens(
et)
à gauche et à droiteMaintenant, vous pourriez vous penser intelligent et ne chercher
(
à gauche que si vous ne rencontrez pas)
avant et vice versa à droite. Cela ne fonctionnera pas dans ce cas:((.....) + 55555 + (.....))
- à l'intérieur des parens même s'il y a des fermetures)
et(
à gauche et à droite.Il est impossible de savoir si vous êtes à l'intérieur de parens en utilisant regex, car regex ne peut pas compter combien de parens ont été ouvertes et combien fermées.
Considérez cette tâche plus simple: en utilisant regex, découvrez si toutes les parens (éventuellement imbriquées) dans une chaîne sont fermées, c'est-à-dire pour toutes celles que
(
vous devez trouver)
. Vous découvrirez qu'il est impossible à résoudre et si vous ne pouvez pas résoudre cela avec regex, vous ne pouvez pas déterminer si un mot est à l'intérieur de parens dans tous les cas, car vous ne pouvez pas trouver une certaine position dans la chaîne si tous les précédents(
ont un correspondant)
.la source
Hans si cela ne vous dérange pas, j'ai utilisé la machine à laver de votre voisin appelée perl :)
Modifié: sous un pseudo code:
Compte tenu du fichier input.txt:
Et le script validator.pl:
Exécution:
la source
Je ne sais pas si cela vous aiderait ou non, mais je propose une solution en tenant compte des hypothèses suivantes -
Cependant, j'ai également considéré ce qui suit -
if(
blocs.Ok, voici la solution -
J'ai utilisé C # et avec lui MEF (Microsoft Extensibility Framework) pour implémenter les analyseurs configurables. L'idée est d'utiliser un seul analyseur pour analyser et une liste de classes de validateur configurables pour valider la ligne et retourner vrai ou faux en fonction de la validation. Ensuite, vous pouvez ajouter ou supprimer n'importe quel validateur à tout moment ou en ajouter de nouveaux si vous le souhaitez. Jusqu'à présent, j'ai déjà implémenté pour S1, S2 et S3 que vous avez mentionnés, vérifiez les classes au point 3. Vous devez ajouter des classes pour s4, s5 si vous en avez besoin à l'avenir.
Tout d'abord, créez les interfaces -
Puis vient le lecteur et le vérificateur de fichiers -
Vient ensuite l'implémentation de vérificateurs individuels, les noms de classe sont explicites, donc je ne pense pas qu'ils aient besoin de plus de descriptions.
Le programme -
Pour les tests, j'ai pris le fichier d'exemple de @ Tiago avec les
Test.txt
lignes suivantes -Donne la sortie -
Je ne sais pas si cela vous aiderait ou non, je me suis bien amusé à jouer avec .... :)
La meilleure partie est que, pour ajouter une nouvelle condition, tout ce que vous avez à faire est de fournir une implémentation de
IPatternMatcher
, elle sera automatiquement appelée et donc validera.la source
Identique à @ zx81
(*SKIP)(*F)
mais avec une assertion de recherche négative.DEMO
En python, je ferais facilement comme ça,
Production:
la source