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

Zol*_*ing 13 text-processing replace

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

\n
\\phrase\n{.   .    .     *     *   }\n{I shoul-d've stayed home.}\n{a\xc9\xaa \xca\x83\xca\x8ad\xe2\x80\xbf\xc9\x99v \xcb\x88ste\xc9\xaad \xcb\x88ho\xca\x8am.} <- only replace on this line\n\n\\phrase\n{ .   .   *  }\n{Did you eat?}\n{d\xc9\xaad\xca\x92j\xca\x8a\xca\xb7\xcb\x88it? \xe2\x86\x97} <- only replace on this line\n\n\\phrase\n{ *    .  *    .    *  .  .    .     *   .  }\n{Yeah, I made some pas-ta if you're hun-gry.}\n{\xcb\x88j\xc9\x9b\xc9\x99, a\xc9\xaa \xcb\x88me\xc9\xaad s\xc9\x99m \xcb\x88p\xc9\x91 st\xc9\x99\xca\xb7\xc9\xaaf j\xc9\x99r \xcb\x88h\xca\x8c\xc5\x8b gri.} <- only replace on this line\n
Run Code Online (Sandbox Code Playgroud)\n

这是一个 LaTeX.tex文件。我想用符号(十六r进制代码)替换每个音标中的所有字符(通过音标,我的意思是该\\phrase行之后的每三行)\xc9\xb9U+0279

\n

在 Emacs 中手动完成对我来说很麻烦。我想知道是否有一种方法可以以某种方式定位这些行并自动进行替换。

\n

所有r字符都要替换成\xc9\xb9,无一例外,但只是在拼音中,留下r英文/非音标文本保持原样。

\n

是否可以通过使用脚本或其他东西来做到这一点?我的文档中没有换行符,因此转录始终是之后的第三行\\phrase。谢谢你!

\n

Arc*_*mar 18

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

\n
awk '/\\\\phrase/ { p=NR ; } \n     NR == p+3 { gsub("r","\xc9\xb9")  ; } \n    {print;} ' old-file.tex > new-file.tex\n
Run Code Online (Sandbox Code Playgroud)\n

在哪里

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

这给了你的样品:(注意\xc9\xb9eplace

\n
\\phrase\n{.   .    .     *     *   }\n{I shoul-d've stayed home.}\n{a\xc9\xaa \xca\x83\xca\x8ad\xe2\x80\xbf\xc9\x99v \xcb\x88ste\xc9\xaad \xcb\x88ho\xca\x8am.} <- only \xc9\xb9eplace on this line\n\n\\phrase\n{ .   .   *  }\n{Did you eat?}\n{d\xc9\xaad\xca\x92j\xca\x8a\xca\xb7\xcb\x88it? \xe2\x86\x97} <- only \xc9\xb9eplace on this line\n\n\\phrase\n{ *    .  *    .    *  .  .    .     *   .  }\n{Yeah, I made some pas-ta if you're hun-gry.}\n{\xcb\x88j\xc9\x9b\xc9\x99, a\xc9\xaa \xcb\x88me\xc9\xaad s\xc9\x99m \xcb\x88p\xc9\x91 st\xc9\x99\xca\xb7\xc9\xaaf j\xc9\x99\xc9\xb9 \xcb\x88h\xca\x8c\xc5\x8b g\xc9\xb9i.} <- only \xc9\xb9eplace on this line\n
Run Code Online (Sandbox Code Playgroud)\n

  • 如果你改变`/\\phrase/ { p=NR ; } NR == p+3` 到 `/\\phrase/ { p=NR+3; } NR == p` 那么 a) 它会更高效,因为它只每 5 行而不是每行添加一次,并且 b) 它会更健壮,因为它不能如果文件的第三行不是以 `\phrase` 开头,则不需要更改该文件的第三行(例如,文件开头可能有一些标题行,idk) (2认同)

tha*_*isp 11

awk \'c&&!--c {gsub(/r/,"\xc9\xb9")} /\\\\phrase/ {c=3} 1\' file > newfile\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n

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

\n

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

\n


JoL*_*JoL 9

既然你使用的是 Emacs...

\n

邪恶/Vim 之道

\n

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

\n
:g/^\\\\phrase/+3s/r/\xc9\xb9/g\n
Run Code Online (Sandbox Code Playgroud)\n

这是最简单的。

\n

键盘宏方式

\n

继续使用现有的 Emacs,您可以使用键盘宏:C-x ( C-M-s ^\\\\phrase Enter C-n C-n C-n C-a C-space C-e C-M-% r Enter \xc9\xb9 Enter ! C-x ) C-u 2 C-x e

\n

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

\n

埃利普之路

\n

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

\n
:g/^\\\\phrase/+3s/r/\xc9\xb9/g\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n

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

