1

The below is the Perl script that I wrote today. This reads the content from one file and writes on the other file. It works but, not completely.

#---------------------------------------------------------------------------
#!/usr/bin/perl

open IFILE, "text3.txt" or die "File not found";
open OFILE, ">text4.txt" or die "File not found";

my $lineno = 0;

while(<IFILE>)
{
 @var=<IFILE>;
 $lineno++;
 print OFILE "@var";
}

close(<IFILE>);
close(<OFILE>);
#---------------------------------------------------------------------------

The issue is, it reads and writes contens, but not all. text3.txt has four lines. The above script reads only from second line and writes on text4.txt. So, finally I get only three lines (line.no 2 to line.no 4) of text3.txt.

What is wrong with the above program. I don't have any idea about how to check the execution flow on Perl scripts. Kindly help me.

I'm completely new to Programming. I believe, learning all these would help me in changing my career path.

Thanks in Advance,

Vijay

Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
Invincible
  • 29
  • 1
  • 8
  • 2
    Starting off your question by making rude remarks to other Stack Overflow users makes me want to stop reading. So I did. – Greg Hewgill Aug 11 '10 at 10:01
  • Sorry, Greg Hewgill. But, it had really hurt me last time. This is only the second time I am using stack Overflow. The first time, I got a real bad comment. Hope u cud understand. – Invincible Aug 11 '10 at 10:05
  • 2
    Sorry to hear that you had a bad first experience. However, I would strongly recommend that you remove the rude comments about other users from this (and future) questions. Each question in Stack Overflow stands on its own merit, and you don't want to be known as the guy who always talks about other users in the first paragraph. – Greg Hewgill Aug 11 '10 at 10:07
  • Thanks. Next tip: Indent your code by four spaces (or hit Ctrl+K or the 101010 button) and you won't have to format your code with HTML entities (I already edited your post for formatting). – Greg Hewgill Aug 11 '10 at 10:12
  • Thank you very much, Greg. I'm completely new to Programming Industry. I'm a System Administrator, where there is no scope of programming. This will definitely help me in making my dream come true. I'll be a software programmer one day. :) – Invincible Aug 11 '10 at 10:17
  • Invincible, use modern practices w.r.t. file handles, see http://stackoverflow.com/questions/1479741/why-is-three-argument-open-calls-with-lexical-filehandles-a-perl-best-practice – daxim Aug 11 '10 at 11:50
  • 2
    @Greg - if you look at another Q from the same user, you will see the context. The comments, while a bit harsh, were IMHO entirely deserved - and this is coming from someone who explicitly asked for "more niceness towards newbies in [Perl] tag" on Meta. – DVK Aug 11 '10 at 14:08
  • @daxim: Thank you very much for the information. @DVK: Alright, DVK. You think, it is a bit harsh. First impression is always the best one. I just failed to make the impression. So, I'm quiting from this SOF. This ain't the thing that a newbie expects, who is very new to Internet world, forum and community, etc stuffs. Such harsh comments. I believe I'm not good for all these stuffs. Good Bye every one! All the best... – Invincible Aug 11 '10 at 17:47

5 Answers5

3

