http://www.perlmonks.org?node_id=11141078

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

Hi Monks!

Getting a warning on a line of code where I am trying to use "sprintf" to pad the month date with a leading 0.
Use of uninitialized value $_ in pattern match (m//) at...
#!/usr/bin/env perl use strict; use warnings; my $date = "3-15-1932" $date=~ s/(\d+)-(\d+)-(\d+)/$3-\/sprintf("%02d",$1)\/-$2/ge; print $date; #Want to print like this: 1932-03-15

Is it how I could use spritnf with in the regex?

Thanks for looking!

Replies are listed 'Best First'.
Re: Using sprintf in regular expreession.
by jwkrahn (Abbot) on Feb 02, 2022 at 19:53 UTC
    $date=~ s/(\d+)-(\d+)-(\d+)/ sprintf( "%d-\/%02d\/-%d", $3, $1, $2 +)/ge;
Re: Using sprintf in regular expreession.
by rsFalse (Chaplain) on Feb 02, 2022 at 20:12 UTC
    s/PATTERN/REPLACEMENT/msixpodualngcer:
    >> e Evaluate the right side as an expression.

    The whole right side of the regex is evaluated.
    You can't make some parts of the REPLACEMENT evaluated and some not. So you should wrap "$3-" and "-$2" in doublequotes and join all parts by concatenation, e.g.: $date =~ s/(\d+)-(\d+)-(\d+)/ "$3-" . sprintf("%02d",$1) . "-$2" /ge;

    Upd. You need double quotes to interpolate $3 and $2 (single quotes won't).
      Great explanation! See jwkrahn (above) for a simpler form of the required expression.
      Bill
Re: Using sprintf in regular expreession.
by Fletch (Bishop) on Feb 02, 2022 at 20:41 UTC

    Also not directly relevant because you've got them because of your misuse of /e, but in general if you need to use slashes in a match or substitution it's generally cleaner to use an alternate delimiter (or matched pair of delimiters) like s{MATCH}{REPLACE} instead to reduce the amount of backwhacking you need to do.

    The cake is a lie.
    The cake is a lie.
    The cake is a lie.

Re: Using sprintf in regular expression.
by hippo (Bishop) on Feb 03, 2022 at 11:23 UTC

    rsFalse has given you a good general-purpose answer. However, since you are manipulating dates, you could always use a date module such as the core module Time::Piece.

    use strict; use warnings; use Time::Piece; use Test::More tests => 1; my $date = '3-15-1932'; my $want = '1932-03-15'; my $have = Time::Piece->strptime ($date, '%m-%d-%Y')->date; is $have, $want;

    🦛

Re: Using sprintf in regular expreession.
by johngg (Canon) on Feb 02, 2022 at 22:22 UTC

    Rather than using regular expressions and substitution you could split on hyphens into a list which you then slice into the order required. Pass the re-ordered items through a map in which you apply the sprintf and then join it all up again. Perhaps a bit more long winded but, to my eyes, a little easier to see what's going on.

    johngg@abouriou:~/go$ perl -Mstrict -Mwarnings -E 'say q{}; my $date = q{3-15-1932}; $date = join q{-}, map { sprintf q{%02d}, $_ } ( split q{-}, $date )[ 2, 0, 1 ]; say $date;' 1932-03-15

    I hope this is of interest.

    Cheers,

    JohnGG

      > Perhaps a bit more long winded

      IMHO, you are overcomplicating it. sprintf can take a list of values, no need for a map.

      It can even rearrange the order, tho I prefer the slice here.

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      Wikisyntax for the Monastery

        TIMTOWTDI

        DB<23> $date = "3-15-1932" #### split and slice DB<24> printf "% 4d-%02d-%02d", (split /-/, $date)[2,0,1] 1932-03-15 #### match list DB<25> printf "% 4d-%02d-%02d", ($date =~ /(\d+)-(\d+)-(\d+)/ )[2,0, +1] 1932-03-15 #### reorder printf with 3$ DB<26> printf "%3\$ 4d-%02d-%02d", split /-/, $date 1932-03-15 #### classic DB<27> printf "% 4d-%02d-%02d",$3,$1,$2 if $date =~ /(\d+)-(\d+)-(\d ++)/ 1932-03-15

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        Wikisyntax for the Monastery

Re: Using sprintf in regular expreession.
by Marshall (Canon) on Feb 05, 2022 at 08:11 UTC
    Since this is Perl and there is always more than one way.... you can use 2 regexes and then you don't need sprintf. I don't know which actually executes faster.

    Code follows showing these cases..
    a. could use one regex to handle 2 digit day, another for 1 digit day
    b. probably better is to handle single digit day, then swap order instead of combining the replace and swap
    c. doesn't handle single digit month, so could use a 3rd regex to fix that
    d. finally, could fix any single digit and then swap order

    use strict; use warnings; my $x='11-3-1936'; $x=~ s/(\d+)-(\d\d)-(\d+)/$3-$1-$2/; #2 digit day $x=~ s/(\d+)-(\d)-(\d+)/$3-$1-0$2/; #1 digit day print "$x\n"; # 1936-11-03 $x='2-5-1966'; $x=~ s/(\d+)-(\d)-(\d+)/$1-0$2-$3/; # fix 1 digit day $x=~ s/(\d+)-(\d+)-(\d+)/$3-$1-$2/; # swap order print "$x\n"; # 1966-2-05 doesn't fix single digit month $x='2-5-1966'; $x=~ s/(\d+)-(\d)-(\d+)/$1-0$2-$3/; # fix 1 digit day $x=~ s/(\d)-(\d+)-(\d+)/0$1-$2-$3/; # fix 1 digit month $x=~ s/(\d+)-(\d+)-(\d+)/$3-$1-$2/; # swap order print "$x\n"; # 1966-02-05 ok, but uses 3 regexes $x='2-5-1966'; $x=~ s/\b(\d)\b/0$1/g; # fix any single digit $x=~ s/(\d+)-(\d+)-(\d+)/$3-$1-$2/; # swap order print "$x\n" # 1966-02-05
Re: Using sprintf in regular expreession.
by perlfan (Vicar) on Feb 04, 2022 at 06:00 UTC
    A. that's super funky use of sprintf

    B. you really need to take a look at POSIX::strftime and it's red headed brother, POSIX::strptime. Both give you the same feels as sprintf if that's what you're needing

    I mark my journey as a programmer by moments of sheer awe when I discover something awesome. That combo is one of those times. The other was when I first learned to create a mailto: link in my college computer lab in the 90s. These 2 events were roughtly 19 years apart and the ones that stick out most in my mind. Good luck.