\n
(with-current-buffer "foo"\n  (goto-char (point-min))\n  (while (re-search-forward "^\\\\\\\\phrase" nil t)\n    (forward-line 3)\n    (replace-string-in-region "r" "\xc9\xb9" (point) (line-end-position))))\n
Run Code Online (Sandbox Code Playgroud)\n

Shell命令过滤方式

\n

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

\n

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

\n


ter*_*don 7

如果每个部分之间总是有一个空行,则可以尝试 perl 的“段落”模式将每个部分作为单个“行”读取:

\n
$ perl -F'\\n' -00ane '$F[3]=~s/r/\xc9\xb9/g; print join "\\n",@F , "\\n"' file \n\\phrase\n{.   .    .     *     *   }\n{I shoul-d've stayed home.}\n{a\xc9\xaa \xca\x83\xca\x8ad\xe2\x80\xbf\xc9\x99v \xcb\x88ste\xc9\xaad \xcb\x88ho\xca\x8am.} <- only \xc9\xb9eplace on this line\n\n\\phrase\n{ .   .   *  }\n{Did you eat?}\n{d\xc9\xaad\xca\x92j\xca\x8a\xca\xb7\xcb\x88it? \xe2\x86\x97} <- only \xc9\xb9eplace on this line\n\n\\phrase\n{ *    .  *    .    *  .  .    .     *   .  }\n{Yeah, I made some pas-ta if you're hun-gry.}\n{\xcb\x88j\xc9\x9b\xc9\x99, a\xc9\xaa \xcb\x88me\xc9\xaad s\xc9\x99m \xcb\x88p\xc9\x91 st\xc9\x99\xca\xb7\xc9\xaaf j\xc9\x99\xc9\xb9 \xcb\x88h\xca\x8c\xc5\x8b g\xc9\xb9i.} <- only \xc9\xb9eplace on this line\n\n
Run Code Online (Sandbox Code Playgroud)\n

解释

\n
    \n
  • -a:自动将每个输入行分割到数组中@F

    \n
  • \n
  • -F'\\n': 按换行符分割。

    \n
  • \n
  • -00:“段落模式”,行现在由\\n\\n(空行)定义,因此每个部分都变成“行”。

    \n
  • \n
  • -ne:逐行读取输入文件并应用给出的脚本-e每一行。

    \n
  • \n
  • $F[3]=~s/r/\xc9\xb9/g;:将全部替换r\xc9\xb9数组的第 4 个元素@F(这是每个部分的第 4 行;数组从 0 开始)。

    \n
  • \n
  • print join "\\n",@F , "\\n"':用 加入修改后的@F数组\\n,然后将其与额外的内容一起打印\\n

    \n
  • \n
\n
\n

如果您不能依赖这一点并且需要在行匹配后始终选择第三行\\phrase,您可以执行以下操作:

\n
$ perl -pe '$k=0 if /\\\\phrase\\b/; $k++; s/r/\xc9\xb9/g if $k==4' file \n\\phrase\n{.   .    .     *     *   }\n{I shoul-d've stayed home.}\n{a\xc9\xaa \xca\x83\xca\x8ad\xe2\x80\xbf\xc9\x99v \xcb\x88ste\xc9\xaad \xcb\x88ho\xca\x8am.} <- only \xc9\xb9eplace on this line\n\n\\phrase\n{ .   .   *  }\n{Did you eat?}\n{d\xc9\xaad\xca\x92j\xca\x8a\xca\xb7\xcb\x88it? \xe2\x86\x97} <- only \xc9\xb9eplace on this line\n\n\\phrase\n{ *    .  *    .    *  .  .    .     *   .  }\n{Yeah, I made some pas-ta if you're hun-gry.}\n{\xcb\x88j\xc9\x9b\xc9\x99, a\xc9\xaa \xcb\x88me\xc9\xaad s\xc9\x99m \xcb\x88p\xc9\x91 st\xc9\x99\xca\xb7\xc9\xaaf j\xc9\x99\xc9\xb9 \xcb\x88h\xca\x8c\xc5\x8b g\xc9\xb9i.} <- only \xc9\xb9eplace on this line\n
Run Code Online (Sandbox Code Playgroud)\n

0这会在每次看到 时设置一个计数器\\phrase,并在每个新行上将其加一。然后,我们只在计数器的值为 4 时进行替换。

\n


Sté*_*las 6

与标准sed

\n
sed '/^\\\\phrase$/{n;n;n;s/r/\xc9\xb9/g;}'\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n

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

\n
sed $'/^\\\\\\\\phrase$/{n;n;n;s/r/\\u0279/g;}'\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n
\n

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

\n

  • @TobySpeight Ed的方式实际上是由JoL的答案提供的:`g/^\\phrase/+3s/r/ɹ/g`。虽然 Ed 命令本身确实比较简单,但付诸实践通常看起来有点麻烦(即 `printf '%s\n' 'g/^\\phrase/+3s/r/ɹ/g' wq | ed -s文件`)。 (2认同)