converter arquivo de texto de bits em arquivo binário

12

Eu tenho um arquivo instructions.txtcom o conteúdo:

00000000000000000000000000010011
00000010110100010010000010000011
00000000011100110000001010110011
00000000011100110000010000110011
00000000011100110110010010110011
00000000000000000000000000010011

Como posso criar um arquivo binário instructions.bincom os mesmos dados que instructions.txt. Em outras palavras, o .binarquivo deve ter os mesmos 192 bits que estão no .txtarquivo, com 32 bits por linha. Estou usando o bash no Ubuntu Linux. Eu estava tentando usar, xxd -b instructions.txtmas a saída é muito superior a 192 bits.

dopamane
fonte

Respostas:

6

oneliner para converter cadeias de caracteres de 32 bits e zeros no binário correspondente:

$ perl -ne 'print pack("B32", $_)' < instructions.txt > instructions.bin

o que faz:

  • perl -neirá percorrer cada linha do arquivo de entrada fornecido em STDIN ( instructions.txt)
  • pack("B32", $_)pegará uma lista de strings de 32 bits ( $_que acabamos de ler de STDIN) e a converterá em valor binário (você pode usar alternativamente "b32"se quiser ordem crescente de bits dentro de cada byte em vez de ordem decrescente; veja perldoc -f packmais detalhes)
  • print produziria esse valor convertido para STDOUT, que depois redirecionaremos para nosso arquivo binário instructions.bin

verificar:

$ hexdump -Cv instructions.bin
00000000  00 00 00 13 02 d1 20 83  00 73 02 b3 00 73 04 33  |...... ..s...s.3|
00000010  00 73 64 b3 00 00 00 13                           |.sd.....|
00000018

$ xxd -b -c4 instructions.bin
00000000: 00000000 00000000 00000000 00010011  ....
00000004: 00000010 11010001 00100000 10000011  .. .
00000008: 00000000 01110011 00000010 10110011  .s..
0000000c: 00000000 01110011 00000100 00110011  .s.3
00000010: 00000000 01110011 01100100 10110011  .sd.
00000014: 00000000 00000000 00000000 00010011  ....
Matija Nalis
fonte
8

Adicionar a -ropção (modo reverso) a xxd -bnão funciona como pretendido, porque o xxd simplesmente não suporta a combinação desses dois sinalizadores (ele ignora -bse os dois forem fornecidos). Em vez disso, você deve converter os bits para se hexagonizar primeiro. Por exemplo, assim:

( echo 'obase=16;ibase=2'; sed -Ee 's/[01]{4}/;\0/g' instructions.txt ) | bc | xxd -r -p > instructions.bin

Explicação completa:

  • A parte entre parênteses cria um bcscript. Primeiro, define a base de entrada como binária (2) e a base de saída como hexadecimal (16). Depois disso, o sedcomando imprime o conteúdo instructions.txtcom um ponto-e-vírgula entre cada grupo de 4 bits, o que corresponde a 1 dígito hexadecimal. O resultado é canalizado para bc.
  • O ponto-e-vírgula é um separador de comandos bc, portanto, todo o script faz é imprimir todos os números inteiros de entrada novamente (após a conversão base).
  • A saída de bcé uma sequência de dígitos hexadecimais, que pode ser convertida em um arquivo com o habitual xxd -r -p.

Resultado:

$ hexdump -Cv instructions.bin
00000000  00 00 00 13 02 d1 20 83  00 73 02 b3 00 73 04 33  |...... ..s...s.3|
00000010  00 73 64 b3 00 00 00 13                           |.sd.....|
00000018
$ xxd -b -c4 instructions.bin
00000000: 00000000 00000000 00000000 00010011  ....
00000004: 00000010 11010001 00100000 10000011  .. .
00000008: 00000000 01110011 00000010 10110011  .s..
0000000c: 00000000 01110011 00000100 00110011  .s.3
00000010: 00000000 01110011 01100100 10110011  .sd.
00000014: 00000000 00000000 00000000 00010011  ....
nomadictype
fonte
Desculpe, ainda há um erro de endianness nisso. Trabalhando em consertá-lo!
Nomadictype 10/10/19
1
Na verdade, está tudo bem. Eu estava confuso anteriormente usando a largura de saída incorreta no último comando xxd.
Nomadictype 10/10/19
1
Eu testei o roteiro e ele funciona, mas saídas: (standard_in) 1: syntax error. Você pode explicar a que syntax errorse refere ou por que isso ocorre? Isso também acontece na sua máquina?
dopamane 10/10
2

