=head1 NAME
-perlfaq6 - Regular Expressions ($Revision: 1.26 $, $Date: 2004/10/25 18:47:04 $)
+perlfaq6 - Regular Expressions ($Revision: 1.35 $, $Date: 2005/08/10 15:55:08 $)
=head1 DESCRIPTION
littered with answers involving regular expressions. For example,
decoding a URL and checking whether something is a number are handled
with regular expressions, but those answers are found elsewhere in
-this document (in L<perlfaq9>: ``How do I decode or create those %-encodings
-on the web'' and L<perlfaq4>: ``How do I determine whether a scalar is
-a number/whole/integer/float'', to be precise).
+this document (in L<perlfaq9>: "How do I decode or create those %-encodings
+on the web" and L<perlfaq4>: "How do I determine whether a scalar is
+a number/whole/integer/float", to be precise).
=head2 How can I hope to use regular expressions without creating illegible and unmaintainable code?
but don't get your hopes up. Until then, you can use these examples
if you really need to do this.
-Use the four argument form of sysread to continually add to
+If you have File::Stream, this is easy.
+
+ use File::Stream;
+ my $stream = File::Stream->new(
+ $filehandle,
+ separator => qr/\s*,\s*/,
+ );
+
+ print "$_\n" while <$stream>;
+
+If you don't have File::Stream, you have to do a little more work.
+
+You can use the four argument form of sysread to continually add to
a buffer. After you add to the buffer, you check if you have a
complete line (using your regular expression).
print "$count $line";
}
-If you want these output in a sorted order, see L<perlfaq4>: ``How do I
-sort a hash (optionally by value instead of key)?''.
+If you want these output in a sorted order, see L<perlfaq4>: "How do I
+sort a hash (optionally by value instead of key)?".
=head2 How can I do approximate matching?
=head2 How do I efficiently match many regular expressions at once?
-The following is extremely inefficient:
+( contributed by brian d foy )
- # slow but obvious way
- @popstates = qw(CO ON MI WI MN);
- while (defined($line = <>)) {
- for $state (@popstates) {
- if ($line =~ /\b$state\b/i) {
- print $line;
- last;
- }
- }
- }
+Avoid asking Perl to compile a regular expression every time
+you want to match it. In this example, perl must recompile
+the regular expression for every iteration of the foreach()
+loop since it has no way to know what $pattern will be.
-That's because Perl has to recompile all those patterns for each of
-the lines of the file. As of the 5.005 release, there's a much better
-approach, one which makes use of the new C<qr//> operator:
-
- # use spiffy new qr// operator, with /i flag even
- use 5.005;
- @popstates = qw(CO ON MI WI MN);
- @poppats = map { qr/\b$_\b/i } @popstates;
- while (defined($line = <>)) {
- for $patobj (@poppats) {
- print $line if $line =~ /$patobj/;
- }
- }
+ @patterns = qw( foo bar baz );
+
+ LINE: while( <> )
+ {
+ foreach $pattern ( @patterns )
+ {
+ print if /\b$pattern\b/i;
+ next LINE;
+ }
+ }
+
+The qr// operator showed up in perl 5.005. It compiles a
+regular expression, but doesn't apply it. When you use the
+pre-compiled version of the regex, perl does less work. In
+this example, I inserted a map() to turn each pattern into
+its pre-compiled form. The rest of the script is the same,
+but faster.
+
+ @patterns = map { qr/\b$_\b/i } qw( foo bar baz );
+
+ LINE: while( <> )
+ {
+ foreach $pattern ( @patterns )
+ {
+ print if /\b$pattern\b/i;
+ next LINE;
+ }
+ }
+
+In some cases, you may be able to make several patterns into
+a single regular expression. Beware of situations that require
+backtracking though.
+
+ $regex = join '|', qw( foo bar baz );
+
+ LINE: while( <> )
+ {
+ print if /\b(?:$regex)\b/i;
+ }
+
+For more details on regular expression efficiency, see Mastering
+Regular Expressions by Jeffrey Freidl. He explains how regular
+expressions engine work and why some patterns are surprisingly
+inefficient. Once you understand how perl applies regular
+expressions, you can tune them for individual situations.
=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 behavior of all the regex metacharacters.
+(contributed by brian d foy)
+
+Ensure that you know what \b really does: it's the boundary between a
+word character, \w, and something that isn't a word character. That
+thing that isn't a word character might be \W, but it can also be the
+start or end of the string.
+
+It's not (not!) the boundary between whitespace and non-whitespace,
+and it's not the stuff between words we use to create sentences.
+
+In regex speak, a word boundary (\b) is a "zero width assertion",
+meaning that it doesn't represent a character in the string, but a
+condition at a certain position.
+
+For the regular expression, /\bPerl\b/, there has to be a word
+boundary before the "P" and after the "l". As long as something other
+than a word character precedes the "P" and succeeds the "l", the
+pattern will match. These strings match /\bPerl\b/.
+
+ "Perl" # no word char before P or after l
+ "Perl " # same as previous (space is not a word char)
+ "'Perl'" # the ' char is not a word char
+ "Perl's" # no word char before P, non-word char after "l"
+
+These strings do not match /\bPerl\b/.
+
+ "Perl_" # _ is a word char!
+ "Perler" # no word char before P, but one after l
-Here are examples of the incorrect application of C<\b>, with fixes:
+You don't have to use \b to match words though. You can look for
+non-word characters surrounded by word characters. These strings
+match the pattern /\b'\b/.
- "two words" =~ /(\w+)\b(\w+)/; # WRONG
- "two words" =~ /(\w+)\s+(\w+)/; # right
+ "don't" # the ' char is surrounded by "n" and "t"
+ "qep'a'" # the ' char is surrounded by "p" and "a"
- " =matchless= text" =~ /\b=(\w+)=\b/; # WRONG
- " =matchless= text" =~ /=(\w+)=/; # right
+These strings do not match /\b'\b/.
-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.
+ "foo'" # there is no word char after non-word '
+
+You can also use the complement of \b, \B, to specify that there
+should not be a word boundary.
+
+In the pattern /\Bam\B/, there must be a word character before the "a"
+and after the "m". These patterns match /\Bam\B/:
+
+ "llama" # "am" surrounded by word chars
+ "Samuel" # same
+
+These strings do not match /\Bam\B/
+
+ "Sam" # no word boundary before "a", but one after "m"
+ "I am Sam" # "am" surrounded by non-word chars
-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?
-Once Perl sees that you need one of these variables anywhere in
-the program, it provides 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 regex that contains capturing
-parentheses. If you never use $&, etc., in your script, then regexes
-I<without> capturing parentheses won't be penalized. So avoid $&, $',
-and $` if you can, but if you can't, once you've used them at all, use
-them at will because you've already paid the price. Remember that some
-algorithms really appreciate them. As of the 5.005 release. the $&
-variable is no longer "expensive" the way the other two are.
+(contributed by Anno Siegel)
+
+Once Perl sees that you need one of these variables anywhere in the
+program, it provides them on each and every pattern match. That means
+that on every pattern match the entire string will be copied, part of it
+to $`, part to $&, and part to $'. Thus the penalty is most severe with
+long strings and patterns that match often. Avoid $&, $', and $` if you
+can, but if you can't, once you've used them at all, use them at will
+because you've already paid the price. Remember that some algorithms
+really appreciate them. As of the 5.005 release, the $& variable is no
+longer "expensive" the way the other two are.
+
+Since Perl 5.6.1 the special variables @- and @+ can functionally replace
+$`, $& and $'. These arrays contain pointers to the beginning and end
+of each match (see perlvar for the full story), so they give you
+essentially the same information, but without the risk of excessive
+string copying.
=head2 What good is C<\G> in a regular expression?
Here are a few ways, all painful, to deal with it:
- $martian =~ s/([A-Z][A-Z])/ $1 /g; # Make sure adjacent ``martian''
+ $martian =~ s/([A-Z][A-Z])/ $1 /g; # Make sure adjacent "martian"
# bytes are no longer adjacent.
print "found GX!\n" if $martian =~ /GX/;
=head1 AUTHOR AND COPYRIGHT
-Copyright (c) 1997-2002 Tom Christiansen and Nathan Torkington.
-All rights reserved.
+Copyright (c) 1997-2005 Tom Christiansen, Nathan Torkington, and
+other authors as noted. All rights reserved.
This documentation is free; you can redistribute it and/or modify it
under the same terms as Perl itself.