在循环中将十六进制转换为一个字符串

Convert hex to one string in a loop

我尝试将十六进制值转换成一个字符串,我已经读过这个 question 但我不知道如何以动态方式转换它,比如如果我的函数得到一个 8 的十六进制序列或9 个或更多。我如何使用一个循环来优化此代码段,该循环遍历给定的十六进制值并转换每个十六进制值(第一个除外)后跟 .?

/* 
Input: char[] ="2b06010201010300"
Output: char[]="1.3.6.1.2.1.1.3.0"
*/
void test(char * hex_arr)
{
  char string[sizeof(hex_arr)*2+1];
  string[0] = '1';
  string[1] = '.';
  string[2] = '3';
  string[3] = '.';
  sprintf(&string[4], "%x", hex_arr[1]);
  string[5] = '.';
  sprintf(&string[6], "%x", hex_arr[2]);
  // ...
  printf("Output: %s\n", string);
}

输入char[]表示一个SNMP OBJECT IDENTIFIER.

请注意,在问题中发布的代码中,string[]test() 函数中使用 sizeof(hex_arr) 声明。但是 hex_arr 是指向 char 的指针,而不是数组,因此这不会给出 string[] 的正确大小。另外,这里的string是一个自动变量,它的生命周期仅限于函数test();如果唯一的目标是打印结果,那很好,但如果结果需要在代码的其他地方使用,则必须找到另一个解决方案。

解决此问题的一种方法是将输出数组与输入数组一起传递到转换函数中。如果输入格式不可接受,函数可以 return -1,否则 returning 0,输出数组可以保存由十六进制数字输入字符串产生的标识符字符串.

输入中的每一对数字代表一个十六进制数,可以是三位十进制数,每一个三位十进制数(最后一位除外,后跟一个空终止符)在它之后添加了一个附加的分隔符;所以每对输入数字最多转换为四个字符。这意味着将 output[] 分配为 input 大小的两倍将足以存储任何结果,包括用于空终止符的 space。这实际上提供了一些额外的 space,这是需要的。

此处id_from_hexstring()函数执行转换的方法涉及在循环中使用sscanf()从输入中读取一对字符作为字符串进入临时缓冲区val_str[]. %n 转换说明符用于跟踪每次成功转换后到达的位置,将成功匹配的次数存储在 offset 中,并使用此值将指针递增到 input[] 中进行准备用于下一次转换。

strtoul()函数用于将val_str[]中存储的两位十六进制字符串转换为unsigned long值。当input[]中的第一对字符被转换时,如果转换后的值与IN_PREFIX不匹配,函数returns有一个错误代码。否则,OUT_PREFIX 将打印到 output[] 的开头,而不是转换后的值。在第一对之后,剩余的转换值将打印到 output[] 字符串的末尾。

转换循环终止后,尾随 . 分隔符留在 output[] 的末尾,最后的任务是在 returning [=21= 之前删除它] 表示成功。通过将 input[] 数组的大小(而不是字符串长度)加倍来确定 output[] 数组的大小,保证 output[] 可以容纳额外的 .分隔符。

这段代码可以做更多的事情来验证输入。就像现在一样,id_from_hexstring() 函数期望输入由有效的十六进制字符组成。这是完整的程序及其输出:

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

#define IN_PREFIX      0x2b
#define OUT_PREFIX     "1.3."
#define ID_PREFIX_ERR  -1
#define ID_PREFIX_OK   0

int id_from_hexstring(char *out, char *in);

int main(void)
{
    char input[] = "2b06010201010300";
    char output[sizeof input * 2] = "";

    if (id_from_hexstring(output, input) == -1) {
        fprintf(stderr, "Error in input format\n");
    } else {
        printf("Input: %s\n", input);
        printf("Output: %s\n", output);
    }

    return 0;
}

/* Expects that out points to a suitably sized array containing an empty
 * string, and in points to a string containing only unsigned hex digits */
int id_from_hexstring(char *out, char *in)
{
    int ret_val = ID_PREFIX_OK;
    int offset;
    char *ptr = in;
    char val_str[3];

    while (sscanf(ptr, "%2s%n", val_str, &offset) == 1) {

        unsigned long dec_val = strtoul(val_str, NULL, 16);

        /* Handle prefix; return error code if invalid */
        if (ptr == in) {
            if (dec_val != IN_PREFIX) {
            ret_val = ID_PREFIX_ERR;
            break;
            } else {
                sprintf(out, OUT_PREFIX);
                ptr += offset;
                continue;
            }
        }

        /* Print next value to identifier string */
        size_t end = strlen(out);
        sprintf(out + end, "%lu.", dec_val);
        ptr += offset;
    }

    /* Replace trailing point */
    size_t out_len = strlen(out);
    if (out_len > 0) {
        out[out_len - 1] = '[=10=]';
    }

    return ret_val;
}
Input: 2b06010201010300
Output: 1.3.6.1.2.1.1.3.0