如何将字符串解析为结构体? [英] How to parse string to struct?

查看:184
本文介绍了如何将字符串解析为结构体?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I want to know if there is any way to convert a string like "5ABBF13A000A01" to the next struct using struct and union method:

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

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

I'm thinking in next struct of data:

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

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

But I got a struct of 12 bytes, I think it's because bit fields don't work for different types of data. I should just convert string to array of hex, copy to pcBytes and have access to each field.

更新:在 stm32 平台上我使用了这个代码:

Update: In stm32 platform i used this code:

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

推荐答案

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

If you are still struggling with the separation of your input into flags, rate & timestamp, then the suggestions in the comments regarding using an unsigned type to hold your input string converted to a value, and using the exact width types provided in <stdint.h>, will avoid potential problems inherent in manipulating signed types (e.g. potential sign-extension, etc.)

如果您想分离值并在结构中协调这些值,那是 100% 好的.工作是分离单独的 标志,速率 &时间戳.您选择如何存储它们以便在您的代码中方便使用取决于您.一个使用精确宽度类型的简单结构可以是:

If you want to separate the values and coordinate those in struct, that is 100% fine. The work come in separating the individual flags, rate & timestamp. How you choose to store them so they are convenient within your code is up to you. A simple struct utilizing exact-width type could be:

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

在使用 strtoullchar *uint64_t 的转换中,您有两个主要的验证检查:

In a conversion from char * to uint64_t with strtoull, you have two primary validation checks:

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

  1. utilize both the pointer to the string to convert and the endptr parameter to validate that digits were in fact converted (strtoull sets endptr to point 1-character after the last digit converted). You use this to compare endptr with the original pointer for the data converted to confirm that a conversion took place (if no digits were converted the original pointer and endptr will be the same and the value returned will be zero); and

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

you set errno = 0; before the conversion and then check again after the conversion to insure no error occurred during the conversion itself. If strtoull encounters an error, value exceeds range, etc.., errno is set to a positive value.

(如果您有特定的范围验证,例如您想以小于转换的大小存储结果,例如 uint32_t 而不是 uint64_t>,需要验证最终值可以存储在较小的类型中)

(and if you have specific range validations, e.g. you want to store the result in a size less than that of the conversion, like uint32_t instead of uint64_t, you need to validate the final value can be stored in the smaller type)

一个简单的方法是:

    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 & 的各个值.时间戳,可以通过简单的移位来完成ANDS,例如

Finally, separating the value in data into the individual values of flags, rate & timestamp, can be done with simple shifts & ANDS, e.g.

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

总而言之,你可以做类似的事情:

Putting it altogether, you could do something similar to:

#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

检查一下,如果您还有其他问题,请告诉我.

Look things over and let me know if you have further questions.

这篇关于如何将字符串解析为结构体?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