merge 2 files per third using perl

I am viewing a test and I cannot get this code to encode correctly.

Problem: write a perl script called ileaf that will alternate with the line in the line with the file of another file that writes the result to the third file. If the files have different lengths, then redundant lines are written at the end.

Sample call: File ileaf file1 file2

This is what I have:

#!/usr/bin/perl -w open(file1, "$ARGV[0]"); open(file2, "$ARGV[1]"); open(file3, ">$ARGV[2]"); while(($line1 = <file1>)||($line2 = <file2>)){ if($line1){ print $line1; } if($line2){ print $line2; } } 

This sends information to the screen so that I can immediately see the result. The final verson is to "print file3 $ line1;" I get all the files file1, and then the whole file2 and / and line interleaving.

If I understand correctly, this is a function of using "||" in my while loop. At the same time, it checks the first comparison, and if it is true, it enters the loop. This will only check file1. After file1 is false, then while checks file2 and enters the loop again.

What can I do to alternate strings?

+4
source share
4 answers

You do not get what you want from while(($line1 = <file1>)||($line2 = <file2>)){ , because so far ($line1 = <file1>) true, ($line2 = <file2>) will never happen.

Try something like this:

 open my $file1, "<", $ARGV[0] or die; open my $file2, "<", $ARGV[1] or die; open my $file3, ">", $ARGV[2] or die; while (my $f1 = readline ($file1)) { print $file3 $f1; #line from file1 if (my $f2 = readline ($file2)) { #if there are any lines left in file2 print $file3 $f2; } } while (my $f2 = readline ($file2)) { #if there are any lines left in file2 print $file3 $f2; } close $file1; close $file2; close $file3; 
+7
source

You think that if they teach you Perl, they will use modern Perl syntax. Please do not take it personally. In the end, this is how you studied. However, you should be aware of the new Perl programming style, as it helps eliminate all sorts of programming errors and makes code easier to understand.

  • Use pragmas use strict; and use warnings; . The warning pragma replaces the need for the -w flag on the command line. It is actually more flexible and better. For example, I can turn off special warnings when I know that they will be a problem. Pragma use strict; requires me to declare my variables with both mine and ours. (NOTE: Do not declare Perl built-in variables.) In 99% of cases you will use mine. These variables are called lexically encompassed, but you can think of them as true local variables. Vocabulary-oriented variables have no value outside their scope. For example, if you declare a variable using my inside a while loop, that variable will disappear after the loop exits.
  • Use three parameter syntaxes for the open statement . In the example below, I use three parameter syntaxes. That way, if the file is named >myfile , I can read it.
  • ** Use locally defined file descriptors. Note that I'm using my $file_1_fh instead of just FILE_1_HANDLE. The old way is FILE_1_HANDLE all over the world, and it is very difficult to pass a file descriptor to a function. Using lexically processed files works great.
  • Use or and and instead of || and && . They are easier to understand, and their priority for operators is better. They are likely to cause no problems.
  • Always check if your open statement works : you need to make sure that your open statement actually opened the file. Or use the pragma use autodie; , which will kill your program if open instructions fail (this is probably what you want to do anyway.

And here is your program:

 #! /usr/bin/env perl # use strict; use warnings; use autodie; open my $file_1, "<", shift; open my $file_2, "<", shift; open my $output_fh, ">", shift; for (;;) { my $line_1 = <$file_1>; my $line_2 = <$file_2>; last if not defined $line_1 and not defined $line_2; no warnings qw(uninitialized); print {$output_fh} $line_1 . $line_2; use warnings; } 

In the above example, I read both files, even if they are empty. If there is nothing to read, then $line_1 or $line_2 simply undefined. After I read, I check if $line_1 and $line_2 undefined. If so, I use last to complete my loop.

Since my file descriptor is a scalar variable, I like to put it in braces, so people know this file descriptor, not the variable I want to print. I do not need this, but it improves clarity.

Pay attention to no warnings qw(uninitialized); . This will disable the uninitialized warning that I will receive. I know that either $line_1 or $line_3 can be uninitialized, so I do not want this warning. I will bring it back below my print application because it is a valuable warning.

Here is another way to do this for loop:

 while ( 1 ) { my $line_1 = <$file_1>; my $line_2 = <$file_2>; last if not defined $line_1 and not defined $line_2; print {$output_fh} $line_1 if defined $line_1; print {$output_fh} $line_2 if defined $line_2; } 

An infinite loop is a while loop instead of a for loop. Some people don't like the C style in the for loop and have banned it from coding practice. That way, if you have an infinite loop, you use while ( 1 ) { . For me, perhaps because I came from the background of C, for (;;) { means an infinite loop, while while ( 1 ) { takes a few extra milliseconds to digest.

In addition, I check if $line_1 or $line_2 before I print them. I think this is better than using no warning and warning , but I need two separate print statements instead of combining them into one.

+2
source

Here is another parameter that uses List :: MoreUtils zip to alternate arrays and File :: Slurp to read and write files:

 use strict; use warnings; use List::MoreUtils qw/zip/; use File::Slurp qw/read_file write_file/; chomp( my @file1 = read_file shift ); chomp( my @file2 = read_file shift ); write_file shift, join "\n", grep defined $_, zip @file1, @file2; 
+2
source

I just noticed that Tim has a good solution already sent. This solution is a bit more verbose, but it can illustrate what exactly happens next.

The method I went through reads all the lines from both files into two arrays, then scrolls them using a counter.

 #!/usr/bin/perl -w use strict; open(IN1, "<", $ARGV[0]); open(IN2, "<", $ARGV[1]); my @file1_lines; my @file2_lines; while (<IN1>) { push (@file1_lines, $_); } close IN1; while (<IN2>) { push (@file2_lines, $_); } close IN2; my $file1_items = @file1_lines; my $file2_items = @file2_lines; open(OUT, ">", $ARGV[2]); my $i = 0; while (($i < $file1_items) || ($i < $file2_items)) { if (defined($file1_lines[$i])) { print OUT $file1_lines[$i]; } if (defined($file2_lines[$i])) { print OUT $file2_lines[$i]; } $i++ } close OUT; 
+1
source

All Articles