Como você converte uma matriz de bytes em uma string hexadecimal em C?

89

Eu tenho:

Quero converter a matriz de bytes em uma string de forma que possa imprimir a string usando printf:

e obter (os dois pontos não são necessários):

Qualquer ajuda seria muito apreciada.

Steve Walsh
fonte
buf[i]deve ser lançado para unsigned char, ou irá transbordar se buf[i] > 127, isto é:buf_ptr += sprintf(buf_ptr, "%02X", (unsigned char)buf[i]);
whatacold

Respostas:

93

para uma forma mais genérica:

para concatenar em uma string, existem algumas maneiras de fazer isso ... eu provavelmente manteria um ponteiro para o final da string e usaria sprintf. você também deve acompanhar o tamanho da matriz para garantir que ela não fique maior do que o espaço alocado:

Mark Synowiec
fonte
Obrigado Mark - meu problema é um pouco mais complicado. Na verdade, tenho um buffer com um comprimento de X bytes. Eu esperava encontrar uma maneira genérica de fazer isso para X bytes e ter uma string como resultado.
Steve Walsh
Apenas atualizado para adicionar código para lidar com qualquer número de bytes dado ... assumindo que x é o comprimento.
Mark Synowiec
Obrigado mais uma vez, Mark, mas o que eu estava achando mais complicado para esse problema é como imprimir isso em uma string.
Steve Walsh
6
printf("%02X", (unsigned char)buf[i]);deve ser usado porque o original causará estouro de caracteres não assinados
easytiger
3
Porque não printf("%02hhX", buf[i])?
Hintron de
32

Para completude, você também pode fazer isso facilmente sem chamar qualquer função de biblioteca pesada (sem snprintf, sem strcat, nem mesmo memcpy). Pode ser útil, digamos, se você estiver programando algum microcontrolador ou kernel do sistema operacional onde libc não está disponível.

Nada realmente sofisticado que você possa encontrar código semelhante por aí se você procurar por ele no Google. Na verdade, não é muito mais complicado do que chamar snprintf e muito mais rápido.

Aqui está outra versão um pouco mais curta. Ele simplesmente evita a variável de índice intermediária i e a duplicação do último código de caso (mas o caractere de terminação é escrito duas vezes).

Abaixo está outra versão para responder a um comentário dizendo que usei um "truque" para saber o tamanho do buffer de entrada. Na verdade, não é um truque, mas um conhecimento de entrada necessário (você precisa saber o tamanho dos dados que está convertendo). Deixei isso mais claro extraindo o código de conversão para uma função separada. Também adicionei o código de verificação de limite para o buffer de destino, o que não é realmente necessário se sabemos o que estamos fazendo.

Kriss
fonte
1
Não é um truque, apenas uma constante. No contexto da pergunta, é claro que o comprimento da fonte que queremos converter para hexadecimal é bem conhecido (eu poderia ter colocado algum 4 codificado em vez de sizeof). No caso geral, a função deve ser chamada em alguma entrada de comprimento conhecido e o buffer de destino tem 3 vezes + 1 bytes disponíveis. Isso deve ser garantido pelo chamador, não há razão para que a função de conversão execute essa tarefa. Chamar strlen () pode ser uma maneira de encontrar o tamanho da fonte em alguns casos, mas nem sempre. E se o número a ser convertido em hexadecimal contiver zeros?
Kriss
Inspirado por sua função, escrevi uma versão que também retorna o número de bytes gravados no buffer de saída, semelhante a snprintf, etc. gist.github.com/cellularmitosis/0d8c0abf7f8aa6a2dff3
Jason Pepas
Acho que você deve fazer seu buffer de saída com o tamanho correto automaticamente usando char str [sizeof (buf) * 3 + 1];
Cecil Ward
Além disso, muito mais constantes o protegeriam. Por exemplo, "const unsigned char const * p" para que você possa ter certeza de que os buffers de entrada não foram gravados. Um torna o endereço (ou 'ponteiro') constante, ou variável, e o outro torna a memória desse endereço somente leitura ou não. Muitas vezes impede que você confunda os ponteiros. E também, ter nomes significativos que documentam quais buffers e ponteiros são para entrada e saída também ajudaria.
Cecil Ward
@Cecil War: a menos que meu código seja falso, usar const não protegerá muito, exceto se você disser misturando ponteiros ou usando o mesmo ponteiro para entrada e saída (ok, ainda é possível). Mas também ajudará o compilador a otimizar o código. Melhor ainda seria usar a palavra-chave restrita (uma pena que C99 não é C ++, mas freqüentemente existe como uma extensão do compilador). O que você deseja mais significativo do que chamar buffer de entrada ine buffer de saída out? Eu também poderia optar por usar uma string e retornar uma cópia em vez de fornecer buffer de saída, em otimizadores C ++ modernos são bons o suficiente para não se importar muito.
Kriss
15

