用于将4个十六进制字符转换为C中的整数的微小代码段

时间:2010-09-10 08:01:08

标签: c code-snippets

我需要将四个十六进制字符的字符串解析为整数。字符出现在一个较长的字符串中,并且没有分隔符 - 我只知道它们可以找到的偏移量。十六进制字符不区分大小写。偏移量为3的示例:

"foo10a4bar" -> 4260

我正在寻找一个

的片段
  • 简短(代码过多总会造成复杂性)
  • 简单(易于理解并验证它是正确的)
  • 安全(检测到无效输入并发出信号,没有潜在的内存问题)

我对使用'sscanf'系列函数有点怀疑,但是如果有一个安全的ANSI C解决方案使用它们,可以使用它们。

7 个答案:

答案 0 :(得分:3)

strtol很简单,错误处理很好:

const int OFFSET = 3, LEN = 4;
char hex[LEN + 1];
int i;
for(i = 0; i < LEN && str[OFFSET + i]; i++)
{
  hex[i] = str[OFFSET + i];
  if(!isxdigit((unsigned char) hex[i]))
  {
    // signal error, return
  }
}
if(i != LEN)
{
  // signal error, return
}
hex[LEN] = '\0';
char *end;
int result = (int) strtol(hex, &end, 16);
if(end != hex + LEN)
{
  // signal error, return
}

答案 1 :(得分:2)

这是我的尝试

#include <assert.h>

static int h2d(char c) {
    int x;
    switch (c) {
        default: x = -1; break; /* invalid hex digit */
        case '0': x = 0; break;
        case '1': x = 1; break;
        case '2': x = 2; break;
        /* ... */
        case 'E': case 'e': x = 14; break;
        case 'F': case 'f': x = 15; break;
    }
    return x;
}

int hex4(const char *src, int offset) {
    int tmp, val = 0;
    tmp = h2d(*(src+offset+0)); assert(tmp >= 0); val += tmp << 12;
    tmp = h2d(*(src+offset+1)); assert(tmp >= 0); val += tmp << 8;
    tmp = h2d(*(src+offset+2)); assert(tmp >= 0); val += tmp << 4;
    tmp = h2d(*(src+offset+3)); assert(tmp >= 0); val += tmp;
    return val;
}

当然,而不是assert使用您首选的验证方法!

你可以像这样使用它

int val = hex4("foo10a4bar", 3);

答案 2 :(得分:2)

通常最好尽可能使用标准函数,以获得简洁明了的代码:

#define HEXLEN 4

long extract_hex(const char *src, size_t offset)
{
    char hex[HEXLEN + 1] = { 0 };
    long val;

    if (strlen(src) < offset + HEXLEN)
        return -1;

    memcpy(hex, src + offset, HEXLEN);

    if (strspn(hex, "0123456789AaBbCcDdEeFf") < HEXLEN)
        return -1;

    errno = 0;
    val = strtol(hex, NULL, 16);

    /* Out of range - can't occur unless HEXLEN > 7 */
    if (errno)
        return -1;

    return val;
}

答案 3 :(得分:1)

这是基于字符算术的替代方案:

int hexdigits(char *str, int ndigits)
{
    int i;
    int n = 0;
    for (i=0; i<ndigits; ++i) {
        int d = *str++ - '0';
        if (d > 9 || d < 0)
            d += '0' - 'A' + 10;
        if (d > 15 || d < 0)
            d += 'A' - 'a';
        if (d > 15 || d < 0)
            return -1;
        n <<= 4;
        n |= d;
    }
    return n;
}

它应该处理两种情况下的数字,并且适用于ASCII和EBCDIC。使用超过7位的数字会引发整数溢出,并且可以使用-1作为错误值,与有效转换无法区分。

只需使用添加到基本字符串的偏移量调用它:例如w = hexdigits(buf+3, 4);表示将3个字符的建议偏移量存储到buf中存储的字符串中。

编辑:这是一个版本较少的版本,可以保证适用于ASCII。我有理由相信它也适用于EBCDIC,但没有任何关于这种风格的文字可以证明它。

另外,我修正了一个愚蠢的疏忽,并使累加器变为int而不是unsigned short。它不会影响4位数的情况,但它只使用16位数而不是int的全部容量溢出。

int hexdigits2(char *str, int ndigits)
{
    int i;
    int n = 0;
    for (i=0; i<ndigits; ++i) {
        unsigned char d = *str++ - '0';
        if (d > 9)
            d += '0' - 'A' + 10;
        if (d > 15)
            d += 'A' - 'a';
        if (d > 15)
            return -1;
        n <<= 4;
        n |= d;
    }
    return n;
}

用法与早期版本相同,但生成的代码可能会小一些。

答案 4 :(得分:0)

现在我自己尝试了一下,我想了一下 - 我不确定这是最好的,所以我会等一会儿,然后接受对我来说最好的答案。

    val = 0;
    for (i = 0; i < 4; i++) {
        val <<= 4;
        if (ptr[offset+i] >= '0' && ptr[offset+i] <= '9')
            val += ptr[offset+i] - '0';
        else if (ptr[offset+i] >= 'a' && ptr[offset+i] <= 'f')
            val += (ptr[offset+i] - 'a') + 10;
        else if (ptr[offset+i] >= 'A' && ptr[offset+i] <= 'F')
            val += (ptr[offset+i] - 'A') + 10;
        else {
            /* signal error */
        }
    }

答案 5 :(得分:0)

/* evaluates the first containing hexval in s */
int evalonehexFromStr( const char *s, unsigned long *val )
{
  while( *s )
   if( 1==sscanf(s++, "%04lx", val ) )
    return 1;
  return 0;
}

它适用于4个十六进制数字,例如:

unsigned long result;
if( evalonehexFromStr("foo10a4bar", &result) )
  printf("\nOK - %lu", result);

如果您需要其他十六进制数字大小,请将“4”替换为您的大小,或者对于任何十六进制值,请将“%lx”替换为最大MAX_ULONG的值。

答案 6 :(得分:-1)

Code

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

int main(int argc, char **argv)
{
    int offset = atoi(argv[2]);
    argv[1][offset + 4] = '\0';
    printf("%lu\n", strtol(argv[1] + offset, NULL, 0x10));
}

用法

matt@stanley:$ make small_hex_converter
cc     small_hex_converter.c   -o small_hex_converter
matt@stanley:$ ./small_hex_converter f0010a4bar 3
4260