<IFILE> reads one line from IFILE (only one because it's in scalar context). So while(<IFILE>) reads the first line, then the <IFILE> in list context within the while block reads the rest. What you want to do is:

# To read each line one by one:
while(!eof(IFILE)) { # check if end of file is reached instead of reading a line
  my $line = <IFILE>; # scalar context, reads only one line
  print OFILE $line;
}

# Or to read the whole file at once:
my @content = <IFILE>; # list context, read whole file
print OFILE @content;
jkramer
  • 15,440
  • 5
  • 47
  • 48
  • Thank you very much, JKramer. It works fine now. Could you suggest me some text book to learn more about Perl? – Invincible Aug 11 '10 at 10:19
  • @Invincible: [Learning Perl](http://learn.perl.org/books.html) is highly recommended (and one of the authors is even a regular contributor to Stack Overflow!). – Greg Hewgill Aug 11 '10 at 10:22
  • The only Perl book I ever read was [Effective Perl](http://www.amazon.com/Effective-Perl-Programming-Idiomatic-Development/dp/0321496949/ref=sr_1_1?ie=UTF8&s=books&qid=1281522398&sr=8-1), but I guess it's targeted at programmers who already know the language. – jkramer Aug 11 '10 at 10:27
  • Is it 'Learn Perl, Fifth Edition by Randal L. Schwartz, Tom Phoenix, brian d foy'? – Invincible Aug 11 '10 at 10:27
  • @Invincible Yes, that's the book @jkramer means. You might also look at *Beginning Perl* which is available in an earlier edition online for free: http://www.perl.org/books/beginning-perl/ – Telemachus Aug 11 '10 at 12:06
1

The problem is that this line...

while(<IFILE>)

...reads one line from text3.txt, and then this line...

@var=<IFILE>;

...reads ALL of the remaining lines from text3.txt.

You can do it either way, by looping with while or all at once with @var=<IFILE>, but trying to do both won't work.

Ed Guiness
  • 34,602
  • 16
  • 110
  • 145
1

This is how I would have written the code in your question.

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

# don't need to use "or die ..." when using the autodie module
open my $input,  '<', 'text3.txt';
open my $output, '>', 'text4.txt';

while(<$input>){
  my $lineno = $.;
  print {$output} $_;
}
# both files get closed automatically when they go out of scope
# so no need to close them explicitly

I would recommend always putting use strict and use warnings at the beginning of all Perl files. At least until you know exactly why it is recommended.

I used autodie so that I didn't have to check the return value of open manually. ( autodie was added to Core in version 5.10.1 )

I used the three argument form of open because it is more robust.

It is important to note that while (<$input>){ ... } gets transformed into while (defined($_ = <$input>)){ ... } by the compiler. Which means that the current line is in the $_ variable.

I also used the special $. variable to get the current line number, rather than trying to keep track of the number myself.

Brad Gilbert
  • 33,846
  • 11
  • 78
  • 129
0

When you're closing the files, use just

close(IFILE);
close(OFILE);

When you surround a file handle with angle brackets like <IFILE>, Perl interprets that to mean "read a line of text from the file inside the angle brackets". Instead of reading from the file, you want to close the actual file itself here.

Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
0

There is a couple of questions you might want to think about, if you are strictly copying a file you could use File::Copy module.

If you are going to process the input before writing it out, you might also consider whether you want to keep both files open at the same time or instead read the whole content of the first file (into memory) first, and then write it to the outfile.

This depends on what you are doing underneath. Also if you have a huge binary file, each line in the while-loop might end up huge, so if memory is indeed an issue you might want to use more low-level stream-based reading, more info on I/O: http://oreilly.com/catalog/cookbook/chapter/ch08.html

My suggestion would be to use the cleaner PBP suggested way:

#!/usr/bin/perl

use strict;
use warnings;
use English qw(-no_match_vars);

my $in_file  = 'text3.txt';
my $out_file = 'text4.txt';

open my $in_fh,  '<', $in_file  or die "Unable to open '$in_file': $OS_ERROR";
open my $out_fh, '>', $out_file or die "Unable to open '$out_file': $OS_ERROR";

while (<$in_fh>) {
    # $_ is automatically populated with the current line
    print { $out_fh } $_ or die "Unable to write to '$out_file': $OS_ERROR";
}

close $in_fh  or die "Unable to close '$in_file': $OS_ERROR";
close $out_fh or die "Unable to close '$out_file': $OS_ERROR";

OR just print out the whole in-file directly:

#!/usr/bin/perl

use strict;
use warnings;
use English qw(-no_match_vars);

my $in_file  = 'text3.txt';
my $out_file = 'text4.txt';

open my $in_fh,  '<', $in_file  or die "Unable to open '$in_file': $OS_ERROR";
open my $out_fh, '>', $out_file or die "Unable to open '$out_file': $OS_ERROR";

local $INPUT_RECORD_SEPARATOR; # Slurp mode, read in all content at once, see: perldoc perlvar

print { $out_fh } <$in_fh> or die "Unable to write to '$out_file': $OS_ERROR";;

close $in_fh  or die "Unable to close '$in_file': $OS_ERROR";
close $out_fh or die "Unable to close '$out_file': $OS_ERROR";

In addition if you just want to apply a regular expression or similar to a file quickly, you can look into the -i switch of the perl command: perldoc perlrun

perl -p -i.bak -e 's/foo/bar/g' text3.txt; # replace all foo with bar in text3.txt and save original in text3.txt.bak
nicomen
  • 1,183
  • 7
  • 16
  • It's probably worth mentioning that PBP stands for Damian Conway's *Perl Best Practices* http://oreilly.com/catalog/9780596001735/ – Telemachus Aug 11 '10 at 13:34
  • Thank you very very much for the explanation and example code. SOF is really helping. I should have used much earlier in my career. Thank you once again. – Invincible Aug 11 '10 at 15:37