【问题标题】:How to extract values from a string in hexadecimal in a C program?如何在 C 程序中从十六进制字符串中提取值?
【发布时间】:2021-10-05 13:20:45
【问题描述】:

我有一个十六进制字符串,例如:

char str[] = "40004A0060007A0034006600";

我想从中提取单个值,例如 0x40、0x00、0x4A、0x00 等。

怎么做?

【问题讨论】:

  • 查看strtol,然后特别是第三个参数,使其以 16 为基数。
  • 您至少对字符串有点熟悉吗?
  • 使用this SO question 和一些非常基本的字符串操作就可以完成这项工作。 6-7 行代码。
  • 如果您不向我们展示,我们无法告诉您为什么您的尝试不起作用。
  • 一旦发布了答案,不要完全改变问题!如果您有不同的问题,请提出新的问题。 “移动球门柱”问题往往很不受欢迎。我回滚了所有内容更改。

标签: c


【解决方案1】:
  • 将感兴趣的 2 个字节复制到一个临时的 3 字节数组中。
  • Null 终止 3 字节数组以将其转换为字符串。
  • 从 stdlib.h 对该数组调用 strtoul

或者,您可以手动对其进行解码,因为这是一件微不足道的事情。屏蔽半字节,减去一些 ASCII 值或进行查找表检查,然后将 ms 半字节乘以 16。

【讨论】:

  • 在这种情况下,“手动”的方式似乎更加简单和高效。
  • @EugeneSh。是的。最简单的取决于程序员的能力。在大学通过 C 编程入门课程的人将能够轻松地编写手动版本的代码。但是这样的人也不必问这个问题……
【解决方案2】:

注意:此答案适用于问题的revision 3。同时,问题已被修改,从而使我的答案的选项#1 无效。正如问题的 cmets 部分所指出的,这不是 OP 的错。

你有两个选择:

  1. 将字符串转换为整数类型,例如使用函数strtoulstrtoull,然后使用位移位(>>运算符)和位掩码(&运算符)得到想要的价值观。但是,由于数据类型 longlong long 可以表示的值范围的限制,此选项只能保证使用最多 8 个十六进制数字(strtoul)和 16 位数字(strtoull)。编辑:同时,问题已被修改为字符串长度超过 16 位,因此此解决方案不再可行。

  2. 直接在字符串中查找所需的值。例如,如果您正在查找第 3rd 组十六进制数字,那么您将使用 str[4]str[5] 找到它们。这将为您提供两个字符值。如果要将这两个十六进制字符转换为它们所代表的数字,则可以从这两个值创建一个字符串,然后在该字符串上使用strtoul

【讨论】:

    【解决方案3】:

    由于您似乎是初学者,因此我将任务分解为各个组成部分。这是一个非常简单的十六进制转储工具,您的代码需要执行的每一步都是它自己的例程。这是一个快速、肮脏且相当不完美的实现,但了解如何改进它将有助于您学习和编写自己的实现。

    #include <ctype.h>
    #include <stdint.h>
    #include <stdio.h>
    
    int
    nibble(uint8_t ch) {
        if ((ch >= '0') && (ch <= '9')) {
            return ch - '0';
        }
    
        if ((ch >= 'A') && (ch <= 'F')) {
            return 10 + (ch - 'A');
        }
    
        if ((ch >= 'a') && (ch <= 'f')) {
            return 10 + (ch - 'a');
        }
    
        /* should never get here if isxdigit was called first */
        return -1;
    }
    
    int
    next_byte(const char *in)
    {
    
        uint8_t hi = 16 * nibble(*in);
        uint8_t lo = nibble(*(in + 1));
    
        return hi + lo;
    }
    
    int points_to_byte(const char *in) {
        return ((*in) && isxdigit(*in))
            && (*(in + 1)) && isxdigit(*(in + 1));
    }
    
    void
    dump(const char *in) {
        /* Decide what to do with input that is not a string of hex bytes */
        for (int i = 0; points_to_byte(in + i); i += 2) {
            printf("%d\n", next_byte(in + i));
        }
    }
    
    int
    main(int argc, char *argv[]) {
        if (argc < 2) {
            puts("Need hex strings as arguments");
        }
    
        for (int i = 1; i < argc; ++i) {
            dump(argv[i]);
        }
    }
    

    将其编译成一个名为t 的可执行文件并使用您的输入运行它,这是我得到的输出:

    $ ./t 400004a005b002000113efb29f73f57589343e70e5244162edf312e303030322e313420200043472d58585858000000000032303139303833585858585858000000505230474C5043343554334C3343
    64 
    0  
    4  
    160
    ...
    67 
    52 
    195
    52
    

    想把它转换成其他字符串,比如

     char str1[] ="0x40,0x00,0x4A,0x00,0x60";
    

    由于您不控制输入字符串,因此您需要malloc 用于输出的缓冲区。这和存储转换后的输出留作练习。

    【讨论】:

    • 请注意,此解决方案仅适用于字母连续存储的字符集,例如 ASCII。例如,它不适用于EBCDIC。 ISO C 标准只保证字符'0''9' 连续存储,但不保证'A''Z',或'a''z'。但是,假设 OP 使用的是与 ASCII 兼容的字符集可能是相当安全的。
    • @AndreasWenzel 当然。如果这确实是家庭作业,那是 OP 可能会或可能不会因在作业中提及或纠正而获得额外荣誉的事情之一。
    • 其实,我只是注意到我之前的评论不太正确。您的解决方案也适用于 EBCDIC,因为字符 'A''I' 是连续存储的。您只需要将字符'A''F' 连续存储,而不是从'A''Z' 的所有字符。因此,EBCDIC 是一个坏例子。但是,可能有一些正在使用的字符集 'A''F' 不是连续存储的(不过我不知道)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-06-16
    • 1970-01-01
    • 2022-01-25
    • 2017-04-25
    • 2019-07-27
    • 2014-02-18
    • 1970-01-01
    相关资源
    最近更新 更多