Aqui está um método muito mais rápido:

Yannuth
fonte
3
Este código contém um bug que se manifesta apenas em entradas estranhas não imprimíveis (não tive tempo de cavar exatamente o que está acontecendo matematicamente). Tente codificar o binário do hexadecimal ca9e3c972f1c5db40c0b4a66ab5bc1a20ca4457bdbe5e0f8925896d5ed37d726e você ÌaÌe3cÌ72f1c5dÌ40c0b4a66Ìb5bÌ1Ì20cÌ4457bÌbÌ5Ì0Ì8Ì258Ì6Ì5Ìd37Ì726sairá. Para corrigir isso, o bit dentro hex_strda primeira linha do loop for precisa ser alterado para (input[i] >> 4) & 0x0Fcomo na resposta de @kriss. Então funciona bem.
niemiro
Bug - não verifica a falha malloc ().
Cecil Ward
É sempre melhor usar unsigned char absolutamente em todos os lugares, pois ninguém quer o risco de assinados chars (um recurso de hardware DEC PDP11 maluco), e dessa forma você não corre o risco de comparações assinadas darem errado ou mudanças certas assinadas corrompendo valores. Nesse caso, para ser justo, o código faz defensivamente um & 0x0F em todos os lugares, o que o protege aqui.
Cecil Ward
O parâmetro de entrada bin deve ser const unsigned char const * bin, para declarar a memória como somente leitura para os propósitos desta rotina.
Cecil Ward
1
Eu
integrei
14

Já existem respostas semelhantes acima, adicionei esta para explicar como a seguinte linha de código funciona exatamente:

É tranquilo complicado e não é fácil de entender, coloco a explicação nos comentários abaixo:

razz
fonte
Lógica brilhante. Estava procurando por uma resposta elegante de string não C ++ para esse desafio!
Mark Terrill
6

Eu só queria adicionar o seguinte, mesmo que seja um pouco fora do tópico (não o padrão C), mas eu me pego procurando por isso com frequência e me deparando com essa questão entre os primeiros resultados de pesquisa. A função de impressão do kernel do Linux,, printktambém tem especificadores de formato para a saída de conteúdo de array / memória "diretamente" por meio de um especificador de formato singular:

https://www.kernel.org/doc/Documentation/printk-formats.txt

... entretanto, esses especificadores de formato não parecem existir para o espaço do usuário padrão (s)printf.

Sdaau
fonte
6

Solução

A função btoxconverte dados arbitrários *bbem uma string não terminada *xpde ndígitos hexadecimais:

Exemplo

Resultado: 00010A0B .

Ao vivo: Tio.run .

7vujy0f0hy
fonte
1

Esta é uma forma de realizar a conversão:

Rajaganesh
fonte
1

Versão Yannith ligeiramente modificada. É que gosto de tê-lo como um valor de retorno

typedef struct {
   size_t len;
   uint8_t *bytes;
} vdata;

char* vdata_get_hex(const vdata data)
{
   char hex_str[]= "0123456789abcdef";

   char* out;
   out = (char *)malloc(data.len * 2 + 1);
   (out)[data.len * 2] = 0;
   
   if (!data.len) return NULL;
   
   for (size_t i = 0; i < data.len; i++) {
      (out)[i * 2 + 0] = hex_str[(data.bytes[i] >> 4) & 0x0F];
      (out)[i * 2 + 1] = hex_str[(data.bytes[i]     ) & 0x0F];
   }
   return out;
}

