htmanning has asked for the wisdom of the Perl Monks concerning the following question:

Monks, I'm using the following to identify 4 digit numbers in a string. I replaces the number with a hyperlinked number. It works, but I don't want to use it if a number is within a date format (i.e. 2018-01-01). I'm not sure how to say, look for the 4 digits unless it has a dash after it.
my $digits_4 = qr{ \b \d{4} \b }xms; $text =~ s{ ($digits_4) } {<a href="script.pl?do_what=view&unit=$1"><b>$1</b></a>}xmsg +;
Thanks!

Replies are listed 'Best First'.
Re: Identifying 3 or 4 digits
by hippo (Archbishop) on Apr 04, 2018 at 08:03 UTC
    I'm using the following to identify 4 digit numbers in a string.

    So not 3 as stated in the title? OK.

    use strict; use warnings; use Test::More; my @good = ( '1234', '5566foo bar', 'baz9980' ); my @bad = ( 'a', '12', '12.4', '-124baz', '2018-01-01' ); my $re = qr/\d{4}(?!-)/a; plan tests => @good + @bad; for my $str (@good) { like ($str, $re, "$str matched"); } for my $str (@bad) { unlike ($str, $re, "$str not matched"); }
Re: Identifying 3 or 4 digits
by Your Mother (Archbishop) on Apr 04, 2018 at 04:49 UTC
    look for the 4 digits unless it has a dash after it

    There's a good reference in the monastery. You want negative look-ahead. I also recommend not using \d. Under Unicode there are MANY numbers that will match that do not conform to the 0..9 I think you expect.

    /\b[0-9]{4}\b(?!-)/
      Thank you! This worked.
Re: Identifying 3 or 4 digits
by BillKSmith (Monsignor) on Apr 04, 2018 at 20:02 UTC
    In addition to the negative look-ahead, you must escape all the html mark-up characters which have special meaning in a substitution string. Normally you could use \Q and \E, but in this case, they would escape the $1's which must have their regex meaning. You also need a \d in the negative look-ahead to prevent matching the first three digits of the year. You probably should repeat this test with more realistic strings, but I think this covers the important cases. (All six cases pass)
    use strict; use warnings; use Test::Simple tests => 6; my $digits_4 = qr/(\d{3,4})(?![\d-])/; my $hyper = '<hyper>'; my @cases = ( # Input Expected output ['2018-1-1 1234', '2018-1-1 <a href="script.pl?do_what=view&unit +=1234"><b>1234</b></a>' ], ['2018-1-1 567', '2018-1-1 <a href="script.pl?do_what=view&unit +=567"><b>567</b></a>' ], ['2018-1-1 abcd', '2018-1-1 abcd' ], ['2018-1-1 1234 x', '2018-1-1 <a href="script.pl?do_what=view&unit +=1234"><b>1234</b></a> x' ], ['2018-1-1 567 x', '2018-1-1 <a href="script.pl?do_what=view&unit +=567"><b>567</b></a> x' ], ['2018-1-1 abcd x', '2018-1-1 abcd x' ], ); foreach my $case (@cases) { $case->[0] =~ s{$digits_4} {\<a href\=\"script\.pl\?do\_what\=view\&unit\=$1\"\>\<b\>$1\< +\/b\>\<\/a\>}; ok( $case->[0] eq $case->[1]); }
    Bill