2008-12-17 4 views
1

J'ai des problèmes avec une expression rationnelle. Je regarde un ensemble de fichiers XML et essaye de détecter du texte dans des nœuds spécifiques qui contiennent un saut de ligne.Regex détecter un changement de ligne à l'intérieur d'un nœud XML

Voici quelques exemples de données:

<item name='GenMsgText'><text>The signature will be discarded.</text></item> 

<item name='GenMsgText'><text>The signature will be discarded.<break/> 
Do you want to continue?</text></item> 

Dans cet échantillon, je veux attraper seul le texte dans le deuxième noeud. Je suis venu avec la solution ci-dessous qui utilise une deuxième expression rationnelle, mais j'aimerais savoir si je peux faire la même chose en utilisant un seul.

if ($content =~m{<item name='GenMsgText'>(<textlist>)?<text>(.*?)</text>}si) 
    { 
    $t = $2; 
    if ($t =~m {\n}i) 
    { 
    print G $t."\n\n"; 
    } 
} 

C'est un outil one-shot qui est pas destiné à être réutilisé, donc je voudrais éviter d'avoir à écrire de code d'analyse syntaxique qui est plus que quelques lignes. D'ailleurs, le code ci-dessus fonctionne déjà, j'ai posé la question pour des connaissances personnelles plus que pour un usage réel.

+0

@annakata: En espérant le badge Taxonomist? :) Même si vous êtes le seul à utiliser cette balise, vous l'aurez très bientôt, je suppose ... – Tomalak

Répondre

0

Je ne suis pas sûr, mais pense que cela devrait fonctionner:

<item name='GenMsgText'>(<textlist>)?<text>(.*\n.*)</text> 
+0

Non, cela attire beaucoup plus que ce dont j'ai besoin. – Antoine

3

je devrais envisager d'utiliser un certain analyseur SAX pour cela. Regex est trop fragile pour gérer les entrées XML.

+0

Ce n'est pas parce que regex serait fragile, c'est plus parce qu'il ne peut pas analyser les structures imbriquées de manière sensée. – Tomalak

5

Regex n'est pas le bon outil pour cette tâche, il ne peut tout simplement pas gérer très bien les structures imbriquées. Si vous avez une API DOM votre disposition, ce XPath trouverait les nœuds à droite:

Si vous cherchez <break/> éléments, comme par exemple suggère:

//item[@name='GenMsgText']/text[break] 

Pour les sauts de ligne « vrais », étant CR (0xD) ou LF (0xA):

//item[@name='GenMsgText']/text[contains(., '&#xD;') or contains(., '&#xA;')] 
0

Le problème est que vos s en mode .*? peut correspondre à crochets, ainsi que les nouvelles lignes. Si la regex commence à correspondre à un élément qui ne peut pas correspondre, rien ne l'empêche de poursuivre la tentative de correspondance dans l'élément suivant. Si vous savez qu'il n'y aura jamais entre crochets dans le texte, vous pouvez limiter le match à un seul élément comme celui-ci:

<item name='GenMsgText'><text>([^<>\n]*\n[^<>]*)</text></item> 

EDIT: Il est important de noter que les expressions régulières offertes par Max et Kibbee devrait pas être appliqué en mode s (/ s, single-line, DOTALL ...). C'est ce qui les empêche de correspondre au-delà de la fin de l'élément "item": pour atteindre le suivant, ils doivent faire correspondre les séparateurs de lignes entre les éléments. Mais même sans le modificateur/s, les deux expressions régulières peuvent échouer s'il y a deux éléments sans sauts de ligne internes sur des lignes successives (c'est-à-dire, avec un seul saut de ligne entre eux). Par exemple, ces deux lignes seraient jumelés comme l'un:

<item name='GenMsgText'><text>foo</text></item> 
<item name='GenMsgText'><text>bar</text></item> 

D'autre part, s'il y a plus de deux lignes dans le texte? Les autres expressions régulières correspondent exactement à un saut de ligne, elles échoueraient donc. Dans mon regex, je explicitement décrire la première linefeed pour vous assurer qu'il est, mais s'il y a des plus linefeeds, ils vont aller de pair avec la deuxième classe de caractères: [^<>]*

Ce genre de chose est pourquoi je ont tendance à éviter d'utiliser .* ou .*?.

0

Dans le même sens que ce que Alan a mentionné, vous pouvez utiliser une capture paresseux pour ne capturer autant que nécessaire avant correspondant à la déclaration de texte de clôture

<item name='GenMsgText'><text>(.*?\n.*?)</text></item> 

Mais encore une fois, regex est probablement tout à fait le mauvais outil pour le travail, et vous devriez utiliser un analyseur XML approprié.

Questions connexes