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

Aloha all,

I've got some files like this:

... 1125356700 0 0 0 0 1125356400 15 0 25 0 1125356100 25 0 25 0 1125355800 25 0 25 0 1125355500 25 0 25 0 ... lines here ... 1125397800 0 0 0 0 1125397500 0 0 0 0 1125397200 0 0 0 0 1125396900 0 0 0 0 1125396600 0 0 0 0 1125396300 0 0 0 0 1125396000 39006 0 63597 0 1125395700 63597 0 63597 0 1125395400 63597 0 63597 0 1125395100 63597 0 63597 0 1125394800 63597 0 63597 0 1125394500 63597 0 63597 0 1125394200 63597 0 63597 0 ...

I would like to go through this file, and look at ONLY the four fields on the right, not the timestamp (the first number). If any of those nubmers is over 1000, I would like to replace it with a zero.

Is this possible without a zillion lines of code? Or should I hack up some crazy shell solution?

I imagine that if someone could at least tell me how to break each line into the four separate numbers, ie $var1, $var2, $var3, and $var4, then I could likely get the rest myself, though it wouldn't be pretty.

Thanks a lot,

-Jack C
jack {at} crepinc.com
http://www.crepinc.com/

Replies are listed 'Best First'.
Re: String Search/Replace
by Fletch (Bishop) on Aug 31, 2005 at 14:32 UTC

    Fore!

    perl -lane 'for(@F[1..4]){$_=0if$_>1000};print"@F"'

    Update: Shave a couple of strokes.

    perl -lape 'for(@F[1..4]){$_=0if$_>1000};$_="@F"'

    --
    We're looking for people in ATL

      It worked perfectly!

      Thanks!

      -Jack C
      jack {@} crepinc.com
      http://www.crepinc.com/

Re: String Search/Replace
by ikegami (Patriarch) on Aug 31, 2005 at 14:24 UTC
    Zillion? How about only one:
    perl -ple "@f=split; for(@f[1..4]){ $_=0 if $_>1000 } $_=qq{@f}" infil +e >outfile
    or in-place:
    perl -i.bak -ple "@f=split; for(@f[1..4]){ $_=0 if $_>1000 } $_=qq{@f} +" file
    Update:

    Better yet, -a saves us the trouble of splitting:

    perl -lape "for(@F[1..4]){ $_=0 if $_>1000 } $_=qq{@F}" infile >outfil +e
    or in-place:
    perl -i.bak -lape "for(@F[1..4]){ $_=0 if $_>1000 } $_=qq{@F}" file

    perlrun documents -i, -l, -a, -p and -e.

    The command can be written in fewer strokes, but I left it readable.

      Hm... I tried the first in your original post and your updated, both give the same error. I check, and the file I'm using is definatly writable.
      bash-2.05$ perl -ple "@f=split; for (@f[1..4]) { $_=1000 if $_>1000; } + $_=join(' ',@f)" TEST Can't modify constant item in scalar assignment at -e line 1, near "10 +00 if" Execution of -e aborted due to compilation errors. bash-2.05$ perl -lape "for(@F[1..4]){ $_=1000 if $_>1000 } $_=qq{@F}" +TEST Can't modify constant item in scalar assignment at -e line 1, near "10 +00 if" Execution of -e aborted due to compilation errors.
      Did I do something wrong?

      -Jack C
      jack {@} crepinc.com
      http://www.crepinc.com/

        I'm using quoting appropriate for the Windows shell. Substitute " for ' and vice-versa for unix shells.
Re: String Search/Replace
by gargle (Chaplain) on Aug 31, 2005 at 14:38 UTC

    Hi,

    Many answers already, but, if you're looking to split up the numbers in variables:

    #!/usr/bin/perl use strict; use warnings; while (<DATA>) { chomp; my ($a, $b, $c, $d, $e) = split/ /; print "$a $b $c $d $e\n"; } __DATA__ 1125356700 0 0 0 0 1125356400 15 0 25 0 1125356100 25 0 25 0 1125355800 25 0 25 0 1125355500 25 0 25 0 1125397800 0 0 0 0 1125397500 0 0 0 0 1125397200 0 0 0 0 1125396900 0 0 0 0 1125396600 0 0 0 0 1125396300 0 0 0 0 1125396000 39006 0 63597 0 1125395700 63597 0 63597 0 1125395400 63597 0 63597 0 1125395100 63597 0 63597 0 1125394800 63597 0 63597 0 1125394500 63597 0 63597 0 1125394200 63597 0 63597 0
    --
    if ( 1 ) { $postman->ring() for (1..2); }