Minha resposta original estava incorreta - xxdnão posso aceitar -pou -rcom -b...

Dado que as outras respostas são viáveis ​​e no interesse de " outra maneira ", que tal o seguinte:

Entrada

$ cat instructions.txt
00000000000000000000000000010011
00000010110100010010000010000011
00000000011100110000001010110011
00000000011100110000010000110011
00000000011100110110010010110011
00000000000000000000000000010011

Resultado

$ hexdump -Cv < instructions.bin
00000000  00 00 00 13 02 d1 20 83  00 73 02 b3 00 73 04 33  |...... ..s...s.3|
00000010  00 73 64 b3 00 00 00 13                           |.sd.....|
00000018

Pipeline Bash:

cat instructions.txt \
    | tr -d $'\n' \
    | while read -N 4 nibble; do 
        printf '%x' "$((2#${nibble}))"; \
      done \
    | xxd -r -p \
    > instructions.bin
  • cat - desnecessário, mas usado para maior clareza
  • tr -d $'\n' - remova todas as novas linhas da entrada
  • read -N 4 nibble- leia exatamente 4 × caracteres na nibblevariável
  • printf '%x' "$((2#${nibble}))" converter a mordidela de binário em 1 × caractere hexadecimal
    • $((2#...)) - converta o valor fornecido da base 2 (binária) para a base 10 (decimal)
    • printf '%x' - formate o valor fornecido da base 10 (decimal) para a base 16 (hexadecimal)
  • xxd -r -p- reverse ( -r) uma lixeira comum ( -p) - a partir de hexadecimal para binário raw

Pitão:

python << EOF > instructions.bin
d = '$(cat instructions.txt | tr -d $'\n')'
print(''.join([chr(int(d[i:i+8],2)) for i in range(0, len(d), 8)]))
EOF
  • Um heredoc ( << EOF) não citado é usado para obter conteúdo no código Python
    • Isso não é eficiente se a entrada se tornar grande
  • cate tr- usado para obter uma entrada limpa (uma linha)
  • range(0, len(d), 8)- obtenha uma lista de números de 0 até o final da string d, percorrendo 8 × caracteres por vez.
  • chr(int(d[i:i+8],2))- converta a fatia atual ( d[i:i+8]) de binária em decimal ( int(..., 2)) e depois em um caractere bruto ( chr(...))
  • [ x for y in z]- compreensão da lista
  • ''.join(...) - converte a lista de caracteres em uma única string
  • print(...) - imprima
Attie
fonte
1
Nota: em muitos shells |no final de uma linha funciona como uma barra invertida: o comando continua na próxima linha. Dessa forma, você pode se livrar de algumas barras invertidas. Não tenho certeza se o uso de símbolos de tubulação após LFs foi sua decisão informada. Estou mencionando o contrário, caso você não saiba.
Kamil Maciorowski 10/10
1
Eu não sabia, obrigado! Eu gosto de dividir o pipeline em linhas lógicas e ter os pipes |(ou redirecionamentos >, operadores booleanos &&, etc ...) explicitamente na frente para visibilidade / clareza ... talvez uma coisa estilística / preferencial.
Attie
1
Após algumas reflexões, posso começar a usar esse estilo, porque é possível dizer que as duas linhas estão conectadas, examinando qualquer uma delas. Se |estiver no final, a próxima linha pode parecer um comando independente, pode ser confusa. Por isso, pensei que o estilo poderia ser sua decisão informada.
Kamil Maciorowski 10/10
Awesome, deixe-me saber como ele vai :-)
Attie
1
Está indo bem . :)
Kamil Maciorowski
1

Você também pode postar isso no site CodeGolf SE, mas aqui está a minha versão alternativa do Python (apenas para o desafio do pontapé):

python -c "import sys,struct;[sys.stdout.buffer.write(struct.pack('!i',int(x,2)))for x in sys.stdin]" \
< input.txt > output.bin

Supondo que você input.txtcontenha seus dados e está formatado para 32 caracteres por linha.

Isso usa o structpacote Python 3 e a escrita / leitura para stdin / out. (No Python 2, seria mais curto).

wvxvw
fonte