How to parse string to struct?

好久不见. 提交于 2021-02-07 10:18:26

问题


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

Data should be:

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

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.

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

and call the function:

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

I get data = 0x3a000a01005abbf1


回答1:


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

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;

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

  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

  2. 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.

  3. (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)

A simple approach would be:

    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 */

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

Example Use/Output

$ ./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.




回答2:


Here, you have a string of length 14, representing a 7-byte value, consisting of 14 hexadecimal values. Consider this code using strtol, which hexadecimally converts your string into a long int, and then decodes it digit-wise.

uint64_t n = strtoul(str, NULL, 16); // Convert to hexadecimal
int flags = n % 0x10;
int refresh_rate = (n / 0x100) % 0x100000;
int timestamp = n / 0x1000000;

Here is a test case (#import <stdlib.h>):

char str[16] = "5ABBF13EE00AFF";
uint64_t n = strtoul(str, NULL, 16); // Convert to hexadecimal

// Use divide and mod to extract digit segment
int flags = n % 0x100;
int refresh_rate = (n / 0x100) % 0x10000;
int timestamp = n / 0x1000000;
// Print timestamp, refresh rate, and flags
printf("%p %p %p", timestamp, refresh_rate, flags);

The expected result is 0x5abbf13e 0xe00a 0xff as expected.



来源:https://stackoverflow.com/questions/50263176/how-to-parse-string-to-struct

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!