Re: String Search/Replace
by philcrow (Priest) on Aug 31, 2005 at 14:40 UTC
    I like golf about as much as the next person, but how about something a bit more expansive:
    #!/usr/bin/perl use strict; while (<>) { my ($timestamp, $number, @rest) = split; $number = 0 if ($number > 1000); print "$timestamp $number @rest\n"; }
    The solutions above work in place. This one is a filter. To use it do something like this:
    ./filter < input > output
    But don't make output the same as input or the shell will kill your data.

    Phil

Re: String Search/Replace
by ChrisR (Hermit) on Aug 31, 2005 at 15:07 UTC
    How about a regex:
    #!c:\perl\bin\perl.exe -w use strict; use warnings; for my $line (<DATA>) { $line =~ s/ \d{4,}( |$)/ 0$1/g; print $line; } __DATA__ 1125356700 0 0 0 0 1125356400 15 0 25 0 1125356100 25 0 25 0 1125355800 25 0 25 0 1125355500 25 0 25 0 1125397800 0 0 0 0 1125397500 0 0 0 0 1125397200 0 0 0 0 1125396900 0 0 0 0 1125396600 0 0 0 0 1125396300 0 0 0 0 1125396000 39006 0 63597 0 1125395700 63597 0 63597 0 1125395400 63597 0 63597 0 1125395100 63597 0 63597 0 1125394800 63597 0 63597 0 1125394500 63597 0 63597 0 1125394200 63597 0 63597 0

    Or for a one liner:

    perl.exe -ple "s/ \d{4,}( |$)/ 0$1/g" filename

    Update:

    Oops... As cheseter has pointed out, the above will replace 1000 when it should not.

    Here's my regex that works:

    $line =~ s/ (\d+)/$1>1000?" 0":" $1"/ge;
    or at least I think it does.

      {4,} is greedy, so the ( |$) is not needed. The following is sufficient, shorter and faster:

      perl -ple "s/ \d{4,}/ 0/g" infile >outfile
        Thanks. I didn't think about that. I've never been very good at golf (perl or otherwise).
        The -l is unnecessary, too.

        Caution: Contents may have been coded under pressure.
      Actually doesn't the original question say to replace numbers "over 1000" with 0? Your solution also replaces 1000 with 0, which would be incorrect.
        Err... You've got me there. I think you should remove the strike from your previous post. Your solution is the shortest since it appears that mine is incorrect.

      Hi,

      I really do love regex's but sometimes, when only used to cut up a string in pieces, pieces seperated by the same character, I prefer to use split. Reading the code becomes a bit easier on the eyes because you can immediately deduct the meaning of the line of code. With regex's you still have to do a bit of mental juggling before the intention becomes clear.

      --
      if ( 1 ) { $postman->ring() for (1..2); }
Re: String Search/Replace
by chester (Hermit) on Aug 31, 2005 at 14:57 UTC
    Without using split (I think this is the shortest so far too...):

    perl -ple "s/(?<!^)\b(\d+)/$1>1000?0:$1/eg" filename
    edit: Nope, see below. :)

    edit2: Never mind...

Re: String Search/Replace
by sh1tn (Priest) on Aug 31, 2005 at 14:49 UTC
    Another one liner:
    perl -lape 's/\S+\s+//&&s/(\d+)/$1>1000?0:$1/ge' filename
    Update: ok this (even shorter) does not remove the timestamp:
    perl -pe 's/(?<=\s)(\d+)/$1>1000?0:$1/ge' filename


      That removes the timestamp.