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

I'm splitting a string at the "."

#!/usr/bin/perl print "Content-type: text/html\n\n"; my $latitude = -88.66666; my @values = split(/./,"$latitude") ; print "$values[0] xxxx $values[1]";
Why doesn't this work? I've tried surrounding $latitude with ' " or nothing. My brain is melting.

Replies are listed 'Best First'.
Re: Splitting String???
by ikegami (Patriarch) on Apr 26, 2005 at 05:48 UTC

    Because "." has a special meaning in regular expressions. Try escaping it:

    #!/usr/bin/perl print "Content-type: text/html\n\n"; my $latitude = -88.66666; my @values = split(/\./, $latitude); print "$values[0] xxxx $values[1]";

    btw, the quotes around $latitude are not necessary.

      Okay, I want to truncate the decimal places to 6 decimal places, this is how I'm doing it but my $lat and $lon variables are coming out blank. I'm splitting the string, truncating the decimal places and then recombining them. But the variables aren't printing any values.

      What's wrong? Is there a simpler way to do this? Perhaps in a single function?
      #!/usr/bin/perl my $declon = -88.9999999999999999999; my $declat = 44.1111111111111111111; my @lon = split(/\./,"$declon"); my @lat = split(/\./,"$declat"); $lon[1]=substr($lon[1],0,6); $lat[1]=substr($lat[1],0,6); my $lat = join(@lat); my $lon = join(@lon); print "$lat\n"; print "$lon";

        You forgot join's first argument.

        #!/usr/bin/perl my $declon = -88.9999999999999999999; my $declat = 44.1111111111111111111; print($declon, "\n"); # -89 print($declat, "\n"); # 44.1111111111111 my @lon = split(/\./, $declon); my @lat = split(/\./, $declat); $lon[1] = substr($lon[1], 0, 6); $lat[1] = substr($lat[1], 0, 6); my $lat = join('.', @lat); my $lon = join('.', @lon); print "$lat\n"; print "$lon";

        Again, I removed the useless quotes around $declon and $declat.

        As you can see by the two prints I added near the top, -88.9999999999999999999 gets stringified as -89. You're going beyond the precision of a double. This also demonstrates where your algorithm fails. (-89 prints as "-89." instead of "-89.000000".) What you want is

        # Round (.5 rounds up) $num = int($num * 1000000 + 0.5) / 1000000;

        or maybe

        # Truncate towards 0. $num = int($num * 1000000) / 1000000;

        You can also do it using string manipluation for a little bit more precision:

        $lon = sprintf('%.6f', $declon); $lat = sprintf('%.6f', $declat);

        Example 1:

        #!/usr/bin/perl use strict; use warnings; my $declon = -88.9999999999999999999; my $declat = 44.1111111111111111111; my $lon = int($declon * 1000000 + 0.5) / 1000000; my $lat = int($declat * 1000000 + 0.5) / 1000000; printf("%.6f\n", $lon); # -88.999999 printf("%.6f\n", $lat); # 44.111111

        Example 2:

        #!/usr/bin/perl use strict; use warnings; my $declon = -88.9999999999999999999; my $declat = 44.1111111111111111111; printf("$declon\n"); # -89 printf("$declat\n"); # 44.1111111111111 printf("%.6f\n", $declon); # -89.000000 printf("%.6f\n", $declat); # 44.111111

        Truncating is fairly simple:

        my $number = 123.1234567; $number =~ s/(?<=\.)(\d{1,6}).*$/$1/; print $number; # 123.123456

        =~ s/indicates that we're going to apply a regex to $number
        (?<=\.)signifies that the match is to be made after a literal decimal.
        (\d{1,6})captures the first six \d characters in the class [0-9], and stores the value in $1.
        .*$matches the remainder of the variable contents.
        /seperates the match from the replacement.
        $1is the first six decimal places as captured earlier.

        /;signifies the end of the regex and line respectively.

        If you want to round, that can be done as well (see UPDATE):

        $number =~ s/(?<=\.)(\d{1,6})(\d).*$/$2>4?$1+1:$1/e;

        $2>4?$1+1:$1is a ternary conditional, which tests whether the seventh digit should be rounded ($2 > 4), and then returns either $1 + 1 or $1 as appropriate.
        /einstructs perl to evaluate the replacement as opposed to returning a literal value

        UPDATE: The truncation code works; however the rounding code does not. Given that 9 would round to 10, which has the potential to be iterative, eg 9.9999999, it would affect both itself and the preceeding character, which obviously has the ability to affect characters on both sides of the decimal point. Preliminary testing has shown that the correct line is more complex than a simple example. Nevertheless, it remains a decent demonstration of /e.

        As it turns out the rounding code is considerably easier than I had originally thought:

        my $number = 123.1234567; $number += .0000005; # Add 5 to the seventh digit $number =~ s/(?<=\.)(\d{1,6}).*$/$1/; # truncate to six digits print $number; # 123.123456

        It's essentially the same thing as the truncation code above, save the addition of the += line. Adding 5 to the decimal is a fairly common way to round an int -- I guess I just didn't realize it applied to rounding the 7th decimal place as well.

        Okay, I want to truncate the decimal places to 6 decimal places ... Is there a simpler way to do this?
        You mean like this?
        my $declon = -88.9999999999999999999; my $declat = 44.1111111111111111111; print substr(sprintf ("%.7f", $declon),0,-1), "\n", substr(sprintf ("%.7f", $declat),0,-1);
        Note: The substr is there to avoid the rounding of sprintf. You could write sprintf ("%.6f", $declon) but then the value will get rounded.


        holli, /regexed monk/
Re: Splitting String???
by eibwen (Friar) on Apr 26, 2005 at 05:49 UTC

    Don't forget to escape a literal decimal point.

    The . character in a regex (see perlre) represents any singular character. The line split(/./) does not split on a literal . -- rather every character. As ikegami pointed out above, to split on a literal . merely split(/\./).

    UPDATE: It would seem an inopportune sequence of a browser crash and resolution failures significantly delayed the submission of this node. Appologies for the duplication.