Extrai dados entre dois padrões correspondentes em um arquivo binário

2

Eu estou tentando extrair uma imagem jpeg de um arquivo de texto binário. Eu quero extrair todos os dados entre 0xFF 0xD8 (início da imagem) e 0xFF 0xD9 (fim da imagem), inclusive. Anteriormente, executei com êxito o seguinte comando para obter o image.jpg desejado de um único arquivo de parágrafo received.txt:

sed 's/.*\xFF\xD8/\xFF\xD8/; s/\xFF\xD9.*/\xFF\xD9/' received.txt > image.jpg

Mas quando tentei executar a mesma operação em um arquivo diferente, não funcionou. Eu também tentei usar

sed -n '/\xFF\xD8/,/\xFF\xD9/p' received.txt > temp.txt
sed 's/.*\xFF\xD8/\xFF\xD8/; s/\xFF\xD9.*/\xFF\xD9/' temp.txt > image.jpg

para remover quaisquer linhas antes ou depois das linhas correspondentes, mas não obteve sucesso.

Embora o arquivo fosse muito grande, colei o hex dump da parte relevante abaixo:

0a 55 57 5d 50 cf ff d8 ff fe ff ff ff d9 df 47 fe e7 c9 3b e9 9b 6b 55 c4 57 9b 98 73 fd 15 f7 77 7e f7 95 dd 55 f7 55 05 cc 55 97 55 dd 62 d1 1f 51 ef f1 ef fb e9 bf ed 5f bf f2 9d 75 af fe 6b fb bf 8f f7 f7 7e ff d3 bf 8e d5 5f df 57 75 fe 77 7b bf d7 af df 5d fb 0a 47 de d5 ff c1 23 9b 20 08 20 65 3c 06 83 11 05 30 50 a0 20 55 20 84 41 04 c2 59 50 89 64 44 44 10 05 20 87 28 1d a9

O dump hexadecimal da saída desejada, neste caso, é:

ff d8 ff fe ff ff ff d9

Atualizar

Ao tentar resolver o problema, descobri que o comando sed remove todos os caracteres antes ou depois de um padrão correspondente até o caractere não-ASCII (0x80 - 0xFF), mas não ultrapassa esse caractere não-ASCII. Por exemplo, se tentarmos:

echo 55 57 5d 50 cf 50 65 7f ff d8 ff fe ff ff ff d9 | xxd -r -p | sed 's/.*\xFF\xD8/\xFF\xD8/' > output

O dump hexadecimal da saída pode ser visto como:

xxd output

qual é:

55 57 5d 50 cf ff d8 ff fe ff ff ff d9

Como pode ser visto, os caracteres entre o caractere não-ASCII e o padrão correspondente são removidos, mas os caracteres antes do caractere não-ASCII não são.

Solução alternativa (não perfeita)

Eu usei os seguintes comandos para resolver um pouco o problema:

sed 's/\xFF\xD8/\x0A\xFF\xD8/; s/\xFF\xD9/\xFF\xD9\x0A/' received.txt > temp.txt

execute o seguinte comando (que funcionará se não houver nenhum caractere de nova linha (0x0A) em algum lugar entre 0xFF 0xD8 e 0xFF 0xD9):

sed -n '/\xFF\xD8/{/\xFF\xD9/p}' temp.txt > image.jpg

mas se o arquivo image.jpg estiver vazio (após a execução do comando acima), execute o seguinte comando:

sed -n '/\xFF\xD8/,/\xFF\xD9/p' temp.txt > image.jpg

Esses comandos farão o trabalho desejado, exceto que ele coloca 0x0A no final do arquivo image.jpg (ou seja, após 0xFF 0xD9). No meu caso, ele não criou nenhum problema, pois o arquivo JPEG descarta automaticamente os dados após o marcador 0xFF 0xD9.

Eu estava preso na implementação da condição 'se o arquivo de imagem está vazio' quando a @chaos surgiu com uma solução perfeita. Então, agora estou seguindo a solução dele. Muito obrigado @chaos!

Notas:

