What is the quickest way of replacing 0 by 1 and vice-versa in a stream?

Pau*_*omé 23 performance sed text-processing

Given a string composed of 0s and 1s, my goal is to replace 0 by 1 and vice-versa. Example:

Input

111111100000000000000
Run Code Online (Sandbox Code Playgroud)

Intended output

000000011111111111111
Run Code Online (Sandbox Code Playgroud)

I tried, unsuccessfully, the following sed command

echo '111111100000000000000' | sed -e 's/0/1/g ; s/1/0/g'
000000000000000000000
Run Code Online (Sandbox Code Playgroud)

What am I missing?

Ste*_*itt 62

您可以tr为此使用,其主要目的是字符翻译:

echo 111111100000000000000 | tr 01 10
Run Code Online (Sandbox Code Playgroud)

您的sed命令将所有 0 替换为 1,从而生成仅包含 1 的字符串(原始 1 和所有替换的 0),然后将所有 1 替换为 0,从而生成仅包含 0 的字符串。

在长流上,trsed; 对于 100MiB 文件:

$ time tr 10 01 < bigfileof01s > /dev/null
tr 10 01 < bigfileof01s > /dev/null  0.07s user 0.03s system 98% cpu 0.100 total

$ time sed y/10/01/ < bigfileof01s > /dev/null
sed y/10/01/ < bigfileof01s > /dev/null  3.91s user 0.11s system 99% cpu 4.036 total
Run Code Online (Sandbox Code Playgroud)

  • `tr` 的另一个参数是它应该适用于任何文件/流。0 和 1 的长流不是 POSIX 的正式术语的文本文件。`sed` 的规范说“输入文件应该是文本文件”,而对于 `tr` 它是“任何类型的文件”。我相信 GNU `sed` 具有无限的输入缓冲区(当然不限于 `{LINE_MAX}` 字节),因此它适用于 `bigfileof01s`。其他实现可能不会。 (5认同)
  • @ThorbjørnRavnAndersen 你在问为什么“字符串”不应该是什么意思?或者为什么某些行不应该超过?[文本文件的 POSIX 定义](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_403) 指出“没有行超过 `{LINE_MAX}` 个字节的长度”。`sed` 可能会也可能不会接受不符合要求的文件。`tr` 必须接受任何文件。这是我的观点。对于不熟悉“POSIX 的正式术语”的用户,我的第一条评论可能会产生误导,现在应该很清楚了。(续) (2认同)
  • (续)[“字符串”的 POSIX 定义](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_375) 需要一个空字节。我解释这一点是关于内存中的表示,而不是文件中的表示(例如,文本文件根本不能包含空字节)。无论如何,它与长度无关;加上`sed` 必须以“文本文件”而不是“字符串”的形式处理输入。您使用的“字符串”的定义是什么?为什么它对“{LINE_MAX}”问题很重要? (2认同)

ste*_*ver 40

虽然tr是这项工作的正确工具,但您可以sed使用y(transliteration) 命令而不是s(substitution) 命令来完成它:

$ echo '111111100000000000000' | sed 'y/01/10/'
000000011111111111111
Run Code Online (Sandbox Code Playgroud)

y基本上sedtr-的内部实现- 包含所有隐含的开销。


abo*_*uso 13

一种方式是 echo "111111100000000000000" | sed 's/1/2/g;s/0/1/g;s/2/0/g'

  • 这种方法确实有效,但它有两个缺点:它不具有通用性(输入中不能出现临时替换字符,这里当然是这种情况),并且它比 `sed` 的 `y` 命令慢得多,更不用说`tr`了。 (10认同)
  • 谢谢@StephenKitt。我编写了一个解决方案,特别是为了显示输入问题的错误:在其中,命令会覆盖自身。我的例子不好,但可能有点“教育性” (5认同)
  • 是的,我同意它很有用且有教育意义! (5认同)