Comment remplacer un caractère spécifique entre des mots spécifiques?

2

Seriez-vous en mesure de m'aider à comprendre comment utiliser la fonction Rechercher / Remplacer de Notepad ++ pour remplacer un caractère spécifique entre des mots spécifiques. Veuillez consulter l'exemple ci-dessous. J'aimerais trouver tous les traits d'union entre les mots 'START' et 'END' et les remplacer par des espaces. Tous les autres traits d'union dans le document ne seront pas remplacés.

J'ai essayé de résoudre ce problème en utilisant des exemples d'expressions régulières que j'ai cherchées sur Google mais, malheureusement, je n'ai pas réussi jusqu'à présent.

Voici ce que j'ai

START-Hyphens-should-be-replaced-here-01-END
OTHER-no-changes-here-02-WORD
START-Hyphens-should-also-be-replaced-here-03-END
OTHER-no-changes-here-either-04-TEXT

Voici ce que j'aimerais avoir:

START Hyphens should be replaced here 01 END
OTHER-no-changes-here-02-WORD
START Hyphens should also be replaced here 03 END
OTHER-no-changes-here-either-04-TEXT
Piotr Berebecki
la source
Hm ... Je recommanderais en fait d'éviter les expressions rationnelles et de simplement les analyser caractère par caractère dans un langage de script, si possible.
Bob
Une solution de regex serait plus faisable si vous pouvez garantir qu’il n’y aura jamais une seule occurrence de START et END par ligne, et qu’il ne recouvre pas les lignes (c’est-à-dire qu’il n’ya jamais de nouvelle ligne entre START et END ).
Bob
Salut Bob. Oui, je peux le garantir. Voulez-vous savoir comment faire cela avec regex? Je vous remercie :)
Piotr Berebecki

Réponses:

5

Je vous recommanderais d'abandonner complètement regex - il est tout simplement trop difficile de le faire fonctionner dans des cas comme celui-ci. Tout ne peut pas être résolu facilement avec regex! Dans ce cas, la plupart des langages de script peuvent le faire assez facilement. J'ai écrit un morceau de JavaScript pour vous, ici - entrez simplement vos délimiteurs et votre entrée, puis cliquez sur Soumettre.

Pour ce qui est des explications: il se scinde par le délimiteur de début, puis, pour chaque "bloc", il se divise par le délimiteur de fin. Nous nous retrouvons avec un bloc de texte entre un début et une fin et effectuons un remplacement pour cela. Ensuite, nous rejoignons les blocs séparés. Ce n'est pas le moyen le plus efficace, mais c'est un peu plus facile que de traiter des caractères individuels.

xkcd - Perl Problems


Vieille solution, partiellement efficace, regex se trouve ci-dessous. je fortement vous recommande de ne pas l'utiliser.

Réponse rapide: n'utilisez pas Notepad ++, utilisez ce JS Snippet (entrez votre texte en bas à droite et cliquez sur Soumettre). Remplacer START et END dans le script en bas à gauche si nécessaire.

Vous pouvez également utiliser PowerShell, qui est natif de Windows (Vista):

Get-Content input.txt | %{ $_ -replace "(?<=START.*?)-(?=.*?END)", " " } | Out-File output.txt

Remplacer input.txt et output.txt en conséquence. Remarque: il doit s'agir de fichiers différents.

Cette solution particulière ne fonctionne sans bugs que si vous n’avez que un apparition de START par ligne et un apparition de END par ligne et ils ne couvrent pas les lignes - nous pouvons donc traiter chaque ligne séparément.


Compte tenu des garanties selon lesquelles une seule correspondance sera nécessaire par ligne, chaque correspondance étant contenue dans une ligne, la correspondance entre (?<=START.*?)-(?=.*?END) et remplacer par un seul espace fonctionnerait - sauf Notepad ++ ne semble pas aimer ce motif particulier. Il prend en charge les regards indiscrets (en plein PCRE, apparemment), mais il y a quelque chose à ce sujet qui fait trébucher.

