perlfaq6.pod

来自「ARM上的如果你对底层感兴趣」· POD 代码 · 共 627 行 · 第 1/2 页

POD
627
字号
=head2 What does it mean that regexps are greedy?  How can I get around it?

Most people mean that greedy regexps match as much as they can.
Technically speaking, it's actually the quantifiers (C<?>, C<*>, C<+>,
C<{}>) that are greedy rather than the whole pattern; Perl prefers local
greed and immediate gratification to overall greed.  To get non-greedy
versions of the same quantifiers, use (C<??>, C<*?>, C<+?>, C<{}?>).

An example:

        $s1 = $s2 = "I am very very cold";
        $s1 =~ s/ve.*y //;      # I am cold
        $s2 =~ s/ve.*?y //;     # I am very cold

Notice how the second substitution stopped matching as soon as it
encountered "y ".  The C<*?> quantifier effectively tells the regular
expression engine to find a match as quickly as possible and pass
control on to whatever is next in line, like you would if you were
playing hot potato.

=head2  How do I process each word on each line?

Use the split function:

    while (<>) {
	foreach $word ( split ) { 
	    # do something with $word here
	} 
    }

Note that this isn't really a word in the English sense; it's just
chunks of consecutive non-whitespace characters.

To work with only alphanumeric sequences, you might consider

    while (<>) {
	foreach $word (m/(\w+)/g) {
	    # do something with $word here
	}
    }

=head2 How can I print out a word-frequency or line-frequency summary?

