in reply to Changing string in specific line/position in a file
G'day onemojofilter,
Your data contains four types of whitespace (assuming your alignment uses tabs). These are difficult to differentiate on a webpage. I've used `cat -vet` which shows: a tab as ^I; a form feed as ^L; a newline as $; and, a space as itself.
I created the following test data (working.txt) which has examples of the different types of whitespace. I added an additional page so you now have a "^LH" following both an "H" and a "^LH". I removed much of the original data and replaced the rest with text intended to explain each line. I believe this is still representative of what you originally showed.
$ cat -vet working.txt H$ after 1 space$ after 2 spaces$ no leading spaces$ text^I^Ifollowed by 2 tabs$ target for^Iswap1$ ...$ source for^Ireplace1$ ...$ ^LH$ after 1 space$ after 2 spaces$ no leading spaces$ text^I^Ifollowed by 2 tabs$ target for^Iswap2$ ...$ source for^Ireplace2$ ...$ ^LH$ after 1 space$ after 2 spaces$ no leading spaces$ text^I^Ifollowed by 2 tabs$ target for^Iswap3$ ...$ source for^Ireplace3$ ...$
I then ran this code:
#!/usr/bin/env perl use strict; use warnings; use autodie; use constant { CHANGE_LINE => 4, SOURCE_LINE => 6, PAGE_SEP => "\n\fH\n", }; use File::Copy 'copy'; my $work_file = 'working.txt'; my $bu_file = 'working.txt.bu'; copy($work_file, $bu_file) or die "Can't 'copy($work_file, $bu_file)': $!"; { open my $in_fh, '<', $bu_file; open my $out_fh, '>', $work_file; local $/ = PAGE_SEP; while (<$in_fh>) { chomp; my @lines = split /\n/; my ($change_line, $source_line) = (CHANGE_LINE, SOURCE_LINE); ++$change_line, ++$source_line if $. == 1; my ($replace) = $lines[$source_line] =~ /(\S+)$/; $lines[$change_line] =~ s/\S+$/$replace/; print $out_fh join("\n", @lines), (eof($in_fh) ? "\n" : PAGE_SEP); } }
Here's the result:
$ cat -vet working.txt H$ after 1 space$ after 2 spaces$ no leading spaces$ text^I^Ifollowed by 2 tabs$ target for^Ireplace1$ ...$ source for^Ireplace1$ ...$ ^LH$ after 1 space$ after 2 spaces$ no leading spaces$ text^I^Ifollowed by 2 tabs$ target for^Ireplace2$ ...$ source for^Ireplace2$ ...$ ^LH$ after 1 space$ after 2 spaces$ no leading spaces$ text^I^Ifollowed by 2 tabs$ target for^Ireplace3$ ...$ source for^Ireplace3$ ...$
Note that all of the original whitespace is retained unaltered. I do recommend you make a backup; doing this within the code is easiest and won't be forgotten.
Your initial text positions were incorrect. "25 through 35" covers 11 characters, but "20221103" is only 8 characters. Also, I made the starting position 23 not 25, and that assumes that all of the preceding whitespace was actually spaces; if some, or all, were tabs, that would be a different number. Tabs are just a single character:
$ perl -E 'my $x = "|\t|"; say $x; say length $x;' | | 3
If the situation is more complex than suggested in your OP, let Perl do the counting for you. Bear in mind that your character positions may start at 1 (1st char. is at pos. 1) but Perl will count from zero. I've no idea what you might need, but this should give you some hints:
$ perl -E ' my $x = "PROMPT\t\tTO CHANGE"; my $len = length $x; my $from_index = rindex($x, "\t") + 1; my $from_pos = $from_index + 1; say $x; say "$from_pos through $len"; say substr $x, $from_index, $len - $from_index; say substr $x, $from_index; ' PROMPT TO CHANGE 9 through 17 TO CHANGE TO CHANGE
— Ken
|
|---|