如何替换整个文件每一行中特定上下文中的字符?

如何替换整个文件每一行中特定上下文中的字符?

我有一个大文件,其中包含数百个以下形式的英语短语:

\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。谢谢你!

答案1

awk 版本(您需要一个中继文件,您可以将其一行)

awk '/\\phrase/ { p=NR ; } 
     NR == p+3 { gsub("r","ɹ")  ; } 
    {print;} ' old-file.tex > new-file.tex

在哪里

  • /\\phrase/ { p=NR ; }将设置为出现的p每个行号\phrase
  • NR == p+3 { gsub("r","ɹ") ; } 之后在第 3 行执行替换
  • {print;}打印所有行。

这给了你的样品:(注意ɹeplace

\phrase
{.   .    .     *     *   }
{I shoul-d've stayed home.}
{aɪ ʃʊd‿əv ˈsteɪd ˈhoʊm.} <- only ɹeplace on this line

\phrase
{ .   .   *  }
{Did you eat?}
{dɪdʒjʊʷˈit? ↗} <- only ɹeplace 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əɹ ˈhʌŋ gɹi.} <- only ɹeplace on this line

答案2

awk 'c&&!--c {gsub(/r/,"ɹ")} /\\phrase/ {c=3} 1' file > newfile

c&&!--c是一个常见的awk习惯用法,实现while getline逻辑,请参见参考

仅当从 1 减到 0 时才会执行此条件后的操作。

当匹配文字时'\phrase',我们设置c=3,因此gsub()只会在匹配后的第三行执行,并且这对所有匹配都重复。

答案3

既然你使用的是 Emacs...

邪恶/Vim 之道

如果您已经evil-mode安装(或者切换到 Vim),您可以执行以下操作:

:g/^\\phrase/+3s/r/ɹ/g

这是最简单的。

键盘宏方式

继续使用现有的 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-%开始选择替换。提示什么替换什么后,!表示接受选择中的所有替换。

埃利普之路

您还可以打开*scratch*缓冲区并运行它(C-M-x将光标放在代码上):

(with-current-buffer "foo"
  (goto-char (point-min))
  (while (re-search-forward "^\\\\phrase" nil t)
    (forward-line 3)
    (replace-string-in-region "r" "ɹ" (point) (line-end-position))))

其中foo是您要执行此操作的缓冲区的名称。

编辑:replace-string-in-region在 Emacs 28.1(撰写时的最新版本)中引入。如果您的 Emacs 较旧,您可以使用search-forwardreplace-match来代替:

(with-current-buffer "foo"
  (goto-char (point-min))
  (while (re-search-forward "^\\\\phrase" nil t)
    (forward-line 3)
    (while (search-forward "r" (line-end-position) t)
      (replace-match "ɹ"))))

Shell命令过滤方式

您还可以通过外部命令过滤 Emacs 缓冲区,就像此处的其他答案之一:C-x h C-u M-| <command> Enter

C-x h选择整个缓冲区。M-|将提示输入将过滤选择的命令。C-u修改M-|,以便用输出替换选择,而不是将其放入临时缓冲区中。

答案4

与标准sed

sed '/^\\phrase$/{n;n;n;s/r/ɹ/g;}'

y/r/ɹ/代替s/r/ɹ/g也可以在 POSIX 兼容的sed实现中工作,只要该ɹ字符被视为用户区域设置中的字符,但 s/r/ɹ/g会更便携,因为它也可以与sed不支持多字节字符的实现一起工作(如ɹUTF-8 中的情况) ;我找不到任何ɹ在单个字节上编码的字符编码)。

为了ɹ在用户的区域设置中正确编码,zsh您可以这样做:

sed $'/^\\\\phrase$/{n;n;n;s/r/\u0279/g;}'

它将扩展到 用户区域设置中\u0279该字符的编码ɹ


¹$'\uXXXX'现在,其他一些 shell 也支持这一点,但请注意,在某些 shell 中,它会在语言环境中展开,因为它是在 shell 启动时或读取该行代码时进行的,而不一定是在执行该sed命令的语言环境中进行的。在 ksh93 中,无论用户的区域设置如何,它始终以 UTF-8 扩展。当该字符在区域设置的字符集中不可用时,不同 shell 的行为也会有所不同。它会导致错误zsh

相关内容