Wie kann ich behandeln effizient mehrere Such Perl / Ersetzen-Operationen auf derselben Saite?

StackOverflow https://stackoverflow.com/questions/843542

  •  20-08-2019
  •  | 
  •  

Frage

Also meine Perl-Skript im Grunde nimmt einen String und dann versucht es, indem Sie mehrere Suchbegriffe, um aufzuräumen und ersetzt auf sie, etwa so:

$text =~ s/<[^>]+>/ /g;
$text =~ s/\s+/ /g;
$text =~ s/[\(\{\[]\d+[\(\{\[]/ /g;
$text =~ s/\s+[<>]+\s+/\. /g;
$text =~ s/\s+/ /g;
$text =~ s/\.*\s*[\*|\#]+\s*([A-Z\"])/\. $1/g; # replace . **** Begin or . #### Begin or ) *The 
$text =~ s/\.\s*\([^\)]*\) ([A-Z])/\. $1/g; # . (blah blah) S... => . S...

Wie Sie sehen, ich bin der Umgang mit fiesen html und muß es in der Unterwerfung schlagen.

Ich hoffe, es ist ein einfacher, ästhetisch ansprechende Art und Weise, dies zu tun. Ich habe etwa 50 Zeilen, die nur so aussehen, was oben ist.

Ich habe durch die Verwendung eines Hash einer Version dieses Problem gelöst, wo der Schlüssel der Kommentar, und der Hash ist der reg Ausdruck, etwa so:

%rxcheck = (
        'time of day'=>'\d+:\d+', 
    'starts with capital letters then a capital word'=>'^([A-Z]+\s)+[A-Z][a-z]',
    'ends with a single capital letter'=>'\b[A-Z]\.'
}

Und das ist, wie ich es verwenden:

 foreach my $key (keys %rxcheck) {
if($snippet =~ /$rxcheck{ $key }/g){ blah blah  }
 }

Das Problem kommt, wenn ich meine Hand an einem Hash versuchen, wo der Schlüssel ist der Ausdruck und es zeigt auf, was ich will, es ersetzen mit ... und es gibt ein $ 1 oder $ 2 drin.

%rxcheck2 = (
        '(\w) \"'=>'$1\"'
}

Die oben ist, dies zu tun:

$snippet =~ s/(\w) \"/$1\"/g;

Aber ich kann nicht scheinen, den Teil „$ 1“ zu gelangen in den regulären Ausdruck wörtlich (ich glaube, das ist das richtige Wort ... es scheint, die 1 $ selbst interpretiert wird, obwohl ich 'Marken verwendet.) So ergibt sich:

if($snippet =~ /$key/$rxcheck2{ $key }/g){  }

Und das nicht funktioniert.

So 2 Fragen:

Ganz einfach:? Wie gehe ich mit einer großen Anzahl von regex ist in einer leicht bearbeitbaren Art und Weise, so kann ich sie ohne gerade geschnitten ändern und hinzufügen, und die Zeile vor dem Einfügen

Härtere: Wie kann ich mit ihnen umgehen einen Hash (oder Array, wenn ich, sagen wir, mehrere Stücke, die ich aufnehmen möchten, wie 1) Teil zu suchen, 2) Ersatz 3) Kommentar, 4) global / Groß- und Kleinschreibung Modifikatoren ), wenn das in der Tat der einfachste Weg, dies zu tun?

Danke für Ihre Hilfe -

War es hilfreich?

Lösung

Problem # 1

Da es nicht erscheint viel Struktur, die durch die einzelnen Regexes geteilt zu sein, dann ist es nicht wirklich ein einfacher oder klarer Weise als nur die Befehle Auflistung, wie Sie getan haben. Ein weit verbreiteter Ansatz Wiederholung im Code wie folgt zu abnehmend ist $text in $_ zu bewegen, so dass anstatt sagen zu haben:

$text =~ s/foo/bar/g;

Sie können nur sagen:

s/foo/bar/g;

Ein gemeinsames Idiom, dies zu tun ist eine degenerierte for() Schleife als topicalizer zu verwenden:

for($text)
{
  s/foo/bar/g;
  s/qux/meh/g;
  ...
}

Der Umfang dieser Blöcke wird jeden bereits existierenden Wert von $_ erhalten, so gibt es keine Notwendigkeit local explizit $_ize.

An diesem Punkt haben Sie fast alle Nicht-Text Charakter beseitigt - wie viel kürzer es bekommen kann, auch in der Theorie

Es sei denn, was Sie wirklich wollen (wie Ihr Problem # 2 schon sagt) verbessert Modularität , zum Beispiel die Fähigkeit, iterieren, berichten über, zählen usw. alle reguläre Ausdrücke.

Problem # 2

Sie können die qr// Syntax verwenden, um den „Suche“ Teil der Substitution zu zitieren:

my $search = qr/(<[^>]+>)/;
$str =~ s/$search/foo,$1,bar/;

Allerdings weiß ich nicht, von einer Art und Weise angemessen den „Ersatz“ Teil zitieren. Ich hatte gehofft, dass qr// auch für das funktionieren würde, aber es funktioniert nicht. Es gibt zwei Alternativen eine Überlegung wert:

1. Verwenden Sie eval() in Ihrer foreach Schleife. Dies würde ermöglichen es Ihnen, Ihre aktuelle %rxcheck2 Hash zu halten. Nachteil:. Sie stets darauf bedacht sein sollte, um die Sicherheit mit String eval()s

2. Verwenden Sie eine Reihe von anonymen Subroutinen:

my @replacements = (
    sub { $_[0] =~ s/<[^>]+>/ /g; },
    sub { $_[0] =~ s/\s+/ /g; },
    sub { $_[0] =~ s/[\(\{\[]\d+[\(\{\[]/ /g; },
    sub { $_[0] =~ s/\s+[<>]+\s+/\. /g },
    sub { $_[0] =~ s/\s+/ /g; },
    sub { $_[0] =~ s/\.*\s*[\*|\#]+\s*([A-Z\"])/\. $1/g; },
    sub { $_[0] =~ s/\.\s*\([^\)]*\) ([A-Z])/\. $1/g; }
);

# Assume your data is in $_
foreach my $repl (@replacements) {
    &{$repl}($_);
}

Du könntest natürlich einen Hash stattdessen mit etwas nützlichen Schlüssel als Hash verwenden, und / oder könnten Sie mehrwertige Elemente (oder Hash-Wert) einschließlich Kommentare oder andere Informationen verwendet werden.

Andere Tipps

Sie sagen, Sie mit HTML zu tun hat. Sie erkennen jetzt, dass dies so ziemlich eine verlorene Schlacht mit flüchtig und zerbrechlich Lösungen ist.

Ein richtiger HTML-Parser wäre Ihr Leben leichter machen. HTML :: Parser schwer zu bedienen, aber es gibt auch andere sehr nützliche Bibliotheken auf CPAN was kann ich empfehlen, wenn Sie angeben können, was Sie versuchen, tun, anstatt wie .

Hashes sind nicht gut, weil sie ungeordnet sind. Ich finde, ein Array von Arrays, deren zweiten Array enthält eine kompilierte Regex und eine Zeichenfolge an eval (es ist eigentlich ein Doppel eval) am besten funktioniert:

#!/usr/bin/perl

use strict;
use warnings;

my @replace = (
    [ qr/(bar)/ => '"<$1>"' ],
    [ qr/foo/   => '"bar"'  ],
);

my $s = "foo bar baz foo bar baz";

for my $replace (@replace) {
    $s =~ s/$replace->[0]/$replace->[1]/gee;
}

print "$s\n";

Ich denke, j_random_hacker die zweite Lösung Mine weit überlegen ist. Einzelne Subroutinen geben Ihnen die Flexibilität und sind eine Größenordnung schneller als meine /ee Lösung:

bar <bar> baz bar <bar> baz
bar <bar> baz bar <bar> baz
         Rate refs subs
refs  10288/s   -- -91%
subs 111348/s 982%   --

Hier ist der Code, der diese Zahlen erzeugt:

#!/usr/bin/perl

use strict;
use warnings;

use Benchmark;

my @subs = (
    sub { $_[0] =~ s/(bar)/<$1>/g },
    sub { $_[0] =~ s/foo/bar/g },
);

my @refs = (
    [ qr/(bar)/ => '"<$1>"' ],
    [ qr/foo/   => '"bar"'  ],
);

my %subs = (
    subs => sub {
        my $s = "foo bar baz foo bar baz";
        for my $sub (@subs) {
            $sub->($s);
        }
        return $s;
    },
    refs => sub {
        my $s = "foo bar baz foo bar baz";
        for my $ref (@refs) {
            $s =~ s/$ref->[0]/$ref->[1]/gee;
        }
        return $s;
    }
);

for my $sub (keys %subs) {
    print $subs{$sub}(), "\n";
}

Benchmark::cmpthese -1, \%subs;
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top