问题
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:
utilize both the pointer to the string to convert and the
endptr
parameter to validate that digits were in fact converted (strtoull
setsendptr
to point 1-character after the last digit converted). You use this to compareendptr
with the original pointer for the data converted to confirm that a conversion took place (if no digits were converted the original pointer andendptr
will be the same and the value returned will be zero); andyou set
errno = 0;
before the conversion and then check again after the conversion to insure no error occurred during the conversion itself. Ifstrtoull
encounters an error, value exceeds range, etc..,errno
is set to a positive value.(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 ofuint64_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