如何解析字符串到struct?

时间:2018-05-09 22:55:31

标签: c string parsing struct

我想知道是否有任何方法可以使用struct和union方法将"5ABBF13A000A01"之类的字符串转换为下一个结构:

struct xSensorData2{
    unsigned char flags;           
    unsigned int refresh_rate;
    unsigned int timestamp;
};

数据应该是:

flags = 0x01 (last byte);
refresh_rate = 0x000A (two bytes);
timestamp = 5ABBF13A (four bytes);

我在考虑下一个数据结构:

struct xData{
    union{
        struct{
            unsigned char flags:8;
            unsigned int refresh_rate:16;
            unsigned int timestamp:32;
        };
        char pcBytes[8];
    };
}__attribute__ ((packed));

但是我得到了一个12字节的结构,我认为这是因为位字段不适用于不同类型的数据。我应该将字符串转换为十六进制数组,复制到pcBytes并访问每个字段。

更新: 在stm32平台中,我使用了这段代码:

bool bDecode_HexString(char *p)
{
uint64_t data = 0;                  /* exact width type for conversion 
*/
char *endptr = p;                   /* endptr for strtoul validation */
errno = 0;                                          /* reset errno */

data = strtoull (p, &endptr, 16);    /* convert input to uint64_t */

if (p == endptr && data == 0) {     /* validate digits converted */
    fprintf (stderr, "error: no digits converted.\n");
    return false;
}
if (errno) {    /* validate no error occurred during conversion */
    fprintf (stderr, "error: conversion failure.\n");
    return false;
}
//printf ("data: 0x%" PRIx64 "\n\n", data); /* output conerted string */

sensor.flags = data & 0xFF;                             /* set flags */
sensor.rate = (data >> CHAR_BIT) & 0xFFFF;              /* set rate */
sensor.tstamp = (data >> (3 * CHAR_BIT)) & 0xFFFFFFFF;  /* set timestamp */

    return true;

/* output sensor struct */
//    printf ("sensor.flags : 0x%02" PRIx8 "\nsensor.rate  : 0x%04" PRIx16
//            "\nsensor.tstamp: 0x%08" PRIx32 "\n", sensor.flags, sensor.rate,
//            sensor.tstamp);
}

并调用函数:

char teste[50] = "5ABBF13A000A01";
bDecode_HexString(teste);

我得到data = 0x3a000a01005abbf1

1 个答案:

答案 0 :(得分:2)

如果您仍然在努力将输入分离到flags, rate & timestamp,那么评论中有关使用无符号类型将输入字符串转换为值的建议,并使用提供的确切宽度类型<stdint.h>,将避免操纵签名类型所固有的潜在问题(例如潜在的符号扩展等)

如果你想分离值并在struct中协调它们,那就100%了。这项工作分离了个人flags, rate & timestamp。您如何选择存储它们以便在代码中方便,取决于您自己。使用精确宽度类型的简单结构可以是:

typedef struct {    /* struct holding sensor data */
    uint8_t  flags;
    uint16_t rate;
    uint32_t tstamp;   
} sensor_t;

在使用char *uint64_t转换为strtoull后,您有两项主要验证检查:

  1. 利用指向要转换的字符串的指针和endptr参数来验证数字实际上是否被转换(strtoullendptr设置为点1之后的字符最后一位数转换)。您可以使用它来将endptr与转换数据的原始指针进行比较,以确认发生了转换(如果没有转换数字,原始指针和endptr将是相同的,并且返回的值将是零);以及

  2. 您在转化前设置了errno = 0;,然后在转换后再次检查,以确保在转换过程中没有发生错误。如果strtoull遇到错误,值超出范围等,则errno设置为正值。

  3. (如果您有特定的范围验证,例如,您希望将结果存储的尺寸小于转化的尺寸,例如uint32_t而不是uint64_t,则需要验证最终值可以存储在较小的类型中

  4. 一个简单的方法是:

        uint64_t data = 0;                  /* exact width type for conversion */
        ...
        char *p = argc > 1 ? argv[1] : "0x5ABBF13A000A01",  /* input */
            *endptr = p;                    /* endptr for strtoul validation */
        errno = 0;          /* reset errno */
        ...
        data = strtoull (p, &endptr, 0);    /* convert input to uint64_t */
    
        if (p == endptr && data == 0) {     /* validate digits converted */
            fprintf (stderr, "error: no digits converted.\n");
            return 1;
        }
        if (errno) {    /* validate no error occurred during conversion */
            fprintf (stderr, "error: conversion failure.\n");
            return 1;
        }
        printf ("data: 0x%" PRIx64 "\n\n", data); /* output conerted string */
    

    最后,将data中的值分隔为flags, rate & timestamp的各个值,可以通过简单的移位来完成。 ANDS,例如

        sensor_t sensor = { .flags = 0 };   /* declare instance of sensor */
        ...
        sensor.flags = data & 0xFF;                             /* set flags */
        sensor.rate = (data >> CHAR_BIT) & 0xFFFF;              /* set rate */
        sensor.tstamp = (data >> (3 * CHAR_BIT)) & 0xFFFFFFFF;  /* set timestamp */
    
        /* output sensor struct */
        printf ("sensor.flags : 0x%02" PRIx8 "\nsensor.rate  : 0x%04" PRIx16
                "\nsensor.tstamp: 0x%08" PRIx32 "\n", sensor.flags, sensor.rate,
                sensor.tstamp);
    

    完全放弃,你可以做类似的事情:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stdint.h>
    #include <inttypes.h>
    #include <errno.h>
    #include <limits.h>
    
    typedef struct {    /* struct holding sensor data */
        uint8_t  flags;
        uint16_t rate;
        uint32_t tstamp;   
    } sensor_t;
    
    int main (int argc, char **argv) {
    
        uint64_t data = 0;                  /* exact width type for conversion */
        sensor_t sensor = { .flags = 0 };   /* declare instace of sensor */
        char *p = argc > 1 ? argv[1] : "0x5ABBF13A000A01",  /* input */
            *endptr = p;                    /* endptr for strtoul validation */
        errno = 0;          /* reset errno */
    
        data = strtoull (p, &endptr, 0);    /* convert input to uint64_t */
    
        if (p == endptr && data == 0) {     /* validate digits converted */
            fprintf (stderr, "error: no digits converted.\n");
            return 1;
        }
        if (errno) {    /* validate no error occurred during conversion */
            fprintf (stderr, "error: conversion failure.\n");
            return 1;
        }
        printf ("data: 0x%" PRIx64 "\n\n", data); /* output conerted string */
    
        sensor.flags = data & 0xFF;                             /* set flags */
        sensor.rate = (data >> CHAR_BIT) & 0xFFFF;              /* set rate */
        sensor.tstamp = (data >> (3 * CHAR_BIT)) & 0xFFFFFFFF;  /* set timestamp */
    
        /* output sensor struct */
        printf ("sensor.flags : 0x%02" PRIx8 "\nsensor.rate  : 0x%04" PRIx16
                "\nsensor.tstamp: 0x%08" PRIx32 "\n", sensor.flags, sensor.rate,
                sensor.tstamp);
    
        return 0;
    }
    

    示例使用/输出

    $ ./bin/struct_sensor_bitwise
    data: 0x5abbf13a000a01
    
    sensor.flags : 0x01
    sensor.rate  : 0x000a
    sensor.tstamp: 0x5abbf13a
    

    仔细看看,如果您有其他问题,请告诉我。