Une alternative, similaire à ce que Johannes a déjà répondu, consiste à utiliser un motif simple: (START.*?)-(.*?END) et le remplacer par \1 \2. Le problème ici est que cela ne fera que correspondre un trait d'union par ligne à la fois. Cela devient vite fastidieux.

Ici, nous essayons d’utiliser un langage de script plus complet. JavaScript est une bonne alternative. Cependant, il ne pas soutien regarder derrière. Sur le plan positif, nous pouvons exécuter les choses en boucle, de sorte que la solution fastidieuse précédente est réellement viable. J'ai créé un exemple ici .

Bob
la source
Vous ne savez pas trop pourquoi vous préférez utiliser des solutions de recherche par rapport à la solution que j'ai, elles ralentissent simplement les choses, car le moteur doit beaucoup faire marche arrière. D'accord avec la partie scripting cependant;)
Johannes H.
@JohannesH. Le problème, c’est que, si les lookbehind fonctionnent correctement, vous n’avez pas besoin de boucler. Oui, le moteur a besoin de beaucoup revenir en arrière, mais est-ce pire que de devoir répéter le match au complet le même nombre de fois?
Bob
Notepad ++ ne fait qu'un remplacement par ligne de toute façon. (ou, pour être plus précis, il continue sa recherche de correspondances après la fin de la correspondance en cours et n'essaie pas de faire correspondre le motif à quoi
Johannes H.
@JohannesH. La beauté des regards indiscrets est qu'ils ne font techniquement pas partie du match - cette réponse ne correspond donc qu'au trait d'union. Ensuite, la recherche doit continuer immédiatement après le trait d'union, ne faisant jamais correspondre aucun autre caractère. Je ne sais pas pourquoi cela ne fonctionne pas dans NP ++ - cela fonctionne très bien dans .NET (et Expresso, où j'ai testé).
Bob
1
@JohannesH. Aha! Apparemment, les lookbehind de longueur variable ne fonctionnent que dans .NET et Perl 6 (pas même Perl 5, sur lequel PCRE est principalement basé), des implémentations majeures de regex. Donc Notepad ++ ne supporte pas cela.
Bob
2

Une expression rationnelle appropriée serait (START[^\-]*)-(.*END), remplacez cela par \1 \2 et utilisez "Remplacer tout" PLUSIEURS FOIS (ne remplacera qu'un - sur chaque ligne par clic).

Johannes H.
la source
2
Vous avoir besoin utiliser .*? (match paresseux), sinon cela se terminera sur le dernier apparition de END, ce qui posera problème si END se produit plus d'une fois. Edit: vous devriez probablement utiliser *? dans la première capture aussi, sinon cela ferait probablement la même chose.
Bob
Oh. Vous avez raison, j'ai raté ça. Le modifiera (bien que votre réponse soit de toute façon beaucoup plus complète)
Johannes H.
Ah, tant pis pour le multiple END chose - même un match paresseux ne résout pas le problème! Ma réponse est pareillement cassée. Il serait nécessaire de spécifiquement exclure END au milieu du match, ce qui est très difficile dans les expressions rationnelles, si cela est possible.
Bob
@Bob Exclure END peut être fait en utilisant un aspect négatif, je suppose, ou en utilisant ([^E]|E[^N]|EN[^D]|END[^\b]) (euh ... non testé). (Oh et j'ai enlevé le match paresseux à nouveau, si ça ne marche pas quand même)
Johannes H.
Oui, je suis juste allé avec une solution scriptée complète. Cela fonctionne réellement dans tous les cas. Une expression régulière serait intéressante, mais probablement pas tout à fait pratique. Oh hey, (?<=START([^E]|E[^N]|EN[^D])*?)-(?=([^E]|E[^N]|EN[^D])*?END) travaux. Kinda.
Bob