web-dev-qa-db-ja.com

16進文字列をunsignedchar配列に変換するにはどうすればよいですか?

たとえば、cstring "E8 48 D8 FF FF 8B 0D"(スペースを含む)があり、同等のunsignedchar配列{0xE8,0x48,0xD8,0xFF,0xFF,0x8B,0x0D}に変換する必要があります。これを行うための効率的な方法は何ですか?ありがとう!

編集:私は標準ライブラリを使用できません...だからこれをCの質問と考えてください。申し訳ありません!

14
Gbps

この操作がパフォーマンスのボトルネックであると私に納得させることは決してありません。効率的な方法は、標準Cライブラリを使用して時間を有効に活用することです。

static unsigned char gethex(const char *s, char **endptr) {
  assert(s);
  while (isspace(*s)) s++;
  assert(*s);
  return strtoul(s, endptr, 16);
}

unsigned char *convert(const char *s, int *length) {
  unsigned char *answer = malloc((strlen(s) + 1) / 3);
  unsigned char *p;
  for (p = answer; *s; p++)
    *p = gethex(s, (char **)&s);
  *length = p - answer;
  return answer;
}

コンパイルおよびテスト済み。あなたの例で動作します。

11
Norman Ramsey

これは、C++ソリューションを要求した元の質問に答えます。

istringstreamマニピュレータでhexを使用できます。

std::string hex_chars("E8 48 D8 FF FF 8B 0D");

std::istringstream hex_chars_stream(hex_chars);
std::vector<unsigned char> bytes;

unsigned int c;
while (hex_chars_stream >> std::hex >> c)
{
    bytes.Push_back(c);
}

cは、intではなく、long(またはchar、またはその他の整数型)でなければならないことに注意してください。 char(またはunsigned char)、 間違い >>オーバーロードが呼び出され、16進整数文字列ではなく、文字列から個々の文字が抽出されます。

抽出された値がchar内に収まるようにするための追加のエラーチェックは良い考えです。

28
James McNellis
  • すべての文字を繰り返します。
    • 16進数の場合、番号は(ch >= 'A')? (ch - 'A' + 10): (ch - '0')
      • アキュムレータを4ビット左シフトし、新しい桁に追加(またはOR)します。
    • スペースがあり、前の文字がスペースではなかった場合は、現在のアキュムレータ値を配列に追加し、アキュムレータをゼロにリセットします。
6
Ben Voigt

事前に解析する文字列の長さがわかっている場合(たとえば、/ procから何かを読み取っている場合)、sscanfを「hh」型修飾子とともに使用できます。これは、次の変換がdiouxXの1つであり、それを格納するためのポインターであることを指定します。符号付き文字または符号なし文字のいずれかになります。

// example: ipv6 address as seen in /proc/net/if_inet6:
char myString[] = "fe80000000000000020c29fffe01bafb";
unsigned char addressBytes[16];
sscanf(myString, "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx
%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx", &addressBytes[0],
&addressBytes[1], &addressBytes[2], &addressBytes[3], &addressBytes[4], 
&addressBytes[5], &addressBytes[6], &addressBytes[7], &addressBytes[8], 
&addressBytes[9], &addressBytes[10], addressBytes[11],&addressBytes[12],
&addressBytes[13], &addressBytes[14], &addressBytes[15]);

int i;
for (i = 0; i < 16; i++){
    printf("addressBytes[%d] = %02x\n", i, addressBytes[i]);
}

出力:

addressBytes[0] = fe
addressBytes[1] = 80
addressBytes[2] = 00
addressBytes[3] = 00
addressBytes[4] = 00
addressBytes[5] = 00
addressBytes[6] = 00
addressBytes[7] = 00
addressBytes[8] = 02
addressBytes[9] = 0c
addressBytes[10] = 29
addressBytes[11] = ff
addressBytes[12] = fe
addressBytes[13] = 01
addressBytes[14] = ba
addressBytes[15] = fb
3
Diego Medaglia

「古い」sscanf()関数を使用します。

string s_hex = "E8 48 D8 FF FF 8B 0D"; // source string
char *a_Char = new char( s_hex.length()/3 +1 ); // output char array

for( unsigned i = 0, uchr ; i < s_hex.length() ; i += 3 ) {
    sscanf( s_hex.c_str()+ i, "%2x", &uchr ); // conversion
    a_Char[i/3] = uchr; // save as char
  }
delete a_Char;
2
amigo

純粋なC実装の場合、sscanf(3)に自分が何をするかを説得できると思います。入力文字列に2文字の16進値しか含まれない限り、これは移植可能である必要があると思います(コンパイラをなだめるためのやや危険な型強制を含む)。

#include <stdio.h>
#include <stdlib.h>


char hex[] = "E8 48 D8 FF FF 8B 0D";
char *p;
int cnt = (strlen(hex) + 1) / 3; // Whether or not there's a trailing space
unsigned char *result = (unsigned char *)malloc(cnt), *r;
unsigned char c;

for (p = hex, r = result; *p; p += 3) {
    if (sscanf(p, "%02X", (unsigned int *)&c) != 1) {
        break; // Didn't parse as expected
    }
    *r++ = c;
}
0
bjg