Aqui está como você pode obter os dados reais de seu dump hexadecimal que você pode enviar para o comando sed:

echo 0a 55 57 5d 50 cf ff d8 ff fe ff ff ff d9 df 47 fe e7 c9 3b e9 9b 6b 55 c4 57 9b 98 73 fd 15 f7 77 7e f7 95 dd 55 f7 55 05 cc 55 97 55 dd 62 d1 1f 51 ef f1 ef fb e9 bf ed 5f bf f2 9d 75 af fe 6b fb bf 8f f7 f7 7e ff d3 bf 8e d5 5f df 57 75 fe 77 7b bf d7 af df 5d fb 0a 47 de d5 ff c1 23 9b 20 08 20 65 3c 06 83 11 05 30 50 a0 20 55 20 84 41 04 c2 59 50 89 64 44 44 10 05 20 87 28 1d a9 | xxd -r -p

e você pode ver o despejo hexadecimal de um arquivo por:

xxd file.txt
    
por Adnan Ashraf 22.09.2015 / 13:11

2 respostas

1

Com seus dados de exemplo e grep com expressões regulares perl (PCRE) ativadas ( -P ):

grep -oP '\xFF\xD8.*\xFF\xD9' input >image.jpeg

O sinalizador -o diz grep para imprimir apenas a parte correspondente. O teste depois parece promissor:

$ file image.jpeg
image.jpeg: JPEG image data

Editar : se o acima não funcionar e tiver que ser sed , temos que converter os dados em texto:

hexdump -ve '1/1 "%.2X"' input | sed 's/.*\(FFD8.*FFD9\).*//' | xxd -r -p >image.jpeg
  • Com hexdump , o arquivo input é convertido em uma sequência semelhante à da sua pergunta.
    • -e especifica o formato
    • 1/1 significa aplicar o formato 1 vezes (contagem de iteração) e 1 após o / especificar o número de bytes a serem interpretados por cada iteração (contagem de bytes).
    • %.2X é o formato: um valor hexadecimal de dois dígitos.
  • Em seguida, sed remove tudo antes de FFD8 e depois de FFD9 do despejo.
    • Os parênteses \(...\) especificam um subpadrão que queremos salvar para mais tarde
    • Substitua tudo por , que é o conteúdo do subpadrão de cima.
  • Pelo menos, xxd inverte o hexdump para um formato binário.

O teste é bem-sucedido quando você usa o exemplo em sua pergunta:

$ echo 0a 55 57 5d 50 cf ff d8 ff fe ff ff ff d9 df 47 fe e7 c9 3b e9 9b 6b 55 c4 57 9b 98 73 fd 15 f7 77 7e f7 95 dd 55 f7 55 05 cc 55 97 55 dd 62 d1 1f 51 ef f1 ef fb e9 bf ed 5f bf f2 9d 75 af fe 6b fb bf 8f f7 f7 7e ff d3 bf 8e d5 5f df 57 75 fe 77 7b bf d7 af df 5d fb 0a 47 de d5 ff c1 23 9b 20 08 20 65 3c 06 83 11 05 30 50 a0 20 55 20 84 41 04 c2 59 50 89 64 44 44 10 05 20 87 28 1d a9 | \
  xxd -r -p | \
  hexdump -ve '1/1 "%.2X"' | \
  sed 's/.*\(FFD8.*FFD9\).*//' | \
  xxd -r -p >image.jpeg
$
$ file image.jpeg
image.jpeg: JPEG image data
$ xxd image.jpeg
0000000: ffd8 fffe ffff ffd9                      ........
    
por 22.09.2015 / 13:43
0

Só quero adicionar um pouco mais à solução @chaos

hexdump -ve '1/1 "%.2X "' input | sed 's/.*\(FF D8.*FF D9\).*//' | xxd -r -p > image.jpeg

Acabei de adicionar espaço após %.2X e, entre FFD8 e FFD9 . Isso é para evitar a correspondência do padrão deslocado, como:

0f fd 80 ... 0f fd 90
    
por 01.02.2016 / 07:40