Как быстрее всего заменить 0 на 1 и наоборот в потоке?

Если вы не против экспортировать переменную в свою среду, вы можете использоватьenvsubst:

NAME
       envsubst - substitutes environment variables in shell format strings

SYNOPSIS
       envsubst [OPTION] [SHELL-FORMAT]

DESCRIPTION
       Substitutes the values of environment variables.

Пр. дано

$ cat foo
foo ${bar}

, затем

$ export bar="foo"
$ envsubst < foo
foo foo
20
22.01.2020, 17:09
5 ответов

Хотяtrявляется правильным инструментом для этой работы , вы можете сделать это в sed, используя командуy(транслитерации ), а не командуs(замены ):

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

yв основном является внутренней реализацией sedtr-со всеми вытекающими отсюда накладными расходами.

40
27.01.2020, 19:43

Для этого можно использовать tr, его основная цель — перевод символов:

echo 111111100000000000000 | tr 01 10

Ваша команда sedзаменяет все 0 на 1, что приводит к строке, содержащей только 1 с (исходные 1 с и все замененные 0 с ), а затем заменяет все 1 с 0 с, в результате чего строка содержит только 0 с.

В длинных потоках trбыстрее, чем sed; для файла размером 100 МиБ:

$ 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
27.01.2020, 19:43

Путь естьecho "111111100000000000000" | sed 's/1/2/g;s/0/1/g;s/2/0/g'

13
27.01.2020, 19:43

Вероятно, медленный метод, но он делает это бинарным способом, используя встроенную оболочку -в арифметике:

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
27.01.2020, 19:43

Если ваша строка содержит только одну строку и состоит только из 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'

.
1
27.01.2020, 19:43

Теги

Похожие вопросы