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

I have a log file with some numeric data like

abc.log 12 15 17

I want to update the value present at offset 3 i.e 15 in this case with a given value like 20. I had done something like this

write_log("12345"); sub write_log { my ($message) = @_; my $new_data = ''; my $header_fields_value = 0; my $length = length $message; open (MYFILE, '>>', 'abc.log'); $header_fields_value = read_line(3); $new_data = $header_fields_value+$length+2; $header_fields_value =~ s/$header_fields_value/$new_data/g; seek(MYFILE,3,0); print MYFILE $header_fields_value; close (MYFILE); } sub read_line { my ($offset) = @_; open (MYFILE1, '<', $filename); seek(MYFILE1,$offset,0); my $line = <MYFILE1>; close (MYFILE1); return($line); }

here it is reading the value stored at offset 3 using read_line function. But modified value is being appended at the end of the file. I need it to replace the line present at offset 3.

Please help me in understanding what am i doing wrong or if it could be done in some easier way.

Replies are listed 'Best First'.
Re: Replace a text in file at given offset with other text
by roboticus (Chancellor) on Feb 04, 2014 at 11:55 UTC

    tarun28jain:

    A couple items:

    • Your read_line function isn't returning a file position, so write_log won't be able to seek to the correct location. Instead, you're returning the contents of a line. If it happens to be a number, then you may be changing a line pretty far away from where you intend!
    • It looks like you're confusing file position (the index of the current byte in the file) with line number (how many lines have we read so far).
    • If you fix these problems, you'll find that if your new text is a different length than the original line, you'll either leave garbage in your file, or overwrite some of the next line(s).

    Typically, if you want to make an edit in the middle of the file, you'll want to rewrite the entire file, unless your substitution text has the same number of bytes as the text it's replacing. (Can be complicated, especially when Unicode is involved.)

    I'd suggest doing it a bit more like:

    open my $INF, '<', $filename; open my $OUF, '>', $filename . ".new"; my @lines = <$INF>; $lines[2] = "12345\n"; print $OUF @lines;

    However, reading the entire file into memory may be a problem, so you may want to process line-by-line instead.

    ...roboticus

    When your only tool is a hammer, all problems look like your thumb.

      Thanks for the reply. I got the solution to my specific problem from next reply

Re: Replace a text in file at given offset with other text
by DrHyde (Prior) on Feb 04, 2014 at 12:08 UTC

    '>>' opens the file in *append* mode, which is why your data gets appended. You need to open the file in read/write mode, seek() to the appropriate offset, and then write the replacement. Beware that the replacement must be exactly the same length as the original. Given a file with contents thus:

    15 20 25
    This code will replace the 20 with 94:
    use Fcntl qw(:seek); open(my $fh, '+<', 'foo'); seek($fh, 3, SEEK_SET); print $fh '94'; close($fh);

    If you can't guarantee that the original and replacement text are the same length, then read the file, use an l-value substr() on the data, and write it back out again. The example below replaces the two characters at position three in the string with *three* characters. Obviously you'll need to handle opening, reading and writing the file yourself, but that's trivial:

    $foo = "ab cd ef"; substr($foo, 3, 2) = "cat"; say $foo;

      Thanks for the reply. This exactly solved my problem

Re: Replace a text in file at given offset with other text
by petdance (Parson) on Feb 04, 2014 at 17:59 UTC
    Please note that the seek() function operates on a byte level, not a line level. You are saying "offset 3" which implies byte offset, but it sounds like you really mean "line number 3".

    xoxo,
    Andy