开发者

perl: Writing file at Nth position

I am trying to write in to file at Nth POSITION. I have tried with below example but it writes at the end.开发者_运维知识库 Please help to achieve this.

#!/usr/bin/perl

open(FILE,"+>>try.txt")
or
die ("Cant open file try.txt");

$POS=5;

   seek(FILE,$POS,0);

   print FILE "CP1";


You are opening the file in read-write appending mode. Try opening the file in read-write mode:

my $file = "try.txt";
open my $fh, "+<", $file
    or die "could not open $file: $!";

Also, note the use of the three argument open, the lexical filehandle, and $!.

#!/usr/bin/perl

use strict;
use warnings;

#create an in-memory file
my $fakefile = "1234567890\n";
open my $fh, "+<", \$fakefile
    or die "Cant open file: $!";

my $offset = 5;

seek $fh, $offset, 0
    or die "could not seek: $!";

print $fh "CP1";

print $fakefile;

The code above prints:

12345CP190


If I understand you correctly, if the file contents are

123456789

you want to change that to

1234CP157689

You cannot achieve that using modes supplied to open (regardless of programming language).

You need to open the source file and another temporary file (see File::Temp. Read up to the insertion point from the source and write the contents to the temporary file, write what you want to insert, then write the remainder of the source file to the temporary file, close the source and rename the temporary to the source.

If you are going to do this using seek, both files must be opened in binary mode.

Here is an example using line oriented input and text mode:

#!/usr/bin/perl

use strict; use warnings;
use File::Temp qw( :POSIX );

my $source = 'test.test';
my $temp = tmpnam;

open my $source_h, '<', $source
    or die "Failed to open '$source': $!";

open my $temp_h, '>', $temp
    or die "Failed to open '$temp' for writing: $!";

while ( my $line = <$source_h> ) {
    if ( $line =~ /^[0-9]+$/ ) {
        $line = substr($line, 0, 5) . "CP1" . substr($line, 5);
    }
    print $temp_h $line;
}

close $temp_h
    or die "Failed to close '$temp': $!";

close $source_h
    or die "Failed to close '$source': $!";

rename $temp => $source
    or die "Failed to rename '$temp' to '$source': $!";


this works for me

use strict;
use warnings;

open( my $fh, '+<', 'foo.txt' ) or die $!;

seek( $fh, 3, 0 );

print $fh "WH00t?";

this is also a more "modern" use of open(), see http://perldoc.perl.org/functions/open.html

The file will be closed when $fh goes out of scope ..


"Inserting" a string into a function can (mostly) be done in place. See the lightly used truncate built-in function.

open my $fh, '+<', $file or die $!;
seek $fh, 5, 0;
$/ = undef;
$x = <$fh>;   # read everything after the 5th byte into $x
truncate $fh, 5;
print $fh "CPI";
print $fh $x;
close $fh;


If your file is line or record oriented, you can insert lines or modify individual lines easily with the core module Tie::File This will allow the file to be treated as an array and Perl string and array manipulation to be used to modify the file in memory. You can safely operate on huge files larger than your RAM with this method.

Here is an example:

use strict; use warnings;
use Tie::File;

#create the default .txt file:
open (my $out, '>', "nums.txt") or die $!;
while(<DATA>) { print $out "$_"; }
close $out or die $!;

tie my @data, 'Tie::File', "nums.txt" or die $!;

my $offset=5;
my $insert="INSERTED";

#insert in a string: 
$data[0]=substr($data[0],0,$offset).$insert.substr($data[0],$offset) 
    if (length($data[0])>$offset);

#insert a new array element that becomes a new file line:       
splice @data,$offset,0,join(':',split(//,$insert));

#insert vertically: 
$data[$_]=substr($data[$_],0,$offset) .
          substr(lc $insert,$_,1) .
          substr($data[$_],$offset) for (0..length($insert));

untie @data; #close the file too...

__DATA__
123456789
234567891
345678912
456789123
567891234
678912345
789123456
891234567
912345678

Output:

12345iINSERTED6789
23456n7891
34567s8912
45678e9123
56789r1234
I:N:St:E:R:T:E:D
67891e2345
78912d3456
891234567
912345678

The file modifications with Tie::File are made in place and as the array is modified. You could use Tie::File just on the first line of you file to modify and insert as you requested. You can put sleep between the array mods and use tail -n +0 -f on the file and watch the file change if you wish...

Alternatively, if your file is reasonable size and you want to treat it like characters, you can read the entire file into memory, do string operations on the data, then write the modified data back out. Consider:

use strict; use warnings;

#creat the default .txt file:
open (my $out, '>', "nums.txt") or die $!;
while(<DATA>) { print $out "$_"; }
close $out or die $!;

my $data;
open (my $in, '<', "nums.txt") or die $!;
{ local $/=undef; $data=<$in>; }
close $in or die $!;

my $offset=5;
my $insert="INSERTED";

open (my $out, '>', "nums.txt") or die $!;
print $out substr($data,0,$offset).$insert.substr($data,$offset);
close $out or die $!;

__DATA__
123456789
2
3
4
5
6
7
8
9

Output:

12345INSERTED6789
2
3
4
5
6
7
8
9

If you treat files as characters, beware that under Windows, files in text mode have a \r\n for a new line. That is two characters if opened in binary mode.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