Convert unsigned char to string then again to unsigned char - c++

I want to convert:
A simple unsigned char [] to string
Then again to unsigned char
This is my code:
// This is the original char
unsigned char data[14] = {
0x68,0x65,0x6c,0x6c,0x6f,0x20,0x63,0x6f,0x6d,0x70,0x75,0x74,0x65,0x72,
};
// This convert to string
string str(data, data + sizeof data / sizeof data[0]);
// And this convert to unsigned char again
unsigned char* val = new unsigned char[str.length() + 1];
strcpy_s(reinterpret_cast<char *>(val), str.length()+1 , str.c_str());
The problem is with the 2nd part, It wont convert the string to unsigned char like it was before. I think this img from locals in debug helps

One way:
#include <string>
#include <utility>
#include <cstring>
#include <memory>
#include <cassert>
int main()
{
// This is the original char
unsigned char data[14] = {
0x68,0x65,0x6c,0x6c,0x6f,0x20,0x63,0x6f,0x6d,0x70,0x75,0x74,0x65,0x72,
};
// This convert to string
std::string str(std::begin(data), std::end(data));
// And this convert to unsigned char again
auto size = std::size_t(str.length());
auto new_data = std::make_unique<unsigned char[]>(size);
std::memcpy(new_data.get(), str.data(), size);
// check
for (auto f1 = data, f2 = new_data.get(), e1 = f1 + size ; f1 != e1 ; ++f1, ++f2)
{
assert(*f1 == *f2);
}
}

Related

C++ std::string array to unsigned char array conversion

I have a std::string array which I need to convert to an unsigned char array so that I can use this array with third-party library which only accepts unsigned char array.
let say my array is
std::string array[3];
array[0] = "a105b";
array[1] = "c258e"
array[2] = "ff587";
I need to transfer this array into:
unsigned char cArray[3][5];
I can do hardwire the unsigned char as below:
unsigned char cArray[3][5] = {"a105b", "c258e", "ff587"};
but I was unable to find a way to do it using C++ code to transfer the data from the std::string array to the unsigned char array.
You could make a function that loops through the two arrays and copies from one to the other.
Example:
#include <algorithm>
#include <iostream>
#include <string>
template<size_t R, size_t N>
void foo(const std::string(&src)[R], unsigned char(&dest)[R][N]) {
// both `src` and `dest` must be arrays with `R` rows
// `N` is how many unsigned chars each inner array in `dest` has
for(size_t idx = 0; idx < R; ++idx) {
// Copy from `src[idx]` to `dest[idx]`
// Copy at most `N` chars but no more than the length of the string + 1
// for the null terminator:
std::copy_n(src[idx].c_str(), std::min(N, src[idx].size() + 1), dest[idx]);
// Add the below line if the arrays in cArray are supposed to
// be null terminated strings:
//dest[idx][N - 1] = '\0';
}
}
int main() {
std::string array[3];
array[0] = "a105b";
array[1] = "c258e";
array[2] = "ff587";
unsigned char cArray[3][5];
foo(array, cArray);
}
I can do hardwire the unsigned char as below
unsigned char cArray[3][5] = {"a105b", "c258e", "ff587"};
No, that's not valid in C++. You would have to make the inner array [6] in C++:
unsigned char cArray[3][6] = {"a105b", "c258e", "ff587"};
In code it could look like this:
#include <array>
#include <algorithm>
#include <cstring>
#include <string>
template<typename to_type, size_t buf_size, size_t number_of_strings>
void to_array(const std::array<std::string, number_of_strings>& input,
to_type (&output)[number_of_strings][buf_size])
{
for (std::size_t n = 0; n < number_of_strings; ++n)
{
const auto input_str = input[n].c_str();
// for input string include trailing 0 on input so add one to length
const auto copy_len = std::min(input[n].length()+1, buf_size);
std::memcpy(output[n], input_str, copy_len);
}
}
int main()
{
std::array<std::string, 3> input_array{ "a105b", "c258e", "ff587" };
unsigned char c_array[3][6];
to_array<unsigned char, 6>(input_array, c_array);
return 0;
}
It showed me again that 'c' style arrays are not nice to work with.
You can't return them from a function (like you can with std::array).
So you have to pass the output array as parameter to the conversion function too.
You are not permitted to assign to a plain array. You cannot define your own assignment operator for the plain array, because C++ does not allow overload of the assignment operator except as a non-static member function of a class.
One workaround may be to define an overload for a shift operator, and give a similar syntax to an input stream.
template <unsigned N>
void operator >> (std::string s, unsigned char (&a)[N]) {
auto n = (s.size() < N) ? s.size() + 1 : N;
std::copy_n(s.c_str(), n, a);
}
/*...*/
unsigned char cArray[3][5];
array[0] >> cArray[0];
array[1] >> cArray[1];
array[2] >> cArray[2];

