2010-08-01 22 views
2

Mon but est de remplacer toutes les occurrences d'une balise arrière + à l'intérieur d'une balise. Permet de prendre la ligne à remplacer ressemble à ceci:Perl: Substitution globale dans une chaîne délimitée par des balises

<h> aa- aa- </h> <h> ba- ba- </h> 

et devrait ressembler à la suite comme

<h> aa+ aa+ </h> <h> ba+ ba+ </h> 

D'abord, j'ai essayé cette expression:

s/<h>(.*?)-(.*?)<\/h>/<h>$1+$2<\/h>/g; 

qui a produit cette sortie:

<h> aa+ aa- </h> <h> ba+ ba- </h> 

Le g op tion conduit à plus d'une substitution par ligne, mais seulement pour la première instance par parenthèse (et seulement si les deux parenthèses contiennent le point d'interrogation).

Pour affiner le problème, j'ai ensuite essayé d'obtenir une substitution sans tenir compte des étiquettes. L'expression

s/(.*?)-(.*?)/$1+$2/g; 

conduit en effet au résultat souhaité

<h> aa+ aa+ </h> <h> ba+ ba+ </h> 

Cela remplacera en dehors des supports d'étiquettes ainsi, bien sûr.

Alors, quel est le problème avec ma première expression, et comment puis-je atteindre mon objectif de substitution complète entre les parenthèses?

+0

S'il existe des limites sur le jeu de caractères, tout ce qui précède le '-' vous pourriez exploiter cela. – adamse

+1

Vous devez utiliser un analyseur à part entière, ce que les expressions régulières ne sont pas. – Ether

Répondre

0

Voici une façon de le faire: diviser la chaîne en bits étiquetés et bits non étiquetés, et effectuer le remplacement uniquement sur les bits marqués.

$_ = join("", map { if(/^<h>/) { # if it's a tagged bit... 
         s/-($|\s|<)/+$1/g; # replace all trailing '-'s 
        } 
        $_} 
        split m!(<h>.*?</h>)!) # split into tagged and non-tagged bits 
1

Puisque vous analyse XML avec des expressions régulières (pas une bonne idée dans le cas général), je suppose que vous êtes prêt à faire quelques hypothèses au sujet de votre entrée. Si oui, la substitution suivante pourrait être assez bonne. Il remplace les signes moins par des signes plus, à condition que le signe moins soit: (a) à la limite d'un mot, et (b) suivi d'un texte optionnel de non angle gauche et d'une étiquette de fermeture. Pas besoin de s'inquiéter de l'étiquette de début si nous pouvons supposer un document valide. La deuxième condition est appliquée avec une assertion anticipée afin que l'expression régulière ne consomme pas la chaîne, vous permettant de remplacer tous ces signes moins.

s/ \b- (?= [^<]* <\/h>) /+/xg; 

Une autre option consiste à exécuter votre regex jusqu'à ce qu'il ne remplace rien. Dans un contexte scalaire, une substitution globale renvoie le nombre de remplacements effectués, ce qui peut servir de test pour savoir quand arrêter le traitement d'une ligne:

my $n = 1; 
$n = s/YOUR_REGEX/YOUR_REPLACE/g while $n;