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\xb9
U+0279
。
在 Emacs 中手动完成对我来说很麻烦。我想知道是否有一种方法可以以某种方式定位这些行并自动进行替换。
\n所有r
字符都要替换成\xc9\xb9
,无一例外,但只是在拼音中,留下r
英文/非音标文本保持原样。
是否可以通过使用脚本或其他东西来做到这一点?我的文档中没有换行符,因此转录始终是之后的第三行\\phrase
。谢谢你!
Arc*_*mar 18
awk 版本(您需要一个中继文件,您可以将其一行)
\nawk '/\\\\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/\\\\phrase/ { p=NR ; }
将设置p
为每个行号,其中\\phrase
出现的每个行号NR == p+3 { gsub("r","\xc9\xb9") ; }
之后在第 3 行执行替换{print;}
打印所有行。这给了你的样品:(注意\xc9\xb9eplace
)
\\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
tha*_*isp 11
awk \'c&&!--c {gsub(/r/,"\xc9\xb9")} /\\\\phrase/ {c=3} 1\' file > newfile\n
Run Code Online (Sandbox Code Playgroud)\nc&&!--c
是一个常见的awk
习惯用法,实现while
getline
逻辑,请参阅参考资料。
仅当从 1 减到 0 时才会执行此条件后的操作。
\n当匹配文字时\'\\phrase\'
,我们设置c=3
,因此gsub()
只会在匹配后的第三行执行,并且这对所有匹配都重复。
既然你使用的是 Emacs...
\n如果你有evil-mode
安装(或者切换到 Vim),您可以执行以下操作:
:g/^\\\\phrase/+3s/r/\xc9\xb9/g\n
Run Code Online (Sandbox Code Playgroud)\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
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
将光标放在代码上):
:g/^\\\\phrase/+3s/r/\xc9\xb9/g\n
Run Code Online (Sandbox Code Playgroud)\n其中foo
是您要执行此操作的缓冲区的名称。
编辑:replace-string-in-region
在 Emacs 28.1(撰写时的最新版本)中引入。如果您的 Emacs 较旧,您可以使用search-forward
和replace-match
来代替:
(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您还可以通过外部命令过滤 Emacs 缓冲区,就像此处的其他答案之一:C-x h C-u M-| <command> Enter
C-x h
选择整个缓冲区。M-|
将提示输入将过滤选择的命令。C-u
修改,M-|
以便用输出替换选择,而不是将其放入临时缓冲区中。
如果每个部分之间总是有一个空行,则可以尝试 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-a
:自动将每个输入行分割到数组中@F
。
-F'\\n'
: 按换行符分割。
-00
:“段落模式”,行现在由\\n\\n
(空行)定义,因此每个部分都变成“行”。
-ne
:逐行读取输入文件并应用给出的脚本-e
每一行。
$F[3]=~s/r/\xc9\xb9/g;
:将全部替换r
为\xc9\xb9
数组的第 4 个元素@F
(这是每个部分的第 4 行;数组从 0 开始)。
print join "\\n",@F , "\\n"'
:用 加入修改后的@F
数组\\n
,然后将其与额外的内容一起打印\\n
。
如果您不能依赖这一点并且需要在行匹配后始终选择第三行\\phrase
,您可以执行以下操作:
$ 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)\n0
这会在每次看到 时设置一个计数器\\phrase
,并在每个新行上将其加一。然后,我们只在计数器的值为 4 时进行替换。
与标准sed
:
sed '/^\\\\phrase$/{n;n;n;s/r/\xc9\xb9/g;}'\n
Run Code Online (Sandbox Code Playgroud)\ny/r/\xc9\xb9/
代替s/r/\xc9\xb9/g
也可以在 POSIX 兼容的sed
实现中工作,只要该\xc9\xb9
字符被视为用户区域设置中的字符,但 s/r/\xc9\xb9/g
会更便携,因为它也可以与sed
不支持多字节字符的实现一起工作(如\xc9\xb9
UTF-8 中的情况) ;我找不到任何\xc9\xb9
在单个字节上编码的字符编码)。
为了\xc9\xb9
在用户的区域设置中正确编码,zsh
您可以这样做:
sed $'/^\\\\\\\\phrase$/{n;n;n;s/r/\\u0279/g;}'\n
Run Code Online (Sandbox Code Playgroud)\n它将扩展到 用户区域设置\xc2\xb9 中\\u0279
该字符的编码\xc9\xb9
\xc2\xb9$'\\uXXXX'
现在有一些其他 shell 支持,但请注意,在某些 shell 中,它在语言环境中扩展,因为它是在 shell 启动时或读取该行代码时进行的,而不一定是在该语言环境中的语言环境中扩展的。sed
命令被执行。在 ksh93 中,无论用户的区域设置如何,它始终以 UTF-8 扩展。当该字符在区域设置的字符集中不可用时,不同 shell 的行为也会有所不同。它会导致错误zsh