2010-08-31 12 views
0

Je veux ajouter un attribut à chaque balise dans mon xml, qui incrémente utilisant awk, sed, shell perl ou simple cmdAjout d'un attribut de valeur incrémenter à chaque balise dans le script en utilisant xml

Pour exemple:

<tag1 key="123"> 
    <tag2 abc="xf d"/> 
    <tag3 def="d2 32"> 
    </tag3> 
</tag1> 

J'attends la sortie suivante

<tag1 key="123" order="1"> 
    <tag2 abc="xf d" order="2"/> 
    <tag3 def="d2 32" order="3"> 
    </tag3> 
</tag1> 

Si possible, je ne suis pas à la recherche sur les dépendances (Brindille, LibXML), la manipulation de chaînes pure.

+2

@aeh: Je suis curieux de savoir pourquoi vous insistez pour utiliser la manipulation de chaînes. Certains de ces modules XML font partie de la distribution Perl principale. Si vous pensez qu'il est «plus difficile» d'utiliser un module, détrompez-vous. – Zaid

+1

Zaid, autant que je peux voir, il n'y a pas de modules d'analyse XML dans la distribution Perl de base. Je crois cependant qu'ActivePerl en ajoute. –

+0

@Zaid: Je comprends ce que je demande n'est pas trivial. Je suis juste curieux? Ce que mes exigences énoncent ci-dessus est structurel, rien à voir avec XML dans son individualité. ai-je vraiment besoin d'un analyseur XML? J'ai aussi quelques limitations sur l'ajout de dépendances. – aeh

Répondre

-1

Normalement, vous devriez utiliser un analyseur approprié pour traiter xml. Mais dans awk:

awk 'match($0, /<[^\/>]+/) { \ 
    $0 = substr($0, 1, RSTART+RLENGTH-1) " order=\"" ++i "\"" \ 
      substr($0, RSTART+RLENGTH) \ 
    }; 1' 

Je recherche un tag d'ouverture (sans la partie > ou />) sur chaque ligne. Si trouvé, mettez la chaîne order="i" après, tout en incrémentant i. Le seul 1 sur la dernière ligne exécute toujours l'action par défaut de awk: { print $0 }. J'ai mis à jour l'expression régulière pour travailler sur votre entrée révisée. Il échoue dès que vous avez plusieurs balises d'ouverture sur une seule ligne, etc

+0

propriété clé n'est pas obligatoire, je vais modifier l'exemple en conséquence – aeh

+0

Pourquoi les downvotes? Je sais (et mentionne) que regex ne remplace pas l'analyse XML correcte, mais si c'est ce que le PO veut/a besoin. – schot

+3

@schot: Les downvotes sont dues aux sentiments forts que la communauté SO a contre l'utilisation d'expressions régulières pour manipuler des documents XML/HTML. Cela n'a probablement rien à voir avec votre réponse. – Zaid

4

J'aime Perl's XML::Twig pour ce genre de chose. Vous devrez l'ajuster pour tout ce que vous faites afin que vous visitiez tous les éléments que vous voulez affecter. Pour gérer les parents avant que les enfants, une file d'attente est probablement ce que vous voulez:

use XML::Twig; 

my $xml = <<'XML'; 
<tag1 key="123"> 
    <tag2 key="1234"/> 
    <tag3 key="12345"> 
    </tag3> 
</tag1> 
XML 

my $twig = XML::Twig->new(
    pretty_print => 'indented', 
    ); 
$twig->parse($xml); 
my @queue = ($twig->root); 

my $n = 1; 
while(my $elem = shift @queue) { 
    next unless $elem->tag =~ /\Atag[123]\z/; 
    $elem->set_att(order => $n++); 
    push @queue, $elem->children(qr/\Atag/); 
    } 

$twig->print; 

La sortie de ce script est:

<tag1 key="123" order="1"> 
    <tag2 key="1234" order="2"/> 
    <tag3 key="12345" order="3"></tag3> 
</tag1> 
+0

Si possible, je ne cherche pas de dépendances (Twig), la manipulation pure chaîne. De plus, la chaîne "123" peut ne pas toujours être présente. éditera l'exemple – aeh

+3

@aeh, en essayant de manipuler XML sans utiliser un analyseur XML approprié est toujours risqué. Vous pouvez vous en sortir si votre XML est assez "normal" et que le changement que vous faites est simple, mais il n'y a aucune garantie. En outre, '[123]' n'a rien à voir avec 'key =" 123 "'. C'est une classe de personnage; cette ligne recherche des étiquettes nommées tag1 ou tag2 ou tag3. – cjm

+0

@cjm: désolé pour une mauvaise interprétation (je ne sais pas perl) mais même les balises n'ont pas besoin d'être tag1..2..3. Je comprends sans utiliser un analyseur approprié, il ne serait pas approprié de manipuler XML. J'ai une exigence simple et j'essaie juste si quelqu'un a une solution propre sans aucune dépendance à l'analyseur. – aeh

2

Il est assez simple avec XML :: LibXML et une goutte de XPath.

#!/usr/bin/perl 

use strict; 
use warnings; 

use XML::LibXML; 

my $counter = 1; 

my $xp = XML::LibXML->new->parse_file('test.xml'); 

foreach($xp->findnodes('//*')) { # '//*' returns all nodes 
    $_->setAttribute('order', $counter++); 
} 

print $xp->toString; 
+0

Si possible aucune dépendance (LibXML). Désolé de ne pas mentionner plus tôt. – aeh

+2

L'analyse XML sans utiliser d'analyseur XML est une très mauvaise idée. Je vous recommande fortement de supprimer toute restriction vous empêchant d'utiliser les modules CPAN. Sans CPAN, vous utilisez une version infirme de Perl. –

+0

@davorg: Thanx Je comprends que manipuler un XML sans un analyseur XML n'est pas approprié. Mais mon exigence est simple liée à la structure et ne dépend pas d'une sémantique stricte. Je suis en train d'essayer. Aussi je ne peux pas avoir d'autres dépendances. – aeh

Questions connexes