How to convert str number from dec to hex

Hello I have variable like this:
uint8_t *str value = "100663296";
I just wan't to convert to hexadecimal interpretation of str.
I doesn't need any math operations on this variable.
uint8_t *output value = "6000000";
How to do it correctly? I can't use convert to int and use sprintf because i don't have memmory for this on my MCU.
UPDATE: missed the part about MCU and memory limitations :) this answer won't be useful
You can try to do the following:
convert your string representation of the number to integer value (you can use int atoi( const char * str ); function
once you have your integer you can print it as HEX using, for example, sprintf function with %x as a format parameter and you integer as a value parameter
Here is a working example: https://ideone.com/axAPWH
#include <iostream>
using namespace std;
int main() {
int n;
char hex_val[50];
n = atoi("100663296");
sprintf(hex_val, "%x", n);
cout << hex_val;
return 0;
}
Could use <charconv> if available:
#include <charconv>
#include <cstdio>
#include <cstdlib>
int main() {
auto const* str = "2147483647";
errno = 0;
char* end;
long i = std::strtol(str, &end, 10);
if (end == str || errno == ERANGE)
return 1;
char out[20];
auto const [p, err] = std::to_chars(out, out + sizeof out, i, 16);
if (err != std::errc{})
return 2;
*p = 0; // null terminate
std::puts(out);
}

Converting from C++ string to unsigned char* and back

What would be the code to convert an std::string to unsigned char* and back?
str = "1234567891234567"
unsigned char* unsignedStr = ConvertStrToUnsignedCharPointer(str);
str1 = ConvertUnsignedCharToStr(unsignedStr);
str and str1 must be same with no loss of precision.
auto str1 = std::string{"1234567891234567"}; // start with string
auto chrs = str.c_str(); // get constant char* from string
auto str2 = std::string{ chrs }; // make string from char*
Unsigned char*:
auto uchrs = reinterpret_cast<unsigned char*>(const_cast<char*>(chrs));
Using vectors instead of raw pointers:
using namespace std;
auto str1 = string{"1234567891234567"};
vector<char> chars{ begin(str1), end(str1) };
vector<unsigned char> uchars;
transform(begin(str1), end(str1), back_inserter(uchars),
[](char c) { return reinterpret_cast<unsigned char>(c); });

Store value in pointer var

How to store int values in *pData and display values from it?
int id = 12;
int age = 14;
unsigned char* pData = new unsigned char[8];
memcpy(pData,&id,4);/* using memcpy to copy */
pData = pData + 4;
memcpy(pData,&age,4);/* using memcpy to copy */
// How to print value from buffer *pData
After using memcpy to copy the bytes of an int into an unsigned char buffer, the only correct way to display the ints is to copy the bytes back into an int. For example:
int temp_int;
memcpy(&temp_int, pData, sizeof temp_int);
std::cout << temp_int << '\n';
memcpy(&temp_int, pData + sizeof temp_int, sizeof temp_int);
std::cout << temp_int << '\n';
Attempting to reinterpret the buffer as an int would cause undefined behaviour by violating the strict aliasing rule.
you can change pData into int *, then you can print the int valve.
cout<<*((int *)pData);
memcpy(pData,&id,4);
This line copies the four bytes data in id to pData as int but you declared it as char * .
if you declare it as int *pData = new int[2]; then you can ptint exact values.
using namespace std;
int id = 12;
int age = 14;
unsigned int* pData = new unsigned int[2];
memcpy(pData,&id,4);
pData = pData + 1;
memcpy(pData,&age,4);
pData = pData - 1;
cout<<"ID:"<<pData[0]<<"\nAge:"<<pData[1]<<endl;
This will print the values.
If you really need to allocate memory as unsigned char*, you can do it like below:
#include <iostream>
#include <cstring>
using namespace std;
int main() {
int id = 12;
int age = 14;
// const pointer to buffer which can contain 2 ints
unsigned char* const pData = new unsigned char[2*sizeof(int)];
// non-const pointer to operate on data
unsigned char* pDataPtr = pData;
memcpy(pDataPtr,&id,sizeof(int));
pDataPtr += sizeof(int);
memcpy(pDataPtr,&age,sizeof(int));
std::cout<<*reinterpret_cast<int*>(pData)<<std::endl;
std::cout<<*reinterpret_cast<int*>(pData + sizeof(int))<<std::endl;
delete [] pData;
return 0;
}
You can print pData using reinterpret_cast operator :
#include <iostream>
#include <cstring>
int main(void)
{
int id = 12;
int age = 14;
const size_t size = sizeof(int);
unsigned char* pData = new unsigned char[2*size];
memcpy(pData,&id,size);/* using memcpy to copy */
pData = pData + size;
memcpy(pData,&age,size);/* using memcpy to copy */
std::cout<<*reinterpret_cast<int*>(pData)<<std::endl;
pData = pData - size;
std::cout<<*reinterpret_cast<int*>(pData)<<std::endl;
delete []pData;
return 0;
}

iconv only works once

I try to make method which converts s-jis string to utf-8 string using iconv.
I wrote a code below,
#include <iconv.h>
#include <iostream>
#include <stdio.h>
using namespace std;
#define BUF_SIZE 1024
size_t z = (size_t) BUF_SIZE-1;
bool sjis2utf8( char* text_sjis, char* text_utf8 )
{
iconv_t ic;
ic = iconv_open("UTF8", "SJIS"); // sjis->utf8
iconv(ic , &text_sjis, &z, &text_utf8, &z);
iconv_close(ic);
return true;
}
int main(void)
{
char hello[BUF_SIZE] = "hello";
char bye[BUF_SIZE] = "bye";
char tmp[BUF_SIZE] = "something else";
sjis2utf8(hello, tmp);
cout << tmp << endl;
sjis2utf8(bye, tmp);
cout << tmp << endl;
}
and, output should be
hello
bye
but in fact,
hello
hello
Does anyone know why this phenomenon occurs? What's wrong with my program?
Note that "hello" and "bye" are Japanese s-jis strings in my original program, but I altered it to make program easy to see.
I think you are misusing the iconv function by passing it the global variable z. The first time you call sjis2utf8, z is decremented to 0. The second call to sjis2utf8 have no effect (z==0) and leaves tmp unchanged.
From the iconv documentation :
size_t iconv (iconv_t cd,
const char* * inbuf, size_t * inbytesleft,
char* * outbuf, size_t * outbytesleft);
The iconv function converts one multibyte character at a time, and for each character conversion it increments *inbuf and decrements *inbytesleft by the number of converted input bytes, it increments *outbuf and decrements *outbytesleft by the number of converted output bytes, and it updates the conversion state contained in cd.
You should use two separate variables for the buffers lengths :
size_t il = BUF_SIZE - 1 ;
size_t ol = BUF_SIZE - 1 ;
iconv(ic, &text_sjis, &il, &text_utf8, &ol) ;
Then check the return value of iconv and the buffers lengths for the conversion success.
#include <iconv.h>
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
const size_t BUF_SIZE=1024;
class IConv {
iconv_t ic_;
public:
IConv(const char* to, const char* from)
: ic_(iconv_open(to,from)) { }
~IConv() { iconv_close(ic_); }
bool convert(char* input, char* output, size_t& out_size) {
size_t inbufsize = strlen(input)+1;// s-jis string should be null terminated,
// if s-jis is not null terminated or it has
// multiple byte chars with null in them this
// will not work, or to provide in other way
// input buffer length....
return iconv(ic_, &input, &inbufsize, &output, &out_size);
}
};
int main(void)
{
char hello[BUF_SIZE] = "hello";
char bye[BUF_SIZE] = "bye";
char tmp[BUF_SIZE] = "something else";
IConv ic("UTF8","SJIS");
size_t outsize = BUF_SIZE;//you will need it
ic.convert(hello, tmp, outsize);
cout << tmp << endl;
outsize = BUF_SIZE;
ic.convert(bye, tmp, outsize);
cout << tmp << endl;
}
based on Kleist's answer
You must put length of entry string in third parameter of iconv.
Try:
//...
int len = strlen(text_sjis);
iconv(ic , &text_sjis, &len, &text_utf8, &z);
//...
size_t iconv (iconv_t cd,
const char* * inbuf, size_t * inbytesleft,
char* * outbuf, size_t * outbytesleft);
iconv changes the value pointed to by inbytesleft. So after your first run z is 0. To fix this you should use calculate the length of inbuf and store it in a local variable before each conversion.
It is described here: http://www.gnu.org/s/libiconv/documentation/libiconv/iconv.3.html
And since you tagged this as C++ I would suggest wrapping everything up in a nice little class, as far as I can tell from the documentation you can reuse the inconv_t gained from iconv_open for as many conversions as you'd like.
#include <iconv.h>
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
const size_t BUF_SIZE = 1024;
size_t z = (size_t) BUF_SIZE-1;
class IConv {
iconv_t ic_;
public:
IConv(const char* to, const char* from)
: ic_(iconv_open(to,from)) { }
~IConv() { iconv_close(ic_); }
bool convert(char* input, char* output, size_t outbufsize) {
size_t inbufsize = strlen(input);
return iconv(ic_, &input, &inbufsize, &output, &outbufsize);
}
};
int main(void)
{
char hello[BUF_SIZE] = "hello";
char bye[BUF_SIZE] = "bye";
char tmp[BUF_SIZE] = "something else";
IConv ic("UTF8","SJIS");
ic.convert(hello, tmp, BUF_SIZE);
cout << tmp << endl;
ic.convert(bye, tmp, BUF_SIZE);
cout << tmp << endl;
}