【问题标题】:Reading hex values from and saving it to unsigned char array从 unsigned char 数组中读取十六进制值并将其保存到
【发布时间】:2016-02-05 07:03:43
【问题描述】:

我有一个包含以下格式的十六进制值的文件

0x020x000x000x000x000x000x680x000x000x000x020x000x000x000x000x00

我想读取这些十六进制值并将其保存为相同格式的无符号字符数组中的十六进制值

代码片段(hex是char数组,licensebin是unsigned char数组)

fgets(hex, sizeof(hex), licenseFile);

used = 0;
while ((sscanf(hex+used, "%x", &licensebin[i]))==1)
{
    printf("%x", licensebin[i]);
    offset = 4;
    used += offset;
    i++;
}

上面的代码没有正确读取值。

【问题讨论】:

  • 文件是二进制还是字符? x020 不是十六进制字节值,即x02x20
  • licensebin的类型是什么?
  • @Pooya licensebin 是 unsigned char 类型
  • @PaulOgilvie 十六进制值限制为两个字符,因此在上面的示例中,0x02 是一个值,0x00 是另一个
  • 顺便说一句,对于错误分析,我建议使用调试器或在程序的关键位置插入调试输出。它可能会显示循环只执行一次,第一个值是 32 而不是 2。如果 gdb 对于这样的 ISO C 中的小任务太难学习,并且您可以访问 Windows 机器,只需安装免费的Visual Studio 版本并创建一个小的临时项目。那里的调试器非常用户友好。

标签: c


【解决方案1】:

scanf function (and family) 在读取其数据时是贪婪,这意味着"%x" 格式将从下一个 数字中读取前导零作为最后一个零当前号码。

在您展示的示例中,它读取的第一个数字将是 0x020 而不是 0x02

这可以通过告诉sscanf 只读取四个字符来轻松解决:

sscanf(hex+used, "%4x", &licensebin[i])

在您添加关于licensebin[i]unsigned char 的评论后,正确的格式实际上应该是

sscanf(hex+used, "%4hhx", &licensebin[i])

前缀"hh" 告诉sscanf 该参数实际上是一个指向unsigned char 的指针。您仍然需要告诉sscanf 最多只能读取四个字符,因为它仍然很贪心。

【讨论】:

  • 反对者愿意解释吗?我认为所有答案都是正确的(约阿希姆是第一个)。
  • 感谢它的工作,我不知道 sscanf 的贪婪本性。
  • 请注意,我很确定 hh 大小前缀不是完全可移植的,例如我认为 Visual C 不支持。
【解决方案2】:

我尝试了以下方法,它奏效了。使用 %x 而不是 unsigned char 时,您可能需要将许可证定义为 int:

#include <stdio.h>

int main() {

    int license[20];
    char* hex = "0x020x000x000x000x000x000x680x000x000x000x020x000x000x000x000x00";
    int used = 0;
    int i = 0;
    while((sscanf(hex+used,"%x",&license[i]))==1)
    {
        printf("%x",license[i]);
        i++;
        used += 4;
    }

}

【讨论】:

  • 不错。您可以根据要求将许可证更改为无符号字符数组,方法是使用临时 int 来保存 scanf 的结果。如果您愿意,请随时从我的答案中复制。
【解决方案3】:

也许改变这个:

