我有一个大文件,其中包含以下形式的数百个英语短语:
\phrase
{. . . * * }
{I shoul-d've stayed home.}
{aɪ ʃʊd‿əv ˈsteɪd ˈhoʊm.} <- only replace on this line
\phrase
{ . . * }
{Did you eat?}
{dɪdʒjʊʷˈit? ↗} <- only replace on this line
\phrase
{ * . * . * . . . * . }
{Yeah, I made some pas-ta if you're hun-gry.}
{ˈjɛə, aɪ ˈmeɪd səm ˈpɑ stəʷɪf jər ˈhʌŋ gri.} <- only replace on this line
这是一个 LaTeX.tex
文件。我想用符号(十六进制代码)替换r
每个音标中的所有字符(通过音标,我的意思是每行后的第三行)。\phrase
ɹ
U+0279
在 Emacs 中手动操作对我来说很麻烦。我想知道是否有办法以某种方式定位这些行并自动进行替换。
所有r
字符都必须替换为ɹ
,也不例外,但仅在拼音r
中,在英文/非拼音文本中保持原样。
是否可以通过使用脚本或其他方式以某种方式做到这一点?我的文档中没有换行符,所以转录总是在\phrase
. 谢谢!
一个 awk 版本(你需要一个中继文件,你可以单行)
在哪里
/\\phrase/ { p=NR ; }
将设置为出现p
的每个行号\phrase
NR == p+3 { gsub("r","ɹ") ; }
之后在第 3 行执行替换{print;}
打印所有行。这给了你的样本:(注意
ɹeplace
)c&&!--c
是一个常见的awk
成语,实现while
getline
逻辑,见参考。只有在从 1 减到 0 时,才会执行此条件之后的操作。
匹配文字
'\phrase'
时,我们设置c=3
了 ,因此gsub()
将仅在匹配后的第 3 行执行,并且对所有匹配都重复。既然你在 Emacs 上...
邪恶/Vim 方式
如果你已经
evil-mode
安装(或者你切换到 Vim),你可以这样做:这是最简单的。
键盘宏方式
继续使用 Emacs,您可以使用键盘宏:
C-x ( C-M-s ^\\phrase Enter C-n C-n C-n C-a C-space C-e C-M-% r Enter ɹ Enter ! C-x ) C-u 2 C-x e
C-x (
启动宏,C-x )
结束宏,C-x e
运行宏,C-u 2
/C-2
修改C-x e
使其运行宏 2 次。C-u 10000
如果您不想数数,也可以使用大数字。C-M-s
搜索正则表达式。向下移动 3 行并选择行后,C-M-%
在选择中开始替换。在提示什么替换什么后,!
表示接受选择中的所有替换。Elisp 方式
您还可以打开
*scratch*
缓冲区并运行它(C-M-x
同时将光标放在代码上):where
foo
是您要执行此操作的缓冲区的名称。编辑:
replace-string-in-region
在 Emacs 28.1(写作时的最新版本)中引入。如果您的 Emacs 较旧,您可以使用search-forward
并replace-match
喜欢这样:Shell 命令过滤方式
您还可以通过外部命令过滤 Emacs 缓冲区,就像这里的其他答案之一:
C-x h C-u M-| <command> Enter
C-x h
选择整个缓冲区。M-|
将提示输入将过滤选择的命令。C-u
修改M-|
,因此它用输出替换选择,而不是将其放在临时缓冲区中。如果您总是在每个部分之间有一个空行,您可以尝试 perl 的“段落”模式将每个部分读为单个“行”:
解释
-a
: 将每个输入行自动拆分到数组@F
中。-F'\n'
: 在换行符处分割。-00
:“段落模式”,行现在由\n\n
(空行)定义,因此每个部分都成为“行”。-ne
:逐行读取输入文件并将给出的脚本-e
应用于每一行。$F[3]=~s/r/ɹ/g;
: 全部替换r
为ɹ
数组的第 4 个元素@F
(这是每个部分的第 4 行;数组从 0 开始)。print join "\n",@F , "\n"'
: 用 加入修改后的@F
数组\n
,然后将其与额外的\n
.如果您不能依赖它并且需要在 line matching 之后总是去第三行
\phrase
,您可以这样做:0
每次我们看到 时都会设置一个计数器\phrase
,并在每一行上将其递增一。然后,我们只在计数器的值为 4 时进行替换。带标准
sed
:y/r/ɹ/
代替s/r/ɹ/g
也可以在符合 POSIXsed
的实现中工作,前提是该ɹ
字符在用户的语言环境中被视为一个字符,但s/r/ɹ/g
更便携,因为它也适用于sed
不支持多字节字符的实现(如ɹ
UTF-8 ; 我找不到ɹ
在单个字节上编码的任何字符编码)。为了
ɹ
在用户的语言环境中正确编码zsh
,您可以这样做:在哪里
\u0279
可以扩展到ɹ
用户语言环境中该字符的编码¹¹
$'\uXXXX'
其他一些 shell 现在支持这一点,但请注意,在某些情况下,它会在区域设置中扩展,就像在 shell 启动或读取该行代码时一样,不一定sed
是在执行该命令的区域设置时. 在 ksh93 中,无论用户的语言环境如何,它总是以 UTF-8 扩展。当该字符在语言环境的字符集中不可用时,shell 之间的行为也会有所不同。它会导致错误zsh
相当直截了当;
$.
为 unicode 处理设置标志,如果我们看到,请记住行号 ( )\phrase
,如果行号大于该行号 3,则进行替换。由于我们得到了其他答案,因此这是一个几乎重复的问题的有效解决方案。这是针对 GNU
sed
的,但在链接的答案中也有 POSIX 建议:这样做是采用
\phrase
(bound to start-of-line) 并使用它和接下来的两行 (+3
,从匹配的行作为第一行开始)。对于该组的前两行,它不应用从r
to的替换ɹ
(这意味着对于该组的最后一行,它确实应用了替换)。示例的输出:
使用Raku(以前称为 Perl_6)
您可能想尝试 Raku,因为它是从头开始构建的以处理 Unicode。上面的代码(实际上)与@hobbs 发布的 Perl5 答案非常相似,因为它使用 Raku 的自动打印逐行
-pe
命令行标志,并从看到的行开始向下计数\phrase
。对于上面的代码,变量在程序开始时
$ph
是state
d 一次。由于文件是逐行读取的,因此$ph
设置为0
当一行包含\phrase
但没有遇到任何其他内容时(意思是++$ph
== 1 为真)。从这一点开始执行自动递增测试if ++$ph == 4
(倒数 3 行),如果满足,则指示替换操作员s:g/r/ɹ/
在:global
所需行内执行操作。$.
支持state
变量声明器和相关的匿名状态变量,例如$
、@
和%
. 根据文档, “state
声明词法范围的变量,就像my
。但是,初始化只发生一次......” . Raku 中的$
匿名状态变量可用于将行号添加到文本文件,即raku -ne 'put ++$ ~ " $_";'
]。请注意,由于 Raku 可以优雅地处理 Unicode,因此
s:g/r/ɹ/
可以轻松编写替换:s:g/r/\x0279/
或者
s:g/r/\c[Latin Small Letter Turned R]/
...当您遇到与字体/Unicode 相关的困难时(或者...如果您只是厌倦了尝试记住 Unicode 十六进制代码),上述描述性转换“Latin Small Letter Turned R”可能会有所帮助。
样本输出:
https://en.wikipedia.org/wiki/IPA_Extensions
https://docs.raku.org/syntax/state
https://raku.org