1

I got file called numbers.txt which is basically line with 5 numbers: they look like this:

1 2 3 4 5

What I'm trying to achieve is I want to read those numbers from the line (which already works), then in each iteration I want to add +1 to every number which was read from that file and print them on screen with print, so the final result should look like:

1 2 3 4 5
2 3 4 5 6
3 4 5 6 7
4 5 6 7 8
5 6 7 8 9

.

#!/usr/bin/perl

use strict;
use warnings;

open("handle", 'numbers.txt') or die('unable to open numbers file\n');
$/ = ' ';

OUT: for my $line (<handle>) {
        for (my $a = 0; $a < 5; $a++) {
            chomp $line;
            $line += 1;
            print "$line ";
                next OUT;
        }
}
close("handle");

Haven't done looping in perl for a while now and would be great if someone could provide working example.

Also, it would be great if you could provide more than one working example, just to be future proof ;) Thanks

0

5 Answers 5

1

You can try this on for size.

#!/usr/bin/perl

use strict;
use warnings;

open("handle", 'numbers.txt') or die('unable to open numbers file\n');

for my $line (<handle>) {
    chomp $line;
    for my $number (split /\s+/, $line) {
        for (my $a = $number; $a < $number+5; $a++) {
            print "$a ";
        }       
        print "\n";
    }
}
close("handle");

You can dispense with $/=' ' and instead let the outer loop iterate on lines of the file.
For each line you want to iterate for each number which is separated by white space, thus the split /\s+/, $line which gives you a list of numbers for the inner loop. For your output $a starts at the number read from the file.

Sign up to request clarification or add additional context in comments.

1 Comment

split /\s+/ should almost always be split ' '
1

This will do what you're after:

use strict;
use warnings;

while(<DATA>) {
    chomp;
    print "$_\n";
    my @split = split;
    my $count = 0;
    for (1..4){
        $count++;
        foreach (@split){ 
            my $num = $_ + $count;
            print "$num ";
        }
        print "\n";
    }

}

__DATA__
1 2 3 4 5

Comments

1

Here no need to use nested loop it's always program make slower.

#!/usr/bin/perl
use strict;
use warnings;

my @num = split(" ",(<DATA>)[0]);  

foreach my $inc (0..$#num)
{
    print map{$inc+$_," "}@num;  # Add one by one in array element
    print "\n";
}


__DATA__
1 2 3 4 5

Comments

1

Update   Added another method, this one in line with the posted approach.


Increment each number in the string, changing the string in place. Repeat that. Below are two ways to do that. Yet another method reads individual numbers and prints following integer sequences.

(1) With regular expressions. It also fits in one-liner

echo "1 2 3 4 5" | perl -e '$v = <>; for (1..5) { print $v; $v =~ s/(\d+)/$1+1/eg; }'

This prints the desired output. But better put it in a script

use warnings;
use strict;

my $file = 'numbers.txt';
open my $fh, '<', $file or die "can't open $file: $!";

while (my $line = <$fh>) {  
    # Add chomp($line) if needed for some other processing.
    for (1..5) {
        print $line;
        $line =~ s/(\d+)/$1+1/eg;
    }   
}

The /e modifier is crucial for this. It makes the replacement side of the regex be evaluated as code instead of as a double-quoted string. So you can actually execute code there and here we add to the captured number, $1+1, for each matched number as /g moves down the string. This changes the string so the next iteration of the for (1..5) increments those, etc. I match multiple digits, \d+, which isn't necessary in your example but makes far more sense in general.

(2) Via split + map + join, also repeatedly changing the line in place

while (my $line = <$fh>) {
    for (1..5) {
        print $line;
        $line = join ' ', map { $_+1 } split '\s+', $line;            
    }
}

The split gets the list of numbers from $line and feeds it to map, which increments each, feeding its output list to join. The joined string is assigned back to $line, and this is repeated. I split by \s+ to allow multiple white space but this makes it very 'relaxed' in what input format it accepts, see perlrecharclass. If you know it's one space please change that to ' '.

(3) Take a number at a time and print the integer sequence starting from it.

open my $fh, '<', $file or die "can't open $file: $!";
local $/ = ' ';
while (my $num = <$fh>) {
    print "$_ " for $num..$num+4; 
    print "\n";
}

The magical 4 can be coded by pre-processing the whole line to find the sequence length, say by

my $len = () = $line =~ /(\d+)/g;

or by split-ing into an array and taking its scalar, then using $len-1.


Additional comments.

  • I recommend the three-argument open, open my $fh, '<', $file

  • When you check a call print the error, die "Your message: $!", to see the reason for failure. If you decide to quit, if ($bad) { die "Got $bad" }, then you may not need $!. But when an external call fails you don't know the reason so you need the suitable error variable, most often $!.

1 Comment

@jonasv Updated: added another method (more like OP), fixed a blooper in regex, changed prints around to streamline.
0

Your program has a number of problems. Here is what's stopping it working

  • You are setting the record separator to a single space. Your input file contains "1 2 3 4 5\n", so the while loop will iterate five times setting $line to "1 ", "2 ", "3 ", "4 ", "5\n"

  • Your for loop is set up to iterate five times. It does chomp $line which removes the space after the number, then increments $line and prints it. Then you jump out of the for loop, having executed it only once, with next OUT. This results in each value in the file being incremented by one and printed, so you get 2 3 4 5 6

  • Removing the unnecessary next OUT, produces something closer

    2 3 4 5 6 3 4 5 6 7 4 5 6 7 8 5 6 7 8 9 6 7 8 9 10
    

    There are now five numbers being printed for each number in the input file

  • Adding print "\n" after the for loop help separate the lines

    2 3 4 5 6 
    3 4 5 6 7 
    4 5 6 7 8 
    5 6 7 8 9 
    6 7 8 9 10
    
  • Now we need to print the number before it is incremented instead of afterwards. If we swap $line += 1 and print "$line " we get this

    1 2 3 4 5 
    2 3 4 5 6 
    3 4 5 6 7 
    4 5 6 7 8 
    5
     6 7 8 9 
    

    What is happening here is that the 5 is still followed be a newline, which now appears in the output. The chomp won't remove this because it removes the value of $/ from the end of a string. You've set that to a space, so it will remove only spaces. The fix is to replace chomp with a substitution s/\s+//g which removes *all whitespace from the string. You also need to do that only once so I've put it outside the for loop at the top

Now we get this

    1 2 3 4 5 
    2 3 4 5 6 
    3 4 5 6 7 
    4 5 6 7 8 
    5 6 7 8 9 

And this is your code as it ended up

use strict;
use warnings;

open( "handle", 'numbers.txt' ) or die('unable to open numbers file\n');

$/ = ' ';

for my $line (<handle>) {

    $line =~ s/\s+//g;

    for ( my $a = 0; $a < 5; $a++ ) {
        print "$line ";
        $line += 1;
    }

    print "\n";
}

close("handle");

There are a few other best practices that could improve your program

  • Use use warnings 'all'

  • Use lexical file handles, and the three-parameter form of open

  • Use local if you are changing Perl's built-in variables

  • Put $! into your die string so that you know why the open failed

  • Avoid the C-style for loop, and iterate over a list instead

Making these fixes as well looks like this. The output is identical to the above

use strict;
use warnings 'all';

open my $fh, '<', 'numbers.txt'
        or die qq{Unable to open "numbers.txt" for input: $!};

local $/ = ' ';

for my $line ( <$fh> ) {

    $line =~ s/\s+//g;

    for my $a ( 0 .. 4 ) {
        print "$line ";
        ++$line;
    }

    print "\n";
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.