简体   繁体   English

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

[英]Tiny snippet for converting 4 hex characters to an integer in C

I need to parse strings of four hex characters to an integer. 我需要将四个十六进制字符的字符串解析为整数。 The characters appear inside a longer string, and there are no separators - I just know the offset they can be found in. The hex characters are case insensitive. 字符出现在更长的字符串中,并且没有分隔符-我只知道可以在其中找到偏移量。十六进制字符不区分大小写。 Example with offset 3: 偏移量为3的示例:

"foo10a4bar" -> 4260

I'm looking for a snippet that is 我正在寻找一个片段

  • Short (too much code always creates complexity) 简短(太多代码总会带来复杂性)
  • Simple (simple to understand and verify that it is correct) 简单(易于理解和验证正确)
  • Safe (invalid input is detected and signalled, no potential memory problems) 安全(检测到无效输入并发出信号,没有潜在的内存问题)

I'm a bit leery of using the 'sscanf' family of functions for this, but if there's a safe ANSI C solution using them, they can be used. 我有点不愿使用'sscanf'函数系列,但是如果有使用它们的安全ANSI C解决方案,则可以使用它们。

strtol is simple with good error handling: 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
}

Here's my attempt 这是我的尝试

#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;
}

Of course, instead of assert use your preferred method of validation! 当然,不要assert使用您喜欢的验证方法!

And you can use it like this 你可以这样使用它

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

It's usually best to use standard functions where you can, to get concise and simple code: 通常最好是尽可能使用标准函数,以获取简洁明了的代码:

#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;
}

Here's an alternative based on character arithmetic: 这是一种基于字符算术的替代方法:

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;
}

It should handle digits in both cases, and work for both ASCII and EBCDIC. 两种情况下都应处理数字,并且对ASCII和EBCDIC均有效。 Using it for more than 7 digits invites integer overflow, and may make the use of -1 as an error value indistinguishable from a valid conversion. 超过7位使用它会引起整数溢出,并且可能会将-1作为错误值与有效转换区分开来。

Just call it with the offset added to the base string: eg w = hexdigits(buf+3, 4); 只需在偏移量添加到基本字符串的情况下调用它即可:例如w = hexdigits(buf+3, 4); for the suggested offset of 3 chars into a string stored in buf . 建议将3个字符偏移到buf存储的字符串中。

Edit: Here's a version with fewer conditions that is guaranteed to work for ASCII. 编辑:这是一个条件较少的版本,保证可以使用ASCII。 I'm reasonably certain it will work for EBCDIC as well, but don't have any text of that flavor laying around to prove it. 我可以肯定地说,它也适用于EBCDIC,但没有任何可证明这种风味的文字。

Also, I fixed a stupid oversight and made the accumulator an int instead of unsigned short . 另外,我修复了一个愚蠢的疏忽,将累加器int而不是unsigned short It wouldn't affect the 4-digit case, but it made it overflow at only 16-bit numbers instead of the full capacity of an int . 它不会影响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;
}

Usage is the same as the earlier version, but the generated code could be a bit smaller. 用法与早期版本相同,但是生成的代码可能会小一些。

Here's my own try at it now that I thought about it for a moment - I'm not at all sure this is the best, so I will wait a while and then accept the answer that seems best to me. 现在,我想了一下,现在是我自己的尝试-我完全不确定这是最好的,因此我将稍等一下,然后接受对我来说似乎最好的答案。

    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 */
        }
    }
/* 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;
}

It works for exactly 4 hex-digits, eg: 它恰好适用于4个十六进制数字,例如:

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

If you need other hex-digit sizes, replace "4" to your size or take "%lx" for any hexval for values up to MAX_ULONG. 如果您需要其他十六进制数字大小,则将“ 4”替换为您的大小,或者对任何十六进制值取“%lx”,直到MAX_ULONG。

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));
}

Usage 用法

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM