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

I am attempting to use regex on a text file that will match two numbers that represent temperature in Celsius. I attempted to put the numbers in two separate capture groups and then put them inside a formula to convert to Fahrenheit. I would then set two variables equal to two separate formulas with each using one of the capture groups. The point of this is to take a line like this:

KSMO 181551Z 00000KT 10SM CLR 14/08 A3009 RMK AO2 SLP189 T01440083

and take the "14/08" to convert it to Fahrenheit and substitute it back into the line. When I run my code I receive these errors:

Odd number of elements in anonymous hash at lab2-2.pl line 11.

Use of uninitialized value $1 in anonymous hash ({}) at lab2-2.pl line 11.

Odd number of elements in anonymous hash at lab2-2.pl line 12.

Use of uninitialized value $2 in anonymous hash ({}) at lab2-2.pl line 12.

readline() on closed filehandle IN at lab2-2.pl line 13.

I am wondering what I am missing. Is it an issue with my regex or somewhere else? Any help would be appreciated.

#usr/bin/perl use strict; use warnings; open IN,'<',"part2.txt"; open OUT,'>',"airport_fahrenheit.txt"; my $x; my $fahrenheit1 = (9 *{$1}/5) + 32; my $fahrenheit2 = (9 *{$2}/5) + 32; foreach $x (<IN>){ ( $x =~ s/(\d\d)\/(\d\d)/$fahrenheit1\/$fahrenheit2/g ); print OUT "$x"; } close IN; close OUT;

Replies are listed 'Best First'.
Re: substitution with regex
by 1nickt (Canon) on Jan 19, 2018 at 02:43 UTC

    Hey dude, not everywhere has perfect climate like Santa Monica! As you may know, negative temps exist at some locations (and are denoted with a leading 'M' in METAR reports).

    Here is the skeleton of a solution handling negative temperatures (using the reading from Fairbanks, AK at this time for testing).

    (But see also Geo::METAR for an oldie-but-goodie parser for this format, which provides the temp in F in its dump output.)

    use strict; use warnings; while ( my $line = <DATA> ) { $line =~ s{ \s (M?\d{2}) / (M?\d{2}) \s } { sprintf ' %d/%d ', convert($1), convert($2) }xeg; print $line; } sub convert { $_[0] =~ s/M/-/r * 9/5 + 32 }; __DATA__ KSMO 181551Z 00000KT 10SM CLR 14/08 A3009 RMK AO2 SLP189 T01440083 PAFA 190153Z 00000KT 5SM -SN BR FEW012 BKN025 OVC070 M07/M08 A2959 RMK + AO2 SLP030 T10671078
    Output:
    KSMO 181551Z 00000KT 10SM CLR 57/46 A3009 RMK AO2 SLP189 T01440083 PAFA 190153Z 00000KT 5SM -SN BR FEW012 BKN025 OVC070 19/17 A2959 RMK A +O2 SLP030 T10671078

    Hope this helps!


    The way forward always starts with a minimal test.

      Just wondering what exactly is the purpose of the sprintf and %d/%d?

        Hello drose2211,

        The sprintf function applies the formatting specified in the template supplied as its first argument, to produce a string using the remaining arguments. In this case, the template ' %d/%d ' means: print a space, followed by the next argument formatted as an integer, followed by a slash, followed by the next argument formatted as an integer, followed by a space. Without the sprintf function, the code would be longer and less elegant:

        $line =~ s{ \s (M?\d{2}) / (M?\d{2}) \s } { ' ' . int(convert($1)) . '/' . int(convert($2)) . ' ' }xeg +;

        (Perl’s printf and sprintf functions are derived from their equivalents in C. In Perl, there is also print, which does not take a template, but there is no corresponding sprint.)

        Hope that helps,

        Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

        drose2211:   Further to Athanasius's post:   Note also that the  s/// substitution operator needs the  /e modifier to be able to evaluate an arbitrary expression in its replacement block. See  s/PATTERN/REPLACEMENT/ in Regexp Quote-Like Operators in perlop.


        Give a man a fish:  <%-{-{-{-<

Re: substitution with regex
by Laurent_R (Canon) on Jan 19, 2018 at 07:30 UTC
    Hi drose2211,

    Other monks have given good solutions, but you might also be want to know what your mistakes are.

    First, you can't use the $1 and $2 special variables in a formula before they have been set by a regular expression. In other word, they are undefined at the point where you're using them in the conversion formula.

    Second, I am not sure why you enclose them within curly braces then is no need to do that. If you want to isolate them from neighboring characters (not needed here), then the proper syntax is ${1}, not {$1}.

    Finally, it seems from the error messages you get that your input file is not properly opened. When you open a file, you should always check if the statement worked:

    open IN,'<',"part2.txt" or die "could not open file part2.txt $!";
    Update: fixed a typo on the filename on the preceding line. Thanks, soonix.
      open IN,'<',"part2.txt" or die "could not open file part2;txt $!";
      Yep, and that's one reason why I prefer putting filenames in variables :-) (otherwise, ++, of course)
        I prefer putting filenames in variables
        Yes soonix++, and that's what I also do systematically in actual code. When suggesting corrections on a forum, however, I tend to stress only the most essential suggestions, to avoid distracting attention with more secondary matters.
Re: substitution with regex
by Anonymous Monk on Jan 19, 2018 at 01:12 UTC
    s{...}{ c2f($1).'/'.c2f($2) }ge; sub c2f { my $celsius = shift; return (9 *{$celsius}/5) + 32; }