Как превратить шестнадцатеричную строку в массив без знака? - PullRequest
12 голосов
/ 11 июля 2010

Например, у меня есть строка cstring "E8 48 D8 FF FF 8B 0D" (включая пробелы), которую необходимо преобразовать в эквивалентный массив без знака {0xE8,0x48,0xD8,0xFF,0xFF,0x8B,0x0D}. Какой эффективный способ сделать это? Спасибо!

РЕДАКТИРОВАТЬ: я не могу использовать библиотеку STD ... так что считаю это вопросом C. Извини!

Ответы [ 7 ]

28 голосов
/ 11 июля 2010

Это ответ на оригинальный вопрос, который требовал решения 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), будет вызвана неправильная перегрузка >>, и из строки будут извлечены отдельные символы, а не шестнадцатеричные целочисленные строки.

Дополнительная проверка ошибок, чтобы убедиться, что извлеченное значение помещается в char, будет хорошей идеей.

11 голосов
/ 11 июля 2010

Вы никогда не убедите меня, что эта операция является узким местом производительности. Эффективный способ - эффективно использовать свое время, используя стандартную библиотеку 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;
}

Скомпилировано и протестировано. Работает на вашем примере.

6 голосов
/ 11 июля 2010
  • Итерация по всем символам.
    • Если у вас есть шестнадцатеричная цифра, это номер (ch >= 'A')? (ch - 'A' + 10): (ch - '0').
      • Сдвиньте влево ваш аккумулятор на четыре бита и добавьте (или ИЛИ) в новую цифру.
    • Если у вас есть пробел, а предыдущий символ не был пробелом, затем добавьте текущее значение аккумулятора в массив и сбросьте аккумулятор до нуля.
2 голосов
/ 26 февраля 2016

использовать "старую" функцию 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 голосов
/ 21 ноября 2011

Если вы знаете длину строки, которая должна быть проанализирована заранее (например, вы читаете что-то из / proc), вы можете использовать sscanf с модификатором типа 'hh', который указывает, что следующее преобразование - это diouxX и указатель для хранения будет либо подписанный символ, либо неподписанный символ.

// 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
0 голосов
/ 11 июля 2010

Для реализации на чистом C, я думаю, вы можете убедить sscanf(3) делать то, что вам нужно. Я полагаю, что это должно быть переносимым (в том числе слегка хитрым принуждением типов, чтобы успокоить компилятор), если ваша входная строка будет содержать только двухсимвольные шестнадцатеричные значения.

#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 голосов
/ 11 июля 2010

Старый способ C, сделайте это вручную ;-) (есть много более коротких способов, но я не играю в гольф, я собираюсь во время выполнения).

enum { NBBYTES = 7 };
char res[NBBYTES+1];
const char * c = "E8 48 D8 FF FF 8B 0D";
const char * p = c;
int i = 0;

for (i = 0; i < NBBYTES; i++){
    switch (*p){
    case '0': case '1': case '2': case '3': case '4':
    case '5': case '6': case '7': case '8': case '9':
      res[i] = *p - '0';
    break;
    case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
      res[i] = *p - 'A' + 10;
    break;
   default:
     // parse error, throw exception
     ;
   }
   p++;
   switch (*p){
   case '0': case '1': case '2': case '3': case '4':
   case '5': case '6': case '7': case '8': case '9':
      res[i] = res[i]*16 + *p - '0';
   break;
   case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
      res[i] = res[i]*16 + *p - 'A' + 10;
   break;
   default:
      // parse error, throw exception
      ;
   }
   p++;
   if (*p == 0) { continue; }
   if (*p == ' ') { p++; continue; }
   // parse error, throw exception
}

// let's show the result, C style IO, just cout if you want C++
for (i = 0 ; i < 7; i++){
   printf("%2.2x ", 0xFF & res[i]);
}
printf("\n");

Теперь другойтот, который допускает любое число цифр между числами, любое количество пробелов для их разделения, включая начальные или конечные пробелы (спецификации Бена):

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

int main(){
    enum { NBBYTES = 7 };
    char res[NBBYTES];
    const char * c = "E8 48 D8 FF FF 8B 0D";
    const char * p = c;
    int i = -1;

    res[i] = 0;
    char ch = ' ';
    while (ch && i < NBBYTES){
       switch (ch){
       case '0': case '1': case '2': case '3': case '4':
       case '5': case '6': case '7': case '8': case '9':
          ch -= '0' + 10 - 'A';
       case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
          ch -= 'A' - 10;
          res[i] = res[i]*16 + ch;
          break;
       case ' ':
         if (*p != ' ') {
             if (i == NBBYTES-1){
                 printf("parse error, throw exception\n");
                 exit(-1);
            }
            res[++i] = 0;
         }
         break;
       case 0:
         break;
       default:
         printf("parse error, throw exception\n");
         exit(-1);
       }
       ch = *(p++);
    }
    if (i != NBBYTES-1){
        printf("parse error, throw exception\n");
        exit(-1);
    }

   for (i = 0 ; i < 7; i++){
      printf("%2.2x ", 0xFF & res[i]);
   }
   printf("\n");
}

Нет, это не совсем запутано ... но хорошо,это выглядит так.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...