converte arquivo de texto de bits para arquivo binário

10

Eu tenho um arquivo instructions.txt com o conteúdo:

00000000000000000000000000010011
00000010110100010010000010000011
00000000011100110000001010110011
00000000011100110000010000110011
00000000011100110110010010110011
00000000000000000000000000010011

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

    
por DavOS 09.10.2018 / 19:02

4 respostas

5

oneliner para converter strings de 32 bits e zeros em binário correspondente:

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

o que faz:

  • perl -ne irá iterar através de cada linha do arquivo de entrada fornecido no STDIN ( instructions.txt )
  • pack("B32", $_) terá uma lista de strings de 32 bits ( $_ que acabamos de ler de STDIN), e convertê-lo em valor binário (você poderia alternativamente usar "b32" se quisesse ordem crescente de bits dentro de cada byte em vez de descer bits ordem; veja perldoc -f pack para mais detalhes)
  • print produziria então esse valor convertido para STDOUT, que então redirecionamos para nosso arquivo binário instructions.bin

verifique:

$ 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  ....
    
por 10.10.2018 / 04:04
8

Adicionar a opção -r (modo reverso) a xxd -b não funciona como esperado, pois o xxd simplesmente não suporta a combinação desses dois sinalizadores (ele ignora -b se ambos forem fornecidos). Em vez disso, você precisa converter os bits para hexadecimal primeiro. Por exemplo, assim:

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

Explicação completa:

  • A parte dentro dos parênteses cria um script bc . Ele primeiro define a base de entrada como binária (2) e a base de saída como hexadecimal (16). Depois disso, o comando sed imprime o conteúdo de instructions.txt com um ponto-e-vírgula entre cada grupo de 4 bits, o que corresponde a um dígito hexadecimal. O resultado é canalizado para bc .
  • O ponto-e-vírgula é um separador de comando em bc , portanto, todo o script faz a impressão de todo o inteiro de entrada de volta (após a conversão de base).
  • A saída de bc é uma sequência de dígitos hexadecimais, que pode ser convertida em um arquivo com o usual xxd -r -p .

Saída:

( echo 'obase=16;ibase=2'; sed -Ee 's/[01]{4}/;
$ 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  ....
/g' instructions.txt ) | bc | xxd -r -p > instructions.bin
    
por 10.10.2018 / 02:43
2

Minha resposta original estava incorreta - xxd não pode aceitar -p ou -r com -b ...

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

Entrada

$ cat instructions.txt
00000000000000000000000000010011
00000010110100010010000010000011
00000000011100110000001010110011
00000000011100110000010000110011
00000000011100110110010010110011
00000000000000000000000000010011

Saída

$ 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 de 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' - remove todas as novas linhas da entrada
  • read -N 4 nibble - leia exatamente 4 × caracteres na variável nibble
  • printf '%x' "$((2#${nibble}))" converte o nibble de binário para 1 × caractere hexadecimal
    • $((2#...)) - converte o valor fornecido da base 2 (binária) para a base 10 (decimal)
    • printf '%x' - formata o valor dado da base 10 (decimal) para a base 16 (hexadecimal)
  • xxd -r -p - inverter ( -r ) um despejo simples ( -p ) - de hexadecimal para binário bruto

Python:

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 ) sem aspas é usado para obter conteúdo no código Python
    • Isso não é eficiente se a entrada ficar grande
  • cat e tr - usado para obter uma entrada limpa (uma linha)
  • range(0, len(d), 8) - obtenha uma lista de números de 0 ao final da string d , avançando 8 × caracteres de cada vez.
  • chr(int(d[i:i+8],2)) - converte a fatia atual ( d[i:i+8] ) de binário para decimal ( int(..., 2) ) e, em seguida, para um caractere bruto ( chr(...) )
  • [ x for y in z] - compreensão da lista
  • ''.join(...) - converte a lista de caracteres em uma única string
  • print(...) - imprimi-lo
por 10.10.2018 / 11:22
1

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

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 input.txt contenha seus dados e esteja formatado em 32 caracteres por linha.

Isso usa o pacote Python 3 struct e escreve / lê para stdin / out. (No Python 2, teria sido mais curto).

    
por 10.10.2018 / 13:26