Mikhail Baynov
fonte
1

Esta função é adequada onde o usuário / chamador deseja que a string hexadecimal seja colocada em um buffer / array de caracteres. Com a string hexadecimal em um buffer de caracteres, o usuário / chamador pode usar sua própria macro / função para exibi-la ou registrá-la em qualquer lugar que desejar (por exemplo, em um arquivo). Esta função também permite que o chamador controle o número de bytes (hex) para colocar em cada linha.

Exemplo: Código

RESULTADO

Sandesh Kumar Sodhi
fonte
0

Não há nenhum primitivo para isso em C. Eu provavelmente malloc (ou talvez alloca) um buffer longo o suficiente e faço um loop sobre a entrada. Também vi isso ser feito com uma biblioteca de string dinâmica com semântica (mas não sintaxe!) Semelhante à do C ++ ostringstream, que é uma solução plausivelmente mais genérica, mas pode não valer a pena a complexidade extra apenas para um único caso.

maluco
fonte
0

Se você deseja armazenar os valores hexadecimais em uma char *string, você pode usar snprintf. Você precisa alocar espaço para todos os caracteres impressos, incluindo os zeros à esquerda e dois pontos.

Expandindo a resposta de Mark:

Portanto, agora str_bufconterá a string hexadecimal.

Sr. Shickadance
fonte
isso sobrescreve os primeiros 2 caracteres repetidamente .. certo?
xordon
0

A solução do ZincX adaptada para incluir delimitadores de dois pontos:

Gnubie
fonte
0

Vou adicionar a versão C ++ aqui para quem estiver interessado.

Ele imprimirá o hexdump de bufferde comprimento countpara std::ostream out(você pode torná-lo padrão std::cout). Cada linha conterá bytes_per_linebytes, cada byte é representado usando hexadecimal de dois dígitos maiúsculos. Haverá um espaço entre os bytes. E no final da linha ou final do buffer, ele imprimirá uma nova linha. Se bytes_per_lineestiver definido como 0, não imprimirá nova linha. Experimente você mesmo.

WiSaGaN
fonte
0

Para um uso simples, criei uma função que codifica a string de entrada (dados binários):

Uso:

mbuster
fonte
0

Com base na resposta de Yannuth, mas simplificada.

Aqui, o comprimento de dest[]está implícito como sendo o dobro de lene sua alocação é gerenciada pelo chamador.

konsolebox
fonte
0

Sei que essa pergunta já tem uma resposta, mas acho que minha solução pode ajudar alguém.

Portanto, no meu caso, eu tinha uma matriz de bytes representando a chave e precisava converter essa matriz de bytes em uma matriz char de valores hexadecimais para imprimi-la em uma linha. Extraí meu código para uma função como esta:

Agora, posso usar minha função assim:

Serialobject faz parte da biblioteca Arduino e m_publicKeyé membro da minha classe com a seguinte declaração uint8_t m_publicKey[32].

NutCracker
fonte
0

Você pode resolver com snprintf e malloc.

OUT: bbccdd0fef0f0e0d0c

barbaros
fonte
-2

Que soluções complexas!
Malloc e corre e lança oh meu. (Citação OZ)
e nem um único rem em qualquer lugar. Puxa, que tal

algo assim?

Stephen George
fonte
OK, agora você tem dois dígitos hexadecimais. Resta adicionar separadores e cuidar dos outros bytes para os convertidos. Talvez com um laço? Faça disso uma função e você terá algo semelhante ao meu (mas bastante prolixo e difícil de ler). Talvez você deva pelo menos terminar o trabalho antes de xingar em outros cartazes?
Kriss,
1
E uma palavra sobre comentários no código-fonte (não REM, que é a palavra-chave BASIC para comentários, por favor evite isso): comentários dizendo em inglês o que o código está fazendo é uma prática muito, muito ruim! Sim, os programadores devem saber o que os operadores de módulo significam (dar os restos) e essa divisão conta o número de vezes que um número aparece em outro ... e que printf imprime o resultado. Oh meu!
Kriss