http://qs1969.pair.com?node_id=11123972


in reply to Script fails to insert text and appends it towards the end of file.

However, my script is simply appending the text to the output file (after counting 11 lines from the end).

I initially couldn't reproduce this, but managed when running the script on Windows and the files having LF (not CRLF) line endings. Using tie @records, 'Tie::File', "output.fem", recsep=>"\n"; makes it work again. (See also the module's _default_recsep.)

However, I would not implement this script this way, since it requires you to know the correct place to insert the lines beforehand, and it requires there to be enough space for the lines to be inserted, otherwise data will be overwritten. The following uses a state machine pattern, and has the advantages that it does a bit of verification on the input file (it checks that there's only one section of NODE lines) and that it doesn't care how many lines both the input files have. Also, it won't have the above issue, because normal opens on Windows will normally do the right thing with line endings. Plus Tie::File adds significant overhead to the program.

(Update: Shameless plug: If you want to overwrite the input file, then see my module File::Replace for an easier/safer way to do so.)

use warnings; use strict; my $DATAFILE = 'data.txt'; my $INSERTFILE = 'insert.txt'; my $OUTFILE = 'output.txt'; open my $ofh, '>', $OUTFILE or die "$OUTFILE: $!"; select($ofh); # "print"s will go to this handle now open my $dfh, '<', $DATAFILE or die "$DATAFILE: $!"; my $state = 'before nodes'; while ( my $line = <$dfh> ) { if ( $line =~ /^\s*NODE\b/ ) { if ( $state eq 'before nodes' ) { $state = 'in nodes'; } elsif ( $state eq 'in nodes' ) { } elsif ( $state eq 'after nodes' ) { die "expected only one section of NODEs" } else { die $state } } else { if ( $state eq 'before nodes' ) { } elsif ( $state eq 'in nodes' ) { do_node_insert($INSERTFILE); $state = 'after nodes'; } elsif ( $state eq 'after nodes' ) { } else { die $state } } print $line; } close $dfh; sub do_node_insert { my $file = shift; open my $ifh, '<', $file or die "$file: $!"; while ( my $line = <$ifh> ) { next unless $line =~ /^\s*NODE\b/; print $line; } close $ifh; }

data.txt:

'Node ID X Y Z BC NODE 1 4.51000 0.00000 79.00000 NODE 2 0.00000 0.00000 79.00000 NODE 3 0.00000 0.00000 78.27000 NODE 4 -1.88000 0.00000 78.27000 'Elem ID np1 np2 material geom lcoor ecc1 BEAM 1 2 1 2 36 2 BEAM 2 3 2 2 36 1 PIPE 19 4.489 0.022 PIPE 20 4.488 0.021

insert.txt:

NODE 32 0.00000 0.00000 -1.90000 NODE 33 0.00000 0.00000 -5.50000 BEAM 26 27 26 1 14 1

output.txt:

'Node ID X Y Z BC NODE 1 4.51000 0.00000 79.00000 NODE 2 0.00000 0.00000 79.00000 NODE 3 0.00000 0.00000 78.27000 NODE 4 -1.88000 0.00000 78.27000 NODE 32 0.00000 0.00000 -1.90000 NODE 33 0.00000 0.00000 -5.50000 'Elem ID np1 np2 material geom lcoor ecc1 BEAM 1 2 1 2 36 2 BEAM 2 3 2 2 36 1 PIPE 19 4.489 0.022 PIPE 20 4.488 0.021

Replies are listed 'Best First'.
Re^2: Script fails to insert text and appends it towards the end of file.
by always_coys (Novice) on Nov 21, 2020 at 14:53 UTC

    Dear haukex

    Thanks a lot for the wonderful explanation. Adding recsep=>"\n" has solved my problem and this is enough, in the first pass (as I know the line numbers to insert the text, beforehand). However, eventually I intent to use the state machine pattern, or to overwrite the file in each iteration. Thank you for the pointers and for thinking way ahead of me! always_coys.