sed 将匹配中的每个字符替换为单个字符,例如 █████ 编辑

sed 将匹配中的每个字符替换为单个字符,例如 █████ 编辑

是否有 sed 替换函数(或其他正则表达式)可以将匹配模式捕获的每个字符替换为用于覆盖或删除匹配的单个字符?例如,Full Block █ Unicode:U+2588,UTF-8:E2 96 88 是一个不错的选择。有没有简单的方法可以做到这一点?

答案1

假设您的文本不包含 ASCII 字符0x020x03,则:

sed '
s/pattern/\x02\0\x03/g
:loop
s/\x02[^\x03]/█\x02/g
t loop
s/\x02\x03//g
'

我使用的字符分别是 STX(文本开始)和 ETX(文本结束)。如今它们在文本中很少使用;这使得它们成为临时标记的不错选择。

该解决方案首先包含与模式匹配的每个片段,其中包含 STX 和 ETX。接下来,它循环,将每个 STX 移向行尾,直到它遇到 ETX。每次这样的移动都会导致出现。当每个 STX 遇到其对应的 ETX 时,循环结束,最后删除所有 STX+ETX 对。

如果您的sed不支持多字节字符,那么您可能会得到比预期更多的 s。如果与模式匹配的任何内容包含多字节字符(被误解为单字节字符),就会发生这种情况。即便如此,如果输入编码为 UTF-8,STX 和 ETX 也可以安全使用,因为 UTF-8 中的多字节字符由最高位为 的字节组成1,而 STX 或 ETX 的最高位为0。这意味着如果您的sed不支持多字节字符,那么您可能会得到太多的s,但没有有效的 UTF-8 文本会因被意外解释为 STX 或 ETX 而受到干扰(除非它真的是 STX 或 ETX,因此是最初的假设)。

sed在 Linux、UTF-8 语言环境中使用 GNU(支持多字节字符)进行了测试。


例子:

printf '%s\n' 'This is a test: CaMeL, 12a15, foo.' \
| sed '
s/[[:upper:][:digit:]]*/\x02\0\x03/g
:loop
s/\x02[^\x03]/█\x02/g
t loop
s/\x02\x03//g
'

输入是:

This is a test: CaMeL, 12a15, foo.

输出应为:

█his is a test: █a█e█, ██a██, foo.

答案2

替换每个字符很容易:

sed 's/./█/g' file

如果您的模式更复杂,则可能需要将其拆分。例如,如果您有三个长度为 3 的单词要用三个 █: 替换,还有两个长度为 4 的单词,我会编写两个单独的规则:

sed -e 's/\(one\|two\|six\)/███/g' -e 's/\(four\|five\)/████/g' file

如果您有大量不同长度的模式,也许可以切换到更强大的语言,例如 Perl:

perl -pe 's/(four score and seven years ago|all your base are belong to us|the beat goes on and I\x27m so wrong)/ "█" x length($1) /ge' file

(你sed可能有一个-E-r选项可以让你避免使用反斜杠,以产生更像 Perl 的正则表达式。Perl 的正则表达式功能包括很多比的更复杂sed,所以如果你切换到 Perl,就会有各种各样的额外技巧,以及一种更易读的语言来编写循环、算术等等。也许还要注意 Perl 如何让我编写\x27文字单引号,所以我不必摆弄 shell 的引用机制来将文字单引号嵌入单引号字符串中。)

相关内容