To do this, you have to parse out each word in the input stream.  We'll
pretend that by word you mean chunk of alphabetics, hyphens, or
apostrophes, rather than the non-whitespace chunk idea of a word given
in the previous question:

    while (<>) {
	while ( /(\b[^\W_\d][\w'-]+\b)/g ) {   # misses "`sheep'"
	    $seen{$1}++;
	}
    }
    while ( ($word, $count) = each %seen ) {
	print "$count $word\n";
    }

If you wanted to do the same thing for lines, you wouldn't need a
regular expression:

    while (<>) { 
	$seen{$_}++;
    }
    while ( ($line, $count) = each %seen ) {
	print "$count $line";
    }

If you want these output in a sorted order, see the section on Hashes.

=head2 How can I do approximate matching?

See the module String::Approx available from CPAN.

=head2 How do I efficiently match many regular expressions at once?

The following is super-inefficient:

    while (<FH>) {
        foreach $pat (@patterns) {
            if ( /$pat/ ) {
                # do something
            }
        }
    }

Instead, you either need to use one of the experimental Regexp extension
modules from CPAN (which might well be overkill for your purposes),
or else put together something like this, inspired from a routine
in Jeffrey Friedl's book:

    sub _bm_build {
        my $condition = shift;
        my @regexp = @_;  # this MUST not be local(); need my()
        my $expr = join $condition => map { "m/\$regexp[$_]/o" } (0..$#regexp);
        my $match_func = eval "sub { $expr }";
        die if $@;  # propagate $@; this shouldn't happen!
        return $match_func;
    }

    sub bm_and { _bm_build('&&', @_) }
    sub bm_or  { _bm_build('||', @_) }

    $f1 = bm_and qw{
            xterm
            (?i)window
    };

    $f2 = bm_or qw{
            \b[Ff]ree\b
            \bBSD\B
            (?i)sys(tem)?\s*[V5]\b
    };

    # feed me /etc/termcap, prolly
    while ( <> ) {
        print "1: $_" if &$f1;
        print "2: $_" if &$f2;
    }

=head2 Why don't word-boundary searches with C<\b> work for me?

Two common misconceptions are that C<\b> is a synonym for C<\s+>, and
that it's the edge between whitespace characters and non-whitespace
characters.  Neither is correct.  C<\b> is the place between a C<\w>
character and a C<\W> character (that is, C<\b> is the edge of a
"word").  It's a zero-width assertion, just like C<^>, C<$>, and all
the other anchors, so it doesn't consume any characters.  L<perlre>
describes the behaviour of all the regexp metacharacters.

Here are examples of the incorrect application of C<\b>, with fixes:

    "two words" =~ /(\w+)\b(\w+)/;	    # WRONG
    "two words" =~ /(\w+)\s+(\w+)/;	    # right

    " =matchless= text" =~ /\b=(\w+)=\b/;   # WRONG
    " =matchless= text" =~ /=(\w+)=/;       # right

Although they may not do what you thought they did, C<\b> and C<\B>
can still be quite useful.  For an example of the correct use of
C<\b>, see the example of matching duplicate words over multiple
lines.

An example of using C<\B> is the pattern C<\Bis\B>.  This will find
occurrences of "is" on the insides of words only, as in "thistle", but
not "this" or "island".

=head2 Why does using $&, $`, or $' slow my program down?

Because once Perl sees that you need one of these variables anywhere
in the program, it has to provide them on each and every pattern
match.  The same mechanism that handles these provides for the use of
$1, $2, etc., so you pay the same price for each regexp that contains
capturing parentheses. But if you never use $&, etc., in your script,
then regexps I<without> capturing parentheses won't be penalized. So
avoid $&, $', and $` if you can, but if you can't (and some algorithms
really appreciate them), once you've used them once, use them at will,
because you've already paid the price.

=head2 What good is C<\G> in a regular expression?

The notation C<\G> is used in a match or substitution in conjunction the
C</g> modifier (and ignored if there's no C</g>) to anchor the regular
expression to the point just past where the last match occurred, i.e. the
pos() point.

For example, suppose you had a line of text quoted in standard mail
and Usenet notation, (that is, with leading C<E<gt>> characters), and
you want change each leading C<E<gt>> into a corresponding C<:>.  You
could do so in this way:

     s/^(>+)/':' x length($1)/gem;

Or, using C<\G>, the much simpler (and faster):

    s/\G>/:/g;

A more sophisticated use might involve a tokenizer.  The following
lex-like example is courtesy of Jeffrey Friedl.  It did not work in
5.003 due to bugs in that release, but does work in 5.004 or better.
(Note the use of C</c>, which prevents a failed match with C</g> from
resetting the search position back to the beginning of the string.)

    while (<>) {
      chomp;
      PARSER: {
           m/ \G( \d+\b    )/gcx    && do { print "number: $1\n";  redo; };
           m/ \G( \w+      )/gcx    && do { print "word:   $1\n";  redo; };
           m/ \G( \s+      )/gcx    && do { print "space:  $1\n";  redo; };
           m/ \G( [^\w\d]+ )/gcx    && do { print "other:  $1\n";  redo; };
      }
    }

Of course, that could have been written as

    while (<>) {
      chomp;
      PARSER: {
	   if ( /\G( \d+\b    )/gcx  {
		print "number: $1\n";
		redo PARSER;
	   }
	   if ( /\G( \w+      )/gcx  {
		print "word: $1\n";
		redo PARSER;
	   }
	   if ( /\G( \s+      )/gcx  {
		print "space: $1\n";
		redo PARSER;
	   }
	   if ( /\G( [^\w\d]+ )/gcx  {
		print "other: $1\n";
		redo PARSER;
	   }
      }
    }

But then you lose the vertical alignment of the regular expressions.

=head2 Are Perl regexps DFAs or NFAs?  Are they POSIX compliant?

While it's true that Perl's regular expressions resemble the DFAs
(deterministic finite automata) of the egrep(1) program, they are in
fact implemented as NFAs (non-deterministic finite automata) to allow
backtracking and backreferencing.  And they aren't POSIX-style either,
because those guarantee worst-case behavior for all cases.  (It seems
that some people prefer guarantees of consistency, even when what's
guaranteed is slowness.)  See the book "Mastering Regular Expressions"
(from O'Reilly) by Jeffrey Friedl for all the details you could ever
hope to know on these matters (a full citation appears in
L<perlfaq2>).

=head2 What's wrong with using grep or map in a void context?

Both grep and map build a return list, regardless of their context.
This means you're making Perl go to the trouble of building up a
return list that you then just ignore.  That's no way to treat a
programming language, you insensitive scoundrel!

=head2 How can I match strings with multibyte characters?

This is hard, and there's no good way.  Perl does not directly support
wide characters.  It pretends that a byte and a character are
synonymous.  The following set of approaches was offered by Jeffrey
Friedl, whose article in issue #5 of The Perl Journal talks about this
very matter.

Let's suppose you have some weird Martian encoding where pairs of
ASCII uppercase letters encode single Martian letters (i.e. the two
bytes "CV" make a single Martian letter, as do the two bytes "SG",
"VS", "XX", etc.). Other bytes represent single characters, just like
ASCII.

So, the string of Martian "I am CVSGXX!" uses 12 bytes to encode the
nine characters 'I', ' ', 'a', 'm', ' ', 'CV', 'SG', 'XX', '!'.

Now, say you want to search for the single character C</GX/>. Perl
doesn't know about Martian, so it'll find the two bytes "GX" in the "I
am CVSGXX!"  string, even though that character isn't there: it just
looks like it is because "SG" is next to "XX", but there's no real
"GX".  This is a big problem.

Here are a few ways, all painful, to deal with it:

   $martian =~ s/([A-Z][A-Z])/ $1 /g; # Make sure adjacent ``martian'' bytes
                                      # are no longer adjacent.
   print "found GX!\n" if $martian =~ /GX/;

Or like this:

   @chars = $martian =~ m/([A-Z][A-Z]|[^A-Z])/g;
   # above is conceptually similar to:     @chars = $text =~ m/(.)/g;
   #
   foreach $char (@chars) {
       print "found GX!\n", last if $char eq 'GX';
   }

Or like this:

   while ($martian =~ m/\G([A-Z][A-Z]|.)/gs) {  # \G probably unneeded
       print "found GX!\n", last if $1 eq 'GX';
   }

Or like this:

   die "sorry, Perl doesn't (yet) have Martian support )-:\n";

In addition, a sample program which converts half-width to full-width
katakana (in Shift-JIS or EUC encoding) is available from CPAN as

=for Tom make it so

There are many double- (and multi-) byte encodings commonly used these
days.  Some versions of these have 1-, 2-, 3-, and 4-byte characters,
all mixed.

=head1 AUTHOR AND COPYRIGHT

Copyright (c) 1997, 1998 Tom Christiansen and Nathan Torkington.
All rights reserved.

When included as part of the Standard Version of Perl, or as part of
its complete documentation whether printed or otherwise, this work
may be distributed only under the terms of Perl's Artistic License.
Any distribution of this file or derivatives thereof I<outside>
of that package require that special arrangements be made with
copyright holder.

Irrespective of its distribution, all code examples in this file
are hereby placed into the public domain.  You are permitted and
encouraged to use this code in your own programs for fun
or for profit as you see fit.  A simple comment in the code giving
credit would be courteous but is not required.

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?