6

16進表現からデコードするときに、(潜在的に非常に長い)文字列をchar * s = "2f0a3f"それが表す実際のバイトに変換する必要があります。現在私はこれをやっていますが、それは不格好で間違っていると感じています。

  size_t hexlength = strlen(s);
  size_t binlength = hexlength / 2;

  unsigned char * buffer = malloc(binlength);
  long i = 0;
  char a, b;

  for (; i < hexlength; i += 2) {
    a = s[i + 0]; b = s[i + 1];
    buffer[i / 2] =
      ((a < '9' ? a - '0' : a - 'a' + 10) << 4) + (b < '9' ? b - '0' : b - 'a' + 10);
  }

これについて私は2つのことを醜いと思います。

  1. バッファにプッシュするたびに2で割る方法
  2. 16進数の10進値を計算するための条件付きロジック

もっと良い方法はありますか?できれば、依存関係を追加する必要があるものを使用しないでください(クロスプラットフォームの問題を最小限に抑えてこのコードを出荷したいため)。私のビット単位の数学はひどいです;)

注:データは、すべて小文字であり、16進ペアの正しい文字列であることが事前に検証されています。

4

7 に答える 7

6
/* allocate the buffer */
char * buffer = malloc((strlen(s) / 2) + 1);

char *h = s; /* this will walk through the hex string */
char *b = buffer; /* point inside the buffer */

/* offset into this string is the numeric value */
char xlate[] = "0123456789abcdef";

for ( ; *h; h += 2, ++b) /* go by twos through the hex string */
   *b = ((strchr(xlate, *h) - xlate) * 16) /* multiply leading digit by 16 */
       + ((strchr(xlate, *(h+1)) - xlate));

追加するために編集

80x86アセンブリ言語では、strchr()の中心は基本的に1つの命令であり、ループしません。

また、これは境界チェックを行わず、Unicodeコンソール入力では機能せず、無効な文字が渡されるとクラッシュします。

また、いくつかの深刻なタイプミスを指摘してくれた人々に感謝します。

于 2012-09-21T17:41:00.120 に答える
4

それが大きな違いを生むというわけではありませんが、私は除算を掛け算します。a-fまた、文字セット内で隣接していないプラットフォームに移植したい場合があるため、数字コードを分割する価値があります(冗談だけです!)

  inline int digittoint(char d) {
    return ((d) <= '9' ? (d) - '0' : (d) - 'a' + 10);
  }
  #define digittoint(d) ((d) <= '9' ? (d) - '0' : (d) - 'a' + 10)

  size_t hexlength = strlen(s);
  size_t binlength = hexlength / 2;

  unsigned char * buffer = malloc(binlength);
  long i = 0;
  char a, b;

  for (; i < binlength; ++i) {
    a = s[2 * i + 0]; b = s[2 * i + 1];
    buffer[i] = (digittoint(a) << 4) | digittoint(b);
  }

私はあなたの数字から整数への実装のバグを修正し、+ビット単位またはそれがあなたの意図をよりよく表現するという理由でを置き換えました。

次に、実験して、digittoint上記の条件付き演算、、、strspnまたはルックアップテーブルの最適な実装を見つけることができます。

これが可能なブランチレス実装です-ボーナス!-大文字で機能します:

inline int digittoint(char d) {
    return (d & 0x1f) + ((d >> 6) * 0x19) - 0x10;
}
于 2012-09-21T17:48:48.707 に答える
1

次のようなものを試してください。

const unsigned char bin[128] =
{
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
    -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
};

int hexlength = strlen(s); 
int binlength = (hexlength / 2); 

unsigned char * buffer = (unsigned char *) malloc(binlength); 
if (buffer)
{
    char *hex = s; 

    unsigned char *buf = buffer;
    unsigned char b, c;

    int ok = 1;

    for (int i = 0; i < hexlength; i += 2)
    { 
        b = bin[*hex++];
        c = bin[*hex++];

        if ((b == -1) || (c == -1))
        {
            ok = 0;
            break;
        )

        *buf++ = ((b << 4) | c); 
    }

    if (ok == 1)
    {
        // use buffer as needed, up to binlength number of bytes...
    }

    free(buffer);
} 
于 2012-09-21T17:50:35.527 に答える
0

