0
sと1
sで構成される文字列が与えられた場合、私の目標は0を1に、またはその逆を置き換えることです。例:
入力
111111100000000000000
意図された出力
000000011111111111111
失敗しましたが、次のsed
コマンドを試してみました
echo '111111100000000000000' | sed -e 's/0/1/g ; s/1/0/g'
000000000000000000000
何が欠けていますか?
これには tr
を使用できます。その主な目的は文字変換です。
echo 111111100000000000000 | tr 01 10
sed
コマンドは、すべての0を1で置き換え、1のみを含む文字列(元の1およびすべての置き換えられた0)を生成し、次にすべての1を0で置き換え、0のみを含む文字列を生成します。
長いストリームでは、tr
はsed
より高速です。 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
tr
はこのジョブに適したツールですsed
(置換)コマンドではなくy
(文字変換)コマンドを使用してs
で行うことができます。
$ echo '111111100000000000000' | sed 'y/01/10/'
000000011111111111111
y
は基本的にsed
のtr
の内部実装であり、すべてのオーバーヘッドが含まれています。
方法はecho "111111100000000000000" | sed 's/1/2/g;s/0/1/g;s/2/0/g'
おそらく遅い方法ですが、シェルの組み込み算術を使用してバイナリ方式で実行します。
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行のみで構成され、0と1のみで構成されている場合、これを使用できます
echo "111111100000000000000" |
Perl -e 'while (read(STDIN, $b, 1)) { print chr(ord($b) ^ 1); } print "\n";'
文字列に複数の行を含めることができる場合は、Perl -e
をPerl -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'
以外の文字を含む文字列に対しても機能します