web-dev-qa-db-ja.com

ストリームで0を1に、またはその逆に置き換える最も速い方法は何ですか?

0sと1sで構成される文字列が与えられた場合、私の目標は0を1に、またはその逆を置き換えることです。例:

入力

111111100000000000000

意図された出力

000000011111111111111

失敗しましたが、次のsedコマンドを試してみました

echo '111111100000000000000' | sed -e 's/0/1/g ; s/1/0/g'
000000000000000000000

何が欠けていますか?

23
Paulo Tomé

これには tr を使用できます。その主な目的は文字変換です。

echo 111111100000000000000 | tr 01 10

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
62
Stephen Kitt

trはこのジョブに適したツールですsed(置換)コマンドではなくy(文字変換)コマンドを使用してsで行うことができます。

$ echo '111111100000000000000' | sed 'y/01/10/'
000000011111111111111

yは基本的にsedtrの内部実装であり、すべてのオーバーヘッドが含まれています。

39
steeldriver

方法はecho "111111100000000000000" | sed 's/1/2/g;s/0/1/g;s/2/0/g'

12
aborruso

おそらく遅い方法ですが、シェルの組み込み算術を使用してバイナリ方式で実行します。

echo '111111100000000000000' |
  while read -rn1 b; do
    printf '%1d' $((b^1))
  done

または、バイナリストリームをバイトチャンクで処理します。

#!/usr/bin/env bash

# Populate a byte to inverted binary string array
declare -a byte_binstring=()
for ((byte=0; byte<=255; byte++)); do
  for ((bit=0; bit<=7; bit++)); do
    printf -v byte_binstring[byte] '%1s' "$((!(byte>>bit&1)))${byte_binstring[byte]}"
  done
done

# Read input stream by chunks of 8 bits max
while read -rn8 bin_str; do
  # $((2#$bin_str)) converts the bit string into a byte value
  # using Shell built-in base-2 arithmetic conversion
  # byte_binstring[$((2#$bin_str))] gets the string matching this byte value
  # ${#bin_str}} gives the number of bits read (string length)
  # extract the last n characters from string matching
  # number of byte read
  # ${byte_binstring[$((2#$bin_str))]: -${#bin_str}}
  # This prints the inverted binary representation from the read bits stream
  printf '%s' "${byte_binstring[$((2#$bin_str))]: -${#bin_str}}"
done
1
Léa Gris

文字列が1行のみで構成され、0と1のみで構成されている場合、これを使用できます

echo "111111100000000000000" |
    Perl -e 'while (read(STDIN, $b, 1)) { print chr(ord($b) ^ 1); } print "\n";'

文字列に複数の行を含めることができる場合は、Perl -ePerl -neに変更し、バイトの読み取り方法を変更します(readにはファイルハンドルが必要なため)

echo -e "111111100000000000000\n0001111010101" |
    Perl -ne 'while (/(.)/g) { print chr(ord($1)^1) } print "\n"'

ただし、この方法では各行が文字列に分割されるため、大きなファイルの場合はあまり効率的ではありません。その場合は少しチェックが必要です

echo "122111111034000000000abc0000" | Perl -e 'while (read(STDIN, $b, 1)) {
    print ($b eq '0' or $b eq '1' ? chr(ord($b) ^ 1) : $b) } print "\n";'

ご覧のとおり、この方法は'0'および'1'以外の文字を含む文字列に対しても機能します

1
phuclv