while ((sscanf(hex+used, "%x", &licensebin[i]))==1)
{

到这里:

unsigned int n;
while ((sscanf(hex+used, "0x%2x", &n)==1)
{    
    licensebin[i] = (unsigned char)n;

(感谢大家提醒我 scanf 在匹配 %x 时期望的类型。)

这是为了匹配输入中的文字“0x”,并将读取的十六进制数字的数量限制为 2。

交替变化:

used = 0;
while ((sscanf(hex+used, "%x", &licensebin[i]))==1)

收件人:

used = 2;
unsigned int n;
while ((sscanf(hex+used, "%2x", &n)==1)
{    
    licensebin[i] = (unsigned char)n;

所有这些都假设 licensebin 类似于:

char *hex = "0x020x000x000x000x000x000x680x000x000x000x020x000x000x000x000x00";

也就是说,你实际上有一个“0”字符、“x”字符等。如果你的数据是二进制的,你需要一种完全不同的方法。

【讨论】:

  • 我想你的意思是char *hex= "0x020x000x000x000x000x000x680x000x000x000x020x000x000x000x000x00";
  • @PeterA.Schneider,Doh!好的,我会解决的。谢谢。
【解决方案4】:

处理十六进制字符串转换的最简单方法之一是使用strtoul,它可以读取/转换base-16strtoul 可以与指针一起使用,以逐步遍历输入字符串,提供找到的每个值的转换,然后简单地将指针移动到下一个值的开头。根据您的输入,strchr 用于定位每个 'x',然后转换后面的 2 个字符。

为了帮助完成这个过程,您可以编写一个简单的函数来提供所有错误检查和转换验证。像下面这样的东西效果很好:

/** string to unsigned long with error checking */
unsigned long xstrtoul (char *p, char **ep, int base)
{
    errno = 0;

    unsigned long tmp = strtoul (p, ep, base);

    /* Check for various possible errors */
    if ((errno == ERANGE && (tmp == ULONG_MAX)) ||
        (errno != 0 && tmp == 0)) {
        perror ("strtoul");
        exit (EXIT_FAILURE);
    }

    if (*ep == p) {
        fprintf (stderr, "No digits were found\n");
        exit (EXIT_FAILURE);
    }

    return tmp;
}

您只需最少的代码即可将该函数应用于数据文件中的所有行。例如:

#include <stdio.h>
#include <stdlib.h> /* for strtol               */
#include <string.h> /* for strchr               */
#include <limits.h> /* for INT_MIN/INT_MAX      */
#include <errno.h>  /* for errno                */

#define MAXL 256

unsigned long xstrtoul (char *p, char **ep, int base);

int main (int argc, char **argv)
{
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
    char line[MAXL] = {0};
    unsigned values[MAXL] = {0};
    int base = argc > 2 ? atoi (argv[2]) : 16;
    size_t i, idx = 0;

    if (!fp) { /* validate file open */
        fprintf (stderr, "error: file open failen '%s'.\n", argv[1]);
        return 1;
    }

    /* read each line in file (up to MAXL chars per-line) */
    while (fgets (line, MAXL, fp)) {

        char *p = line;
        char *ep = p;
        char digits[3] = {0};
        errno = 0;

        /* convert each string of digits into number */
        while (errno == 0) {

            /* skip any non-digit characters */
            if (!(p = strchr (p, 'x'))) break;
            strncpy (digits, ++p, 2);
            digits[2] = 0;  /* nul-terminate */

            /* convert string to number */
            values[idx++] = (unsigned)xstrtoul (digits, &ep, base);

            if (errno || idx == MAXL) {   /* check for error */
                fprintf (stderr, "warning: MAXL values reached.\n");
                break;
            }
            p += 2;
        }
    }
    if (fp != stdin) fclose (fp);

    /* print results */
    for (i = 0; i < idx; i++)
        printf (" values[%2zu] : 0x%02x\n", i, values[i]);

    return 0;
}

/** string to unsigned long with error checking */
unsigned long xstrtoul (char *p, char **ep, int base)
{
    errno = 0;

    unsigned long tmp = strtoul (p, ep, base);

    /* Check for various possible errors */
    if ((errno == ERANGE && (tmp == ULONG_MAX)) ||
        (errno != 0 && tmp == 0)) {
        perror ("strtoul");
        exit (EXIT_FAILURE);
    }

    if (*ep == p) {
        fprintf (stderr, "No digits were found\n");
        exit (EXIT_FAILURE);
    }

    return tmp;
}

输入文件

$ cat dat/hex3.txt
0x020x000x000x000x000x000x680x000x000x000x020x000x000x000x000x00

使用/输出

$ ./bin/fgets_xstrtoul_simple dat/hex3.txt
 values[ 0] : 0x02
 values[ 1] : 0x00
 values[ 2] : 0x00
 values[ 3] : 0x00
 values[ 4] : 0x00
 values[ 5] : 0x00
 values[ 6] : 0x68
 values[ 7] : 0x00
 values[ 8] : 0x00
 values[ 9] : 0x00
 values[10] : 0x02
 values[11] : 0x00
 values[12] : 0x00
 values[13] : 0x00
 values[14] : 0x00
 values[15] : 0x00

查看一下,如果您有任何问题,请告诉我。

【讨论】:

    猜你喜欢
    • 2023-03-30
    • 2019-10-04
    • 1970-01-01
    • 2018-09-14
    • 1970-01-01
    • 2018-02-24
    • 2014-01-24
    • 2018-07-27
    • 1970-01-01
    相关资源
    最近更新 更多