I'm havig some trouble figuring out how to conver my uint8 vector to stringstream.
On input im getting vector of uint8, first 9 bytes are flags which i dont need in my string stream, next 2 bytes are some data i need as string, let's call them "name1", next 2 bytes are another name, lest call it "name2", then comes 4 bytes which are some uint32 number(but writen as 4 uint8 bytes), lets call it just "number". Now i need to pass these data to stringstream but:
name1 and number2 need to be written as bytes (byte 0x52 -> char[2]={"5","2"})
number needs to be casted to uint32
all variables need to be seperated by semicolons in final stringstream
so if im getting a vector like this one:
---some 9 bytes---, 0x05, 0x00, 0x01,0x00,0x00,0x00,0x08,0x0E, ---some other data---
i need stringstream to be like this:
"0500;0100;2062;"
i have managed to figure out how to cast number to uint32:
uint8_t tab[4];
for(int i=4; i!=0; --i)
{
tab[4-i]=data[i+14];
}
uint32_t* var = (uint32_t*)tab;
is there some better way to do this?
EDIT:
How can i pass uint8 values to string as characters?
example:
byte ouput: 0x05
string output: 05
can i put string to stringstream using "<<" operator or is it not recommended?
if you have a string, just loop on the string and output the characters to your stringstream
std::string name = "name1";
std::stringstream ss;
for(auto c : name)
ss << std::static_cast<int>(c);
ss << ";";
As a side note:
uint32_t* var = (uint32_t*)tab;
is totally useless, you don't need a pointer.
i'm not sure this will solve your problem, but i would take a different approach.
i would use a struct to describe the underlying protocol and then continue with that. example:
struct dx{
uint8 _junk0[9];
char name1[2];
char name2[2];
uint32 num;
} __attribute__((packed));
uint8 *input;
dx* struct=(dx*)input;
printf("%d",dx->num);
Related
I'm trying to convert a byte array into a string using the following code.
String b = String((char*)buffer2);
but when I output the string I get a very strange result that contains white spaces and special characters that normally shouldn't be in the string.
buffer2 is of the type byte and its length is 18.
this is how its declared:
byte buffer2[18];
When I use the following code to print the byte array I get the results I expect.
for (uint8_t i = 0; i < 16; i++) {
Serial.write(buffer2[i] );
}
I'm wondering how I can convert a byte array to a string the proper way.
I have a char array which represents a GUID as bytes (not as chars) but I have to reverse half of the array.
That happened because I used sscanf to convert a GUID string into char array (which represents bytes) using:
sscanf(strguid,"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
,&arr[0],&arr[1],
,&arr[2],&arr[3],....,&arr[15]);
The array I have is for example:
2EC5D8AA85E74B5E872462155EAA9D51
and I have to reverse it so it will give the right GUID:
AAD8C52EE7855E4B872462155EAA9D51
What I tried it the following:
unsigned int temp;
memcpy(&temp,&arr[0],sizeof(char));
memcpy(&arr[0],&arr[3],sizeof(char));
memcpy(,&arr[3],&temp,sizeof(char));
And so on. (The second with the third, the fifth with the sixth and the seventh with the eighth)
Is there an easier way to do that?
If i understand you problem correctly you need change endianness of 3 first members of GUID struct
typedef struct {
unsigned long Data1;
unsigned short Data2;
unsigned short Data3;
byte Data4[ 8 ];
} GUID;
You can try this
std::reverse(guid_, guid_ + 4);
std::reverse(guid_ + 4, guid_ + 6);
std::reverse(guid_ + 6, guid_ + 8);
But i'd prefer changing sscanf format like this
const char *string_ = "AAD8C52E-E785-5E4B-8724-62155EAA9D51";
GUID guid_;
sscanf(string_, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
&guid_.Data1, &guid_.Data2, &guid_.Data3,
&guid_.Data4[0], &guid_.Data4[1], &guid_.Data4[2], &guid_.Data4[3], &guid_.Data4[4], &guid_.Data4[5], &guid_.Data4[6], &guid_.Data4[7]);
Be advised that you need to check input string length to avoid shorter string parsing
So I need a little help, I've currently got a text file with following data in it:
myfile.txt
-----------
b801000000
What I want to do is read that b801 etc.. data as bits so I could get values for
0xb8 0x01 0x00 0x00 0x00.
Current I'm reading that line into a unsigned string using the following typedef.
typedef std::basic_string <unsigned char> ustring;
ustring blah = reinterpret_cast<const unsigned char*>(buffer[1].c_str());
Where I keep falling down is trying to now get each char {'b', '8' etc...} to really be { '0xb8', '0x01' etc...}
Any help is appreciated.
Thanks.
I see two ways:
Open the file as std::ios::binary and use std::ifstream::operator>> to extract hexadecimal double bytes after using the flag std::ios_base::hex and extracting to a type that is two bytes large (like stdint.h's (C++0x/C99) uint16_t or equivalent). See #neuro's comment to your question for an example using std::stringstreams. std::ifstream would work nearly identically.
Access the stream iterators directly and perform the conversion manually. Harder and more error-prone, not necessarily faster either, but still quite possible.
strtol does string (needs a nullterminated C string) to int with a specified base
Kind of a dirty way to do it:
#include <stdio.h>
int main ()
{
int num;
char* value = "b801000000";
while (*value) {
sscanf (value, "%2x", &num);
printf ("New number: %d\n", num);
value += 2;
}
return 0;
}
Running this, I get:
New number: 184
New number: 1
New number: 0
New number: 0
New number: 0
I'm parsing a string that follows a predictable pattern:
1 character
an integer (one or more digits)
1 colon
a string, whose length came from #2
For example:
s5:stuff
I can see easily how to parse this with PCRE or the like, but I'd rather stick to plain string ops for the sake of speed.
I know I'll need to do it in 2 steps because I can't allocate the destination string until I know its length. My problem is gracefully getting the offset for the start of said string. Some code:
unsigned start = 0;
char type = serialized[start++]; // get the type tag
int len = 0;
char* dest = NULL;
char format[20];
//...
switch (type) {
//...
case 's':
// Figure out the length of the target string...
sscanf(serialized + start, "%d", &len);
// <code type='graceful'>
// increment start by the STRING LENGTH of whatever %d was
// </code>
// Don't forget to skip over the colon...
++start;
// Build a format string which accounts for length...
sprintf(format, "%%%ds", len);
// Finally, grab the target string...
sscanf(serialized + start, format, string);
break;
//...
}
That code is roughly taken from what I have (which isn't complete because of the issue at hand) but it should get the point across. Maybe I'm taking the wrong approach entirely. What's the most graceful way to do this? The solution can either C or C++ (and I'd actually like to see the competing methods if there are enough responses).
You can use the %n conversion specifier, which doesn't consume any input - instead, it expects an int * parameter, and writes the number of characters consumed from the input into it:
int consumed;
sscanf(serialized + start, "%d%n", &len, &consumed);
start += consumed;
(But don't forget to check that sscanf() returned > 0!)
Use the %n format specifier to write the number of characters read so far to an integer argument.
Here's a C++ solution, it could be better, and is hard-coded specifically to deal with your example input, but shouldn't require much modification to get working.
std::stringstream ss;
char type;
unsigned length;
char dummy;
std::string value;
ss << "s5:Helloxxxxxxxxxxx";
ss >> type;
ss >> length;
ss >> dummy;
ss.width(length);
ss >> value;
std::cout << value << std::endl;
Disclaimer:
I'm a noob at C++.
You can probably just use atoi which will ignore the colon.
e.g. len = atoi(serialized + start);
The only thing with atoi is that if it returns zero it could mean either the conversion failed, or that the length was truly zero. So it's not always the most appropriate function.
if you replace you colon with a space scanf will stop on it and you can get the size malloc the size then run another scanf to get the rest of the string`
int main (int argc, const char * argv[]) {
char foo[20];
char *test;
scanf("%s",foo); //"hello world"
printf("foo = %s\n", foo);//prints hello
//get size
test = malloc(sizeof(char)* 10);//replace 10 with your string size
scanf("%s", test);
printf("test = %s\n", test);//prints world
return 0;
}
`
Seems like the format is overspecified... (using a variable length field to specify the length of a variable length field).
If you're using GCC, I'd suggest
if (sscanf(serialized,"%c%d:%as",&type,&len,&dest)<3) return -1;
/* use type, dest; ignore len */
free(dest);
return 0;
I'm trying to use istringstream to recreate an encoded wstring from some memory. The memory is laid out as follows:
1 byte to indicate the start of the wstring encoding. Arbitrarily this is '!'.
n bytes to store the character length of the string in text format, e.g. 0x31, 0x32, 0x33 would be "123", i.e. a 123-character string
1 byte separator (the space character)
n bytes which are the wchars which make up the string, where wchar_t's are 2-bytes each.
For example, the byte sequence:
21 36 20 66 00 6f 00 6f 00
is "!6 f.o.o." (using dots to represent char 0)
All I've got is a char* pointer (let's call it pData) to the start of the memory block with this encoded data in it. What's the 'best' way to consume the data to reconstruct the wstring ("foo"), and also move the pointer to the next byte past the end of the encoded data?
I was toying with using an istringstream to allow me to consume the prefix byte, the length of the string, and the separator. After that I can calculate how many bytes to read and use the stream's read() function to insert into a suitably-resized wstring. The problem is, how do I get this memory into the istringstream in the first place? I could try constructing a string first and then pass that into the istringstream, e.g.
std::string s((const char*)pData);
but that doesn't work because the string is truncated at the first null byte. Or, I could use the string's other constructor to explicitly state how many bytes to use:
std::string s((const char*)pData, len);
which works, but only if I know what len is beforehand. That's tricky given that the data is variable length.
This seems like a really solvable problem. Does my rookie status with strings and streams mean I'm overlooking an easy solution? Or am I barking up the wrong tree with the whole string approach?
Try setting your stringstream's rdbuf:
char* buffer = something;
std::stringbuf *pbuf;
std::stringstream ss;
std::pbuf=ss.rdbuf();
std::pbuf->sputn(buffer, bufferlength);
// use your ss
Edit: I see that this solution will have a similar problem to your string(char*, len) situation. Can you tell us more about your buffer object? If you don't know the length, and it isn't null terminated, it's going to be very hard to deal with.
Is it possible to modify how you encode the length, and make that a fixed size?
unsigned long size = 6; // known string length
char* buffer = new char[1 + sizeof(unsigned long) + 1 + size];
buffer[0] = '!';
memcpy(buffer+1, &size, sizeof(unsigned long));
buffer should hold the start indicator (1 byte), the actual size (size of unsigned long), the delimiter (1 byte) and the text itself (size).
This way, you could get the size "pretty" easy, then set the pointer to point beyond the overhead, and then use the len variable in the string constructor.
unsigned long len;
memcpy(&len, pData+1, sizeof(unsigned long)); // +1 to avoid the start indicator
// len now contains 6
char* actualData = pData + 1 + sizeof(unsigned long) + 1;
std::string s(actualData, len);
It's low level and error prone :) (for instance if you read anything that isn't encoded the way that you expect it to be, the len can get pretty big), but you avoid dynamically reading the length of the string.
It seems like something on this order should work:
std::wstring make_string(char const *input) {
if (*input != '!')
return "";
char length = *++input;
return std::wstring(++input, length);
}
The difficult part is dealing with the variable length of the size. Without something to specify the length it's hard to guess when to stop treating the data as specifying the length of the string.
As for moving the pointer, if you're going to do it inside a function, you'll need to pass a reference to the pointer, but otherwise it's a simple matter of adding the size you found to the pointer you received.
It's tempting to (ab)use the (deprecated but nevertheless standard) std::istrstream here:
// Maximum size to read is
// 1 for the exclamation mark
// Digits for the character count (digits10() + 1)
// 1 for the space
const std::streamsize max_size = 3 + std::numeric_limits<std::size_t>::digits10;
std::istrstream s(buf, max_size);
if (std::istream::traits_type::to_char_type(s.get()) != '!'){
throw "missing exclamation";
}
std::size_t size;
s >> size;
if (std::istream::traits_type::to_char_type(s.get()) != ' '){
throw "missing space";
}
std::wstring(reinterpret_cast<wchar_t*>(s.rdbuf()->str()), size/sizeof(wchar_t));