我想将文本的注释转换为脚注的形式。这是文本的最小示例。
第一段。这是第一段的第一个[1]。这是第一段的第二位[2]。
[1]注释第一段
[2]第一段注释
第二段。这是第二段的第一个[1]。这是第二段的第二个[2]。
[1]注释第二段之一
[2]注释第二段中的两个
在每个段落的末尾,将有几个注释以标签[1]开头。每个注释将形成一个段落。
我想要做的是使用latex语法将这些注释插入到文本中。样本文本的所需输出是
第一段。这是第一段的第一个 \ footnote {第一段的注释} 。这是第一段的第二个 \ footnote {第二段注释} 。
第二段。这是第二段的第一个 \ footnote {第二段注释} 。这是第二段的第二个 \ footnote {注释第二段} 。
这不仅仅是匹配模式的简单替换。它可能必须以段落为基础执行。您认为最简单的方法是什么?
编辑:我想出了一个可能的解决方案,以便使用sed。
删除注释前面的换行符
第一段。这是第一段的第一个[1]。这是第一段的第二位[2]。 [1]注释第一段[2]注释第一段中的第二段
第二段。这是第二段的第一个[1]。这是第二段的第二个[2]。 [1]注释第2段[2]注释第2段第2段
匹配模式
[1] text1 [1] text2 [2]
并将其替换为
text2 text1 [2]
基本上第一个[1]是应该插入注释的地方; [1]和[2]之间的东西是要重新定位的注释。
这些问题是相关的:Remove new line / line break characters only for specific lines How can I remove a line-feed/newline BEFORE a pattern using sed,但我无法让这些代码对我缺乏正则表达式的知识。
答案 0 :(得分:1)
从根本上说,sed
是这项工作的错误工具。您可以编写一个预处理文件的sed
脚本,并生成一个处理该文件的新sed
脚本,但是当有许多更好的工具可以完成任务时,您就会抓住这些脚本。我可以使用Perl(但是我在二十年前学过Perl,几年前才学习Python),但是Python也能够处理它,并且小心你甚至可以使用awk
。部分问题是你必须保存第一段的所有文本,直到你到达第二段的开头;只有这样你才能开始为第一段生成实际文本。
我认为'sed
是错误的工具'注释仍然有效,即使sed
脚本捕获了保留空间中段落的内容。那些线条不是以方括号开头的。麻烦的是,当你来到一个带方括号的行时,你需要编写一个正则表达式,用线条的尾部代替方括号的内容代替保留空间。这需要一种“动态正则表达式”。即使你知道段落中的内容永远不会超过9个脚注,所以你可以考虑将代码写入9次的某种黑客攻击,但仍然存在在正确的位置编写替换字符串的问题。 / p>
这是Perl中的一个简单脚本 - 好吧,Perl中一个并不是非常复杂的脚本 - 可以完成这项工作。 “旋转循环”(三个嵌套循环)使得理解起来有点棘手。
#!/usr/bin/env perl
use strict;
use warnings;
my $para = "";
TEXT:
while (<>)
{
NOTES:
while (m/^\s*\[(\d+)]\s+(.*)/)
{
my $tag = $1;
my $note = $2;
$para =~ s/\[$tag]/\\footnote{$note}/m;
while (<>)
{
last if $_ =~ m/^\s*\[/;
if ($_ !~ m/^\s*$/)
{
print $para;
$para = "";
last NOTES;
}
}
last TEXT if eof;
}
$para .= $_;
}
print "$para";
给定输入文件:
Paragraph one. This is the first place [1] of paragraph one. This is the second place [2] of paragraph one.
[1] annotation one of paragraph one
[2] annotation two of paragraph one
Paragraph two. This is the first place [1] of paragraph two. This is the second place [2] of paragraph two.
[1] annotation one of paragraph two
[2] annotation two of paragraph two
该文件中该脚本的输出是:
Paragraph one. This is the first place \footnote{annotation one of paragraph one} of paragraph one. This is the second place \footnote{annotation two of paragraph one} of paragraph one.
Paragraph two. This is the first place \footnote{annotation one of paragraph two} of paragraph two. This is the second place \footnote{annotation two of paragraph two} of paragraph two.
脚本有什么作用?
外部循环(标记为TEXT
)将行读入$_
直到EOF。
标记为NOTES
的循环处理段落后的材料直到下一个段落的开头。它知道它是一个脚注线,因为它以方括号中的数字开头(可能用空格缩进,并且在紧密的方括号后面肯定有空格)。当找到这样一行时,该数字将保存在$tag
中,并且替换文本(必须是单行 - 此处没有扩展的多行脚注)保存在$note
中。然后在保存的段落中方括号内的第一次出现的标记被替换为脚注符号和注释的文本(这是在sed
的单次运行中几乎不可能的部分,并给出脚注数字在段落中重复,甚至两次运行sed
有问题。完成了替换(如果没有替换匹配则不关心),它会读取下一行,这就是循环(和头部)开始旋转的地方。如果新读取的行是注释行,则初始last
退出最内层while
并返回NOTES
循环的下一次迭代。如果该行与空白行不匹配,那么我们必须刚刚阅读下一段的第一行,所以打印上一段(现在有多个替换要进行替换),清空已保存的段落,并退出NOTES
循环。否则,请忽略音符中间的空白行。
在循环之后,检查我们是否有EOF并且如果我们这样做则退出主循环。否则,将刚刚读取的段落行添加到已保存的段落中。
最后,打印最后保存的段落。
这尚未经过详尽的测试。我没有生成段落引用缺失的注释,或没有引用的注释,或不按顺序的注释。我认为它会通过忽视这些问题来“处理”它们;仍然存在对缺失音符的引用,并且未引用的音符将不会显示在输出中。如果相同的音符编号参考在段落中出现两次,但段落后只有一个音符编号,则忽略第二个和后续的音符编号。如果相同的音符编号出现两次('text [1] more [1]')并且段落后的音符重复编号('[1]音符1A','[1]音符1B'),则第一个将用'note 1A'代替,第二个用'note 1B'代替。我没有测试多行段落(但我不指望麻烦)。替换正则表达式不需要多行限定符,因为对标记的引用不能在行上分割,也不能锚定在一行上。
处理多行脚注是读者的练习(并非完全无关紧要)。除此之外,在找到空行,另一个脚注行或下一段的开头之前,您无法开始替换多行脚注。
答案 1 :(得分:0)
更简洁(并且记录较少)的perl版本
perl -00 -pe '
@markers = m{(\[\d+\])}g;
for $i (0..$#markers) {
$footnote = <>;
($marker, $text) = $footnote =~ m{(\[\d+\])\s+(.*)};
s{\Q$marker\E}{\\footnote{$text}};
}
' file
这假定如果一个段落中有5个脚注标记,则该段落将跟随5个脚注。