regex
  • perl
  • 2011-04-22 3 views 1 likes 
    1

    Je connais le? L'opérateur permet le mode "non gourmand", mais je rencontre un problème, je n'arrive pas à me déplacer. Considérons une chaîne comme ceci:Problèmes de rapprochement les plus courts

    my $str = '<a>sdkhfdfojABCasjklhd</a><a>klashsdjDEFasl;jjf</a><a>askldhsfGHIasfklhss</a>'; 
    

    où il y a des balises d'ouverture et de fermeture <a> et </a>, il y a des touches ABC, DEF et GHI mais sont entourés par un autre texte aléatoire. Je veux remplacer le <a>klashsdjDEFasl;jjf</a> par <b>TEST</b> par exemple. Cependant, si j'ai quelque chose comme ça.

    $str =~ s/<a>.*?DEF.*?<\/a>/<b>TEST><\/b>/; 
    

    Même avec les opérateurs non avides * ?, cela ne fait pas ce que je veux. Je sais pourquoi il ne le fait pas, parce que la première <a> correspond à la première occurrence de la chaîne, et correspond à tout le chemin jusqu'à DEF, puis correspond à la plus proche fermeture </a>. Ce que je veux cependant est un moyen de faire correspondre l'ouverture la plus proche <a> et la fermeture </a> à "DEF" si. Donc, actuellement, je reçois cela comme le résultat:

    <a>TEST</b><a>askldhsfGHIasfklhss</a> 
    

    Où que je cherche quelque chose pour obtenir ce résultat:

    <a>sdkhfdfojABCasjklhd</a><b>TEST</b><a>askldhsfGHIasfklhss</a> 
    

    D'ailleurs, je ne suis pas en train d'analyser HTML ici, je sachez qu'il existe des modules pour le faire, je demande simplement comment cela pourrait être fait.

    Merci, Eric Seifert

    Répondre

    6
    $str =~ s/(.*)<a>.*?DEF.*?<\/a>/$1<b>TEST><\/b>/; 
    

    Le problème est que même avec l'appariement non gourmand, Perl essaie toujours de trouver le match qui commence au point de plus à gauche possible dans la chaîne. Depuis .*? peut correspondre <a> ou </a>, cela signifie qu'il trouvera toujours la première <a> sur la ligne.

    Ajout d'un gourmand (.*) au début, il provoque de trouver la dernière possible correspondant <a> sur la ligne (parce que .* première saisit toute la ligne, et puis revient en arrière jusqu'à ce qu'une correspondance est trouvée).

    Une mise en garde: Parce qu'il trouve le match premier extrême droite, vous ne pouvez pas utiliser cette technique avec le modificateur /g. Toutes les correspondances supplémentaires se trouvent à l'intérieur de $1, et /g reprend la recherche où la correspondance précédente s'est terminée, donc il ne les trouvera pas. Au lieu de cela, vous auriez à utiliser une boucle comme:

    1 while $str =~ s/(.*)<a>.*?DEF.*?<\/a>/$1<b>TEST><\/b>/; 
    
    +0

    Merci, c'est exactement ce que je cherchais. –

    2

    au lieu d'un point qui dit: « correspond à tout caractère », utilisez ce que vous avez vraiment besoin qui dit: « correspond à aucun omble chevalier qui ne le début de </a> ".Cela se traduit par quelque chose comme ceci:

    $str =~ s/<a>(?:(?!<\/a>).)*DEF(?:(?!<\/a>).)*<\/a>/<b>TEST><\/b>/; 
    
    +0

    @ysth: Merci pour les évasions ... – ridgerunner

    0
    #!/usr/bin/perl 
    use warnings; 
    use strict; 
    
    my $str = '<a>sdkhfdfojABCasjklhd</a><a>klashsdjDEFasl;jjf</a><a>askldhsfGHIasfklhss</a>'; 
    
    my @collections = $str =~ /<a>.*?(ABC|DEF|GHI).*?<\/a>/g; 
    
    print join ", ", @collections; 
    
    +0

    Tout ce que vous avez fait était de changer l'expression régulière afin qu'elle corresponde à chaque occurrence de ' ...' dans la chaîne. Cela ne résout pas le problème original, qui est de correspondre à un seul de ces groupes. – cjm

    +0

    Ah, vous avez raison. @cjm – SymKat

    0
    s{ 
        <a> 
        (?: (?! </a>) .)* 
        DEF 
        (?: (?! </a>) .)* 
        </a> 
    }{<b>TEST</b>}x; 
    

    Fondamentalement,

    (?: (?! PAT) .) 
    

    est l'équivalent de

    [^CHARS] 
    

    pour des motifs regex au lieu des caractères.

    Questions connexes