2009-08-01 5 views
3

J'écris un obfuscator HTML, et j'ai un hash corrélant des noms faciles à utiliser (des ids et des classes) aux noms obfuscated (comme a, b, c, etc.). J'éprouve des difficultés à venir avec un regexp pour accomplir quelque chose comme remplaçantComment remplacer plusieurs mots, chacun haché par un mot alternatif, dans un attribut HTML utilisant Perl regex?

<div class="left tall"> 

avec

<div class="a b"> 

Si les étiquettes ne pouvaient accepter une classe, l'expression rationnelle serait tout simplement quelque chose comme

s/(class|id)="(.*?)"/$1="$hash{$2}"/ 

Comment dois-je corriger cela pour prendre en compte plusieurs noms de classe entre guillemets? De préférence, la solution devrait être compatible avec Perl.

+0

Certains pourraient dire que 'left' et' tall' sont aussi obfusqués que 'a' et' b'. –

Répondre

-1

Je crois que je ferais ceci:

s/ 
    (class|id)="([^"]+)" 
/ 
    $1 . '="' . (
     join ' ', map { $hash{$_} } split m!\s+!, $2 
    ) . '"' 
/ex; 
+0

Que faites-vous lorsque le texte du HTML contient class = "foo"? Les expressions régulières/substitutions simples ne se mélangent pas bien avec les données structurées récursivement. –

6

Vous ne devriez pas utiliser une expression rationnelle pour ce en premier lieu. Vous essayez d'en faire trop avec une regex (voir Can you provide some examples of why it is hard to parse XML and HTML with a regex? pour pourquoi). Ce dont vous avez besoin est un analyseur HTML. Voir Can you provide an example of parsing HTML with your favorite parser? pour des exemples en utilisant une variété de parseurs.

Regardez HTML::Parser. Voici une implémentation probablement incomplète:

#!/usr/bin/perl 

use strict; 
use warnings; 

use HTML::Parser; 

{ 
    my %map = (
     foo => "f", 
     bar => "b", 
    ); 

    sub start { 
     my ($tag, $attr) = @_; 
     my $attr_string = ''; 
     for my $key (keys %$attr) { 
      if ($key eq 'class') { 
       my @classes = split " ", $attr->{$key}; 
       #FIXME: this should be using //, but 
       #it is only availble starting in 5.10 
       #so I am using || which will do the 
       #wrong thing if the class is 0, so 
       #don't use a class of 0 in %map , m'kay 
       $attr->{$key} = join " ", 
        map { $map{$_} || $_ } @classes; 
      } 
      $attr_string .= qq/ $key="$attr->{$key}"/; 
     } 

     print "<$tag$attr_string>"; 
    } 
} 

sub text { 
    print shift; 
} 

sub end { 
    my $tag = shift; 
    print "</$tag>"; 
} 

my $p = HTML::Parser->new(
    start_h => [ \&start, "tagname,attr" ], 
    text_h => [ \&text, "dtext" ], 
    end_h => [ \&end, "tagname" ], 
); 

$p->parse_file(\*DATA); 

__DATA__ 
<html> 
    <head> 
     <title>foo</title> 
    </head> 
    <body> 
     <span class="foo">Foo!</span> <span class="bar">Bar!</span> 
     <span class="foo bar">Foo Bar!</span> 
     This should not be touched: class="foo" 
    </body> 
</html> 
Questions connexes