【问题标题】:Search and replace script on a given portion of text在文本的给定部分搜索和替换脚本
【发布时间】:2026-02-13 00:45:01
【问题描述】:

我正在处理文本文件,需要构建一个 perl 脚本以仅在给定文本部分(本例中为章节)的注释上运行搜索替换,以便我可以转换此模式:

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 text here(Note t)

进入这个:

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)

换句话说,我需要在每个新章节的开头将每个音符的“计数器”设置为“a”。以下正则表达式匹配每一章:

(?s)^\w{1,10} \d{1,3}:\d{1,3}.+?\(Note \w\).+?(?=\w{1,10} \d{1,3}:\d{1,3})

我曾尝试使用这样的 while 循环:

my @notes = ('Note a', 'Note b', 'Note c', 'Note d');
$Count = a;
foreach my $Marker (@notes) {
    $_=~s/(\\(Note\\))[a-z]/"$1".$Count++/e;
}

但是我被困住了,不可能想出一种方法来构建一个脚本,使其在每个章节划分处停止,然后重新开始直到结束。也许我使用了错误的方法?

我需要做什么才能将搜索和替换仅应用于每个章节,如上所示(即第一个正则表达式)?

任何帮助将不胜感激。 谢谢!

编辑(7 月 30 日)

两个答案都很好。我将第一个选为我最喜欢的,因为我更了解逻辑,但两者都同样有效。

现在,作为我第一个问题的推论。我怎样才能在每行之前轻松地依次包含章节名称和章节编号?像这样:

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)

我需要使用变量并增加它还是有更简单的方法?

【问题讨论】:

  • 发帖时不需要加<br>标签,直接回车换行即可。

标签: regex perl replace


【解决方案1】:

您应该将文本分成章节并单独处理。

# $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;
}

这将适用于您的示例。您只需要弄清楚如何处理超过 26 个音符 (a-z)。

编辑:这是您可以逐行读取文本并编写输出文件的方式:

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;
}

【讨论】:

  • 'z' 将在使用 inc 运算符时增加到 'aa'。 Perl 魔法。
  • @mhyfritz,是的,没错,尽管我认为任何一章都没有那么多注释。但是,我无法确定代码是否有效。我使用设置的 IO 文件,然后打印输出文件(或简单的 $_)。例如: open(Input, '+/rgp/Desktop/Test.txt') || die "没有找到这样的文件!"; while() { 你的代码在这里;打印 $_;如何将结果打印到我的输出文件(或 BBEdit 的 Unix 脚本输出文件)?谢谢!
  • 您可以简单地使用 shell 重定向来打印文件。 IE。 script.pl &gt; output.txt.
  • @TLP 我希望从 perl 脚本本身中执行此操作。这可以做到吗?如果是这样,怎么做?抱歉,现在是凌晨 2 点,我想不清楚...
  • open my $out, '&gt;', 'output.txt' or die $!; 在循环外,并将打印更改为print $out $chap
【解决方案2】:

你也可以不拆分,像这样:

s/^(chapter\s+\d+:\d+)|(\(note\s+)[a-z]+(?=\))/$a='a'if$1; $1?$1:$2.$a++/gime;

PS:别忘了use strict;use warnings;


基于 OP 评论的完整示例:

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;

【讨论】:

  • 在 Mac OS X (BBEdit) 中使用您的代码似乎对我不起作用。我错过了什么吗?这是完整的脚本: open(Input, '+/rgp/Desktop/Test.txt') || die "没有找到这样的文件!";使用严格;使用警告; while() { $_=~s/^(章节\s+\d+:\d+)|((note\s+)[a-z]+(?=))/$a='a'if$1; $1?$1:$2.$a++/gime;打印 $_; }
  • 这个正则表达式可以同时作用于整个文件,我已经更新了答案来告诉你如何使用它。
  • 非常感谢您不厌其烦地回答。我发现这个脚本的问题是它只更改了第 1 章,但保留了第 2 章中的所有注释以及后续内容。我最初的问题是关于整个过程的自动化,因为我处理的是包含数百章的非常大的文件。
  • 它适用于所有章节,至少在您的示例数据上。查看演示:ideone.com/pJaag
  • 你是绝对正确的。我取了一个实际文档的一部分(根据您的演示示例)并且它有效!但是对于我的一生,我无法使您上面的完整示例起作用。正如我之前所说,我正在使用 BBEdit。我在 Western Mac OS Roman 和 Classic Mac CR 文件(即 Test.txt)上运行脚本(Western Mac OS Roman 和 Unix LF)。到底哪里错了?