Search and replace script on a given portion of text
I'm manipulating text files and need to build a perl script to run a search replace only on the notes of a given portion of text (chapters in this case), so that I can convert this pattern:
Chapter 1:1 text here(Note a) more text here(Note b)
2 text here(Note c) more text here(Note d)
3 text here(Note e) more text here(Note f)
4 text here(Note g) more text here(Note h)
Chapter 2:1 text here(Note i) more text here(Note j)
2 text here(Note k) more text here(Note l)
3 text here(Note m) more text here(Note n)
4 text here(Note o) more text here(Note p)
5 text here(Note q) more text here(Note r)
6 text here(Note s) more t开发者_Python百科ext here(Note t)
into this:
Chapter 1:1 text here(Note a) more text here(Note b)
2 text here(Note c) more text here(Note d)
3 text here(Note e) more text here(Note f)
4 text here(Note g) more text here(Note h)
Chapter 2:1 text here(Note a) more text here(Note b)
2 text here(Note c) more text here(Note d)
3 text here(Note e) more text here(Note f)
4 text here(Note g) more text here(Note h)
5 text here(Note i) more text here(Note j)
6 text here(Note k) more text here(Note l)
In other words, I need to set the 'counter' of each note to "a" at the start of each new chapter. The following regex matches every chapter:
(?s)^\w{1,10} \d{1,3}:\d{1,3}.+?\(Note \w\).+?(?=\w{1,10} \d{1,3}:\d{1,3})
and I have tried to use a while loop like this:
my @notes = ('Note a', 'Note b', 'Note c', 'Note d');
$Count = a;
foreach my $Marker (@notes) {
$_=~s/(\\(Note\\))[a-z]/"$1".$Count++/e;
}
But I am stuck and cannot possibly think of a way to build a script in such a way that it stops at every chapter division and then starts again until the end. Maybe I'm using the wrong approach?
What do I need to do in order to apply the search and replace only to each chapter, as shown above (i.e., the first regex)?
Any help will be greatly appreciated. Thanks!
EDIT (July 30)
Both answers work fine. I voted the first one as my favorite because I understand the logic better, but BOTH are equally valid.
Now, as a corollary to my first question. How could I easily include the chapter name and chapter number sequentially before each line? Like this:
Chapter 1:1 text here(Note a) more text here(Note b)
Chapter 1:2 text here(Note c) more text here(Note d)
Chapter 1:3 text here(Note e) more text here(Note f)
Chapter 1:4 text here(Note g) more text here(Note h)
Chapter 2:1 text here(Note a) more text here(Note b)
Chapter 2:2 text here(Note c) more text here(Note d)
Chapter 2:3 text here(Note e) more text here(Note f)
Chapter 2:4 text here(Note g) more text here(Note h)
Chapter 2:5 text here(Note i) more text here(Note j)
Chapter 2:6 text here(Note k) more text here(Note l)
Do I need to use a variable and increment it or is there a simpler approach?
You should split your text into chapters and process those individually.
# $book holds your text
my @chapters = split /^(?=Chapter\s+\d+:\d+)/m, $book;
for my $chap (@chapters) {
my $cnt = 'a';
$chap =~ s/(?<=\(Note )[a-z]/$cnt++/ge;
print $chap;
}
This will work for your example. You just need to figure out how to handle more than 26 notes (a-z).
EDIT: This is how you can read in your text line by line and write an output file:
open IN, 'infile.txt';
open OUT, '>', 'outfile.txt';
my $cnt;
for my $line (<IN>) {
$cnt = 'a' if $line =~ /^Chapter\s+\d+:\d+/;
$line =~ s/(?<=\(Note )[a-z]/$cnt++/ge;
print OUT $line;
}
You could also do it without splitting, like this:
s/^(chapter\s+\d+:\d+)|(\(note\s+)[a-z]+(?=\))/$a='a'if$1; $1?$1:$2.$a++/gime;
PS: Don't forget to use strict;
and use warnings;
.
Full example based on OPs comment:
use strict;
use warnings;
open my $fh, '<', '/Users/rgp/Desktop/Test.txt' or die "cant open file: $!";
my $content = do { local $/ = undef; <$fh> };
close $fh;
$content =~ s/^(chapter\s+\d+:\d+)|(\(note\s+)[a-z]+(?=\))/$a='a'if$1; $1?$1:$2.$a++/gime;
print "Result:\n";
print $content;
精彩评论