16進数から10進数に変換された(文字列内の)数値が必要な場合はatol()sprintf()

バイトごとに実行する必要がある場合は、各バイトをバッファリングできます。各バッファがいっぱいになると、次のようにsprintfに渡します。

char *hexRep;
char *decRep;
long int decVal;
...
decVal = atol(hexRep);
sprintf(decRep, "%u", decVal);

これらは両方ともCの標準ライブラリにあります。各バイトの文字列表現を取得したら、それらを。と連結するだけですstrcat()

于 2012-09-21T17:38:49.640 に答える
0

ここでは、MISRAに準拠するためのいくつかの小さな改善があります。名前は紛らわしかった。

static inline uint8_t HexcharToInt(char c) {
    char result = 16;
    if (('0' <= c) && (c <= '9')) {
        result = c - '0';
    } else if (('a' <= c) && (c <= 'f')) {
        result = c + 10 - 'a';
    } else if (('A' <= c) && (c <= 'F')) {
        result = c + 10 - 'A';
    }
    return (uint8_t) result;
}

uint8_t *array = NULL;

size_t hexstringToArray(char *hexstring) {
    size_t len    = (strlen(hexstring) + 1) / 2; // Aufrunden
    if (array != NULL) {
        free(array);
        array = NULL;
    }
    array = (uint8_t*) malloc(len);
    uint8_t *arr = array;
    for (size_t i = 0; (i < len) && (len > 0); i++) {
        *arr = 0U;
        for (uint8_t shift = 8U; (shift > 0U) && (len > 0); ) {
            shift -= 4U;
            uint8_t curInt = HexcharToInt(*hexstring++);
            if (curInt >= 16U) {
                len = 0;
            } else {
                *arr |= ((uint8_t) curInt << shift);
            }
        }
        arr++;
    }
    return len;
}
于 2017-11-08T09:44:15.353 に答える
0

文字列を取得し、変換結果を境界と整合性チェックを使用して特定のNサイズのバイト配列にバイトごとにコピーする、より単純な関数を思いつきました。

int8_t convert_str_to_bytes(uint8_t *byte_array, char* str, size_t n)
{
    char *hex_match = "0123456789ABCDEF";
    int i, j = 0;
    char cbuf[3];
    long ibuf;

    if (strlen(str) < n) {
            printf("ERROR: String is shorter than specified size.\n");
            return -1;
    }

    for (i = 0; i < n; i += 2) {

            strncpy(cbuf, &str[i], 2);

            if (strspn(cbuf, hex_match) != 2) {
                    printf("ERROR: String is not a hexadecimal representation. Breaking now...\n");
                    return -1;
            }

            ibuf = strtol(cbuf, NULL, 16);

            byte_array[j] = (uint8_t)ibuf;
            ++j;
    }

    return 0;
}
于 2018-04-27T14:12:39.020 に答える
-1
inline char HexToChar(char c)
{
    if ('0' <= c && c <= '9')
    {
        return c - '0';
    }
    else if ('a' <= c && c <= 'f')
    {
        return c + 10 - 'a';
    }
    else if ('A' <= c && c <= 'F')
    {
        return c + 10 - 'A';
    }

    return -1;
}

size_t HexToBinrary( const char* hex, size_t length, char* binrary, size_t binrary_cap )
{
    if (length % 2 != 0 || binrary_cap < length / 2)
    {
        return 0;
    }

    memset(binrary, 0, binrary_cap);
    size_t n = 0;
    for (size_t i = 0; i < length; i += 2, ++n)
    {
        char high = HexToChar(hex[i]);
        if (high < 0)
        {
            return 0;
        }

        char low = HexToChar(hex[i + 1]);
        if (low < 0)
        {
            return 0;
        }

        binrary[n] = high << 4 | low;
    }
    return n;
}
于 2013-05-08T02:21:30.417 に答える