Re: FAQ 5.2 (Was: inserting lines)



Jim Gibson <jgibson@xxxxxxxxxxxxxxxxx> wrote:

I recently complained about the lack of alternate approaches to the FAQ
"How do I change one line in a file/delete a line in a file/insert a
line in the middle of a file/append to the beginning of a file?"

The official FAQ answer is:

"Use the Tie::File module, which is included in the standard distribu-
tion since Perl 5.8.0."


Here is my first attempt at a more complete answer. Comments are
invited.


You should probably review the answer that was there before 5.8.0
(which I liked better than what we have now).

Here is the POD from 5.6.1 for easy reference (=head2 line wrapped by me):

------------------------------------------------------
=head2 How do I change one line in a file/delete a line in a file/
insert a line in the middle of a file/append to the beginning of a file?

Those are operations of a text editor. Perl is not a text editor.
Perl is a programming language. You have to decompose the problem into
low-level calls to read, write, open, close, and seek.

Although humans have an easy time thinking of a text file as being a
sequence of lines that operates much like a stack of playing cards--or
punch cards--computers usually see the text file as a sequence of bytes.
In general, there's no direct way for Perl to seek to a particular line
of a file, insert text into a file, or remove text from a file.

(There are exceptions in special circumstances. You can add or remove
data at the very end of the file. A sequence of bytes can be replaced
with another sequence of the same length. The C<$DB_RECNO> array
bindings as documented in L<DB_File> also provide a direct way of
modifying a file. Files where all lines are the same length are also
easy to alter.)

The general solution is to create a temporary copy of the text file with
the changes you want, then copy that over the original. This assumes
no locking.

$old = $file;
$new = "$file.tmp.$$";
$bak = "$file.orig";

open(OLD, "< $old") or die "can't open $old: $!";
open(NEW, "> $new") or die "can't open $new: $!";

# Correct typos, preserving case
while (<OLD>) {
s/\b(p)earl\b/${1}erl/i;
(print NEW $_) or die "can't write to $new: $!";
}

close(OLD) or die "can't close $old: $!";
close(NEW) or die "can't close $new: $!";

rename($old, $bak) or die "can't rename $old to $bak: $!";
rename($new, $old) or die "can't rename $new to $old: $!";

Perl can do this sort of thing for you automatically with the C<-i>
command-line switch or the closely-related C<$^I> variable (see
L<perlrun> for more details). Note that
C<-i> may require a suffix on some non-Unix systems; see the
platform-specific documentation that came with your port.

# Renumber a series of tests from the command line
perl -pi -e 's/(^\s+test\s+)\d+/ $1 . ++$count /e' t/op/taint.t

# form a script
local($^I, @ARGV) = ('.orig', glob("*.c"));
while (<>) {
if ($. == 1) {
print "This line should appear at the top of each file\n";
}
s/\b(p)earl\b/${1}erl/i; # Correct typos, preserving case
print;
close ARGV if eof; # Reset $.
}

If you need to seek to an arbitrary line of a file that changes
infrequently, you could build up an index of byte positions of where
the line ends are in the file. If the file is large, an index of
every tenth or hundredth line end would allow you to seek and read
fairly efficiently. If the file is sorted, try the look.pl library
(part of the standard perl distribution).

In the unique case of deleting lines at the end of a file, you
can use tell() and truncate(). The following code snippet deletes
the last line of a file without making a copy or reading the
whole file into memory:

open (FH, "+< $file");
while ( <FH> ) { $addr = tell(FH) unless eof(FH) }
truncate(FH, $addr);

Error checking is left as an exercise for the reader.
------------------------------------------------------


--
Tad McClellan SGML consulting
tadmc@xxxxxxxxxxxxxx Perl programming
Fort Worth, Texas
.



Relevant Pages

  • perl.beginners Weekly list FAQ posting
    ... beginners-faq - FAQ for the beginners mailing list ... - How can I get this FAQ? ... A list for beginning Perl programmers to ask questions in a friendly ... an informed decision if a script is one you should be using, ...
    (perl.beginners)
  • perl.beginners Weekly list FAQ posting
    ... beginners-faq - FAQ for the beginners mailing list ... - How can I get this FAQ? ... A list for beginning Perl programmers to ask questions in a friendly ... an informed decision if a script is one you should be using, ...
    (perl.beginners)
  • Re: Trouble running a unix command with a subroutine call
    ... the most infamous and most imaginative Perl programmer ever! ... During all my years of Perl programming, not once have I used subroutinesyntax. ... I find the author of this FAQ to be intellectually insulting by his indicating ...
    (comp.lang.perl.misc)
  • Weekly list FAQ posting
    ... - I don't see something in the FAQ, how can I make a suggestion? ... Casey West owns the beginners list. ... A list for beginning Perl programmers to ask questions in a friendly ... - How do I determine whether a script I download is a good one? ...
    (perl.beginners)
  • Weekly list FAQ posting
    ... - I don't see something in the FAQ, how can I make a suggestion? ... Casey West owns the beginners list. ... A list for beginning Perl programmers to ask questions in a friendly ... - How do I determine whether a script I download is a good one? ...
    (perl.beginners)