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

I have two files. File A and File B. Both are text files. I need to copy File B completely into File A at a particular location in File A which is right after the following line in File A: *** Initial Temperature
  • Comment on Writing in at a certain location in a file

Replies are listed 'Best First'.
Re: Writing in at a certain location in a file
by Zaxo (Archbishop) on Jan 26, 2005 at 06:45 UTC

    It's surprisingly hard to do that right. Not from the perl, but from the need to lock the file you're writing, and to avoid overwriting pieces of it.

    I'm going to assume that the files are large enough that you won't want to slurp the whole thing into memory.

    #!/usr/bin/perl use warnings; use strict; use Fcntl qw/:DEFAULT :flock/; my $fileA = '/path/to/file_a.txt'; my $fileB = '/path/to/file_b.txt'; my $fileC = '/path/to/file_a.bak'; my $tagline = qr/\QFile A: *** Initial Temperature/; sysopen my $fc, $fileC, O_WRONLY | O_EXCL or die 'Competing process: ', $!; flock $fc, LOCK_EX | LOCK_NB or die 'Competing process: ', $!; open my $fa, '+<', $fileA or die $!; flock $fa, LOCK_EX; while (<$fa>) { print $fc $_; last if /^$tagline/; } open my $fb, '<', $fileB or unlink $fileC and die $!; print $fc $_ while <$fb>; close $fb or unlink $fileC and die $!; print $fc $_ while (<$fa>); close $fc or unlink $fileC and die $!; # reverse the order of the next two statements if # your system won't clobber the open file rename $fileC, $fileA; close $fa;
    I use sysopen there to get the exclusive file open mode. If $fileC exists then there is an instance of your script working already (don't think it can't happen ;-). That's why I'm careful to delete $fileC before an error exit.

    It's not easier to do if the files are small enough to slurp, just different. You'd then have the option of doing without the temporary file, but you'd have to seek to the beginning of $fa to write the new content. Inserting the new content would just be a string operation then, with lots of ways to do it.

    Update: Modified the fail actions for opening and locking the temporary file. If there is a competing process or file, we want to stop and let the operator decide what to do.

    After Compline,
    Zaxo

Re: Writing in at a certain location in a file
by K_M_McMahon (Hermit) on Jan 26, 2005 at 05:57 UTC
    Is this what you are looking for?
    open(A,"<$file_a") or die "I cannot open $file_a to read\n"; open(B,"<$file_b") or die "I cannot open $file_b to read\n"; @a=<A>; @b=<B>; close(A); close(B); foreach my $line (@a) { chomp($line); push(@new,$line); if ($line=~m/\*{3} Initial Temperature/) { foreach my $line (@b) { chomp($line); push(@new,$line); } } } open (NEW,">$file_a") or die "I cannot open $file_a to write\n"; foreach my $line (@new) { print NEW "$line\n"; }

      Thank you Kevin, I am going to try this today and let you know if it worked. Thanks again
Re: Writing in at a certain location in a file
by ysth (Canon) on Jan 26, 2005 at 08:54 UTC
    The only tricky part is that you need to stash away at least the part of File A after *** Initial Temperature somewhere so when you write file B there you don't lose it. The easiest code to do this is just to read all of file A into memory. If it is too big, you would rename the file and create a new file A instead. Alternatively, modules like Tie::File can hide some of the details from you.