in reply to replacing string in multi-line text file
One of the problems you will run into is that there is no good way to insert a change into a text file without clobbering something. You can append. You can seek and write. But your records aren't fixed length, so just about any change you make will alter the number of bytes in a given record. This makes in-place editing tricky.
You have a few options. Some of them scale better than others. One option would be to read the file line by line, and simultaneously write a new file line by line, along with the changes you intend to make. After the files are closed, you can delete the original and rename the newly created file. That's a very common way of working with flat files. But it doesn't scale well. The bigger your file gets, the more work has to be done just to enact one change.
Another solution is to make each record in the file uniform length, and each field uniform length. That would allow you to treat the file more like a database. You could literally make a change at any point within the file, and as long as that change doesn't change the field size you're ok. But the problem with that is that you're basically implementing a database from scratch. And if you're going to do that, just go all the way and use something like SQLite.
There are a few other alternatives. You could tie your file to an array with Tie::File. That module allows for in-place editing where the dirty work of rewriting the file upon each edit is hidden behind a simple interface. This method suffers from scalability problems just like the first method, but ends up being quite simple to implement.
Here are examples of the line-by-line with a temp file approach, and of the Tie::File approach:
use Modern::Perl; open my $infh, '<', 'orders.txt'; open my $outfh, '>', 'orders.txt.tmp'; while( <$infh> ) { chomp; my @vals = split "\t"; my $status = $vals[19]; if( $vals[0] = 'trigger text' ) { $vals[19] = 'Delivered'; } print $outfh join("\t",@vals), "\n"; } close $infh; close $outfh; rename "orders.txt", "orders.txt.bak"; rename "orders.txt.tmp", "orders.txt";
Now for the Tie::File method:
use strict; use warnings; use Modern::Perl use Tie::File; tie my @lines, 'Tie::File', 'orders.txt' or die $!; foreach my $line ( @lines ) { chomp $line; my @vals = split "\t", $line; if( $vals[0] == 'trigger text' ) { $vals[19] = 'Delivered'; } $line = join( "\t", @vals ) . "\n"; } untie @lines;
Either of these methods should work fine (though you would have to adapt them to your specific situation). It's up to you which you consider easier. But if your file looks like it's going to grow bigger and bigger, you may seriously consider converting over to a database such as SQLite.
Dave
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re^2: replacing string in multi-line text file
by Anonymous Monk on Mar 25, 2011 at 14:23 UTC | |
by davido (Cardinal) on Mar 25, 2011 at 16:03 UTC | |
by Anonymous Monk on Mar 25, 2011 at 14:27 UTC | |
by Anonymous Monk on Mar 25, 2011 at 14:45 UTC | |
by Anonymous Monk on Mar 25, 2011 at 14:54 UTC |