I am working on a project that takes a Lua string and converts it into a C string – not at all difficult, of course. However, I run into trouble when attempting to convert a binary representation of a function, i.e. one produced by a call to string.dump, to a C string. I am having trouble reading the entire string.
While it is not the ultimate goal of the project, consider the following simple example where I print out the characters in a string one-by-one using a C function called chars that I have registered for use in Lua:
static void chars(char* cp) {
char* pointer = cp;
while (*pointer) {
printf("%c\n", *pointer);
++pointer;
}
return;
}
static int lua_chars(lua_State* L) {
lua_len(L, 1);
size_t len = static_cast<size_t>(lua_tonumber(L, -1)) + 1;
lua_pop(L, 1);
if (len > 0) {
char* cp = static_cast<char*>(malloc(len));
strcat(cp, lua_tostring(L, 1));
chars(cp);
free(cp);
}
return 0;
}
Calling chars from a Lua script would look like this:
chars("Hello World!")
and would print out the characters one by one with each followed by a newline.
Now to the actual issue. Consider this example where I declare a function in Lua, dump it with string.dump, and then pass that string to the function chars to print out its characters individually:
local function foo()
print("foo")
return
end
local s = assert(string.dump(foo))
chars(s)
The string s in its entirety, not printed with my function chars, looks something like this:
uaS?
xV(w#=stdin#A#$#&?&?printfoo_ENV
However, chars only prints the first five bytes:
u
a
S
(Note there are supposed to be two lines of whitespace before the 'u'.)
I am almost certain that this is due to null characters within the string, which I think interferes with lua_tostring's functionality. I have come across lua_Writer for reading chunks, but I have no idea how to use/code it. How can I successfully convert the entire string on the Lua stack to a C string?
I am almost certain that this is due to null characters within the
string
Yes, it's exactly because Lua strings can contain zeroes.
which I think interferes with lua_tostring's functionality.
And this is false. lua_tostring() works as intended. It's just strcat() you're using will only copy the data up to the nearest zero byte.
If you need to copy the string, use memcpy, passing it both the pointer to Lua string data and Lua string length (lua_len, lua_rawlen, etc).
But just for printing you don't even need to copy anything. Pass the len variable as an argument to chars(), and check that length instead of waiting for zero byte.
The Problem isn't lua_tostring but strcat which copies until it finds an null characters. Same Problem with your chars function.
That should work:
memcpy(cp, lua_tostring(L, 1), len);
chars(cp, len);
...
static void chars(char* cp, size_t len) {
for (size_t i = 0; i < len; ++i, ++cp) {
putchar(*cp);
}
}
Related
I played with the string function,i wrote the following one, obviously I set the first character in the ret string to be written in a place that is out of bounds, but instead of an exception, I get a string that has one extra place .
std::string StringManipulations::rev(std::string s)
{
std::string ret(s.size(), ' ');
for (int i = 0; i < s.size(); i++)
{
std::string ch;
ch.push_back(s[i]);
int place = s.size() -i;
ret.replace(place,1,ch);
}
return ret;
}
I write by mistake in a position that corresponds to a place that is one larger than the original string size that I assign at the beginning of the function.
Why don't we get an error ?
s = StringManipulations::rev("abcde");
std::cout << s.size();
std::cout << s;
output is : 6 _edcba
any help ?
solved: adding ch as a String adds a null terminator automatically, and by doing so we can get a new string with size+1.
C++ has a zero-overhead rule.
This means that no overhead, (like checking if an index is in-bounds) should be done unintentionally.
You don't get an exception because c++ simply doesn't verify if the index is valid.
For the extra character, this might have something to do with (regular) c strings.
In c, strings are arrays of type char (char*) without a defined size.
The end of a string is denoted with a null terminator.
C++ strings are backwards compatible, meaning that they have a null terminator too.
It's possible that you replaced the terminator with an other character but the next byte was also a zero meaning that you added one more char.
In addition to the information above about null terminators, another answer to your question is that the docs says it will only throw if the position is greater than the string size, rather than beyond the end of the string.
string replace api
I am doing this IoT based project on displaying data to connected display( I've used the MAX7219 module, in this case) with the help of nodeMCU. The idea here is that the string which is stored in my firebase database is to be display on the led display.
I've had no trouble in getting the value from the database to my nodeMCU but there is this little problem with converting that string to char array since the code i am using( Max72xx_Message_serial, which was available as an example with the max72xx library) has used char array but i can only fetch the stored data in string format. I've modified that code so as to connect with firebase but the main issue is to convert the string fetched from the database to char array.
I tried toCharArray() but it still shows conversion error.
void readfromfirebase(void)
{
static uint8_t putIndex = 0;
int n=1;
while (Firebase.available())
{
newMessage[putIndex] = (char)Firebase.getString("Submit Message"); // this line produces the error
if ((newMessage[putIndex] == '\n') || (putIndex >= BUF_SIZE-3)) // end of message character or full buffer
{
// put in a message separator and end the string
newMessage[putIndex++] = ' ';
newMessage[putIndex] = '\0';
// restart the index for next filling spree and flag we have a message waiting
putIndex = 0;
newMessageAvailable = true;
}
else if (newMessage[putIndex] != '\r')
// Just save the next char in next location
{putIndex++;}
n++;
}
}
I think you are confusing the types
getString returns a String object wich can be converted to a char[] using the methods of the String class.
I assume your newMessage is of type char[] or char*.
Then I would advise you to go for the String.c_str() method, because it returns a C style null-terminated string, meaning a char*.
See https://www.arduino.cc/reference/en/language/variables/data-types/string/functions/c_str/ for reference.
It also sets the last character of the string to 0. So methods like strlen, strcmp etc will work.
! be carefull not to modify the array returned by c_str(), if you want to modify it you chould copy the char[] or use string.toCharArray(buf, len).
Your Code might then look like the following.
String msg = Firebase.getString("Submit Message");
newMessage = msg.c_str();
// rest of your code
If newMessage is a buffer storing multiple messages, meaning char* newMessage[3].
String msg = Firebase.getString("Submit Message");
newMessage[putIndex] = msg.c_str();
// rest of your code
Be careful, because you are storing multiple characters in an array, so use strcmp to compare these arrays!
If you are new to C I would recommend reading.
https://www.cprogramming.com/tutorial/c/lesson9.html
https://www.arduino.cc/reference/en/language/variables/data-types/stringobject/ (as pointed out by #gre_gor)
I have a string like,
string str="aaa\0bbb";
and I want to copy the value of this string to a char* variable. I tried the following methods but none of them worked.
char *c=new char[7];
memcpy(c,&str[0],7); // c="aaa"
memcpy(c,str.data(),7); // c="aaa"
strcpy(c,str.data()); // c="aaa"
str.copy(c,7); // c="aaa"
How can I copy that string to a char* variable without loosing any data?.
You can do it the following way
#include <iostream>
#include <string>
#include <cstring>
int main()
{
std::string s( "aaa\0bbb", 7 );
char *p = new char[s.size() + 1];
std::memcpy( p, s.c_str(), s.size() );
p[s.size()] = '\0';
size_t n = std::strlen( p );
std::cout << p << std::endl;
std::cout << p + n + 1 << std::endl;
}
The program output is
aaa
bbb
You need to keep somewhere in the program the allocated memory size for the character array equal to s.size() + 1.
If there is no need to keep the "second part" of the object as a string then you may allocate memory of the size s.size() and not append it with the terminating zero.
In fact these methods used by you
memcpy(c,&str[0],7); // c="aaa"
memcpy(c,str.data(),7); // c="aaa"
str.copy(c,7); // c="aaa"
are correct. They copy exactly 7 characters provided that you are not going to append the resulted array with the terminating zero. The problem is that you are trying to output the resulted character array as a string and the used operators output only the characters before the embedded zero character.
Your string consists of 3 characters. You may try to use
using namespace std::literals;
string str="aaa\0bbb"s;
to create string with \0 inside, it will consist of 7 characters
It's still won't help if you will use it as c-string ((const) char*). c-strings can't contain zero character.
There are two things to consider: (1) make sure that str already contains the complete literal (the constructor taking only a char* parameter might truncate at the string terminator char). (2) Provided that str actually contains the complete literal, statement memcpy(c,str.data(),7) should work. The only thing then is how you "view" the result, because if you pass c to printf or cout, then they will stop printing once the first string terminating character is reached.
So: To make sure that your string literal "aaa\0bbb" gets completely copied into str, use std::string str("aaa\0bbb",7); Then, try to print the contents of c in a loop, for example:
std::string str("aaa\0bbb",7);
const char *c = str.data();
for (int i=0; i<7; i++) {
printf("%c", c[i] ? c[i] : '0');
}
You already did (not really, see edit below). The problem however, is that whatever you are using to print the string (printf?), is using the c string convention of ending strings with a '\0'. So it starts reading your data, but when it gets to the 0 it will assume it is done (because it has no other way).
If you want to simply write the buffer to the output, you will have to do this with something like
write(stdout, c, 7);
Now write has information about where the data ends, so it can write all of it.
Note however that your terminal cannot really show a \0 character, so it might show some weird symbol or nothing at all. If you are on linux you can pipe into hexdump to see what the binary output is.
EDIT:
Just realized, that your string also initalizes from const char* by reading until the zero. So you will also have to use a constructor to tell it to read past the zero:
std::string("data\0afterzero", 14);
(there are prettier solutions probably)
I have a small query regarding reading a set of characters from a structure. For example: A particular variable contains a value "3242C976*32" (char - type). How can I get only the first 8 bits of this variable. Kindly help.
Thanks.
Edit:
I'm trying to read in a signal:
For Ex: $ASWEER,2,X:3242C976*32
into this structure:
struct pg
{
char command[7]; // saves as $ASWEER,2,X:3242C976*32
char comma1[1]; // saves as ,2,X:3242C976*32
char groupID[1]; // saves as 2,X:3242C976*32
char comma2[1]; // etc
char handle[2]; // this is the problem, need it to save specifically each part, buts its not
char canID[8];
char checksum[3];
}m_pg;
...
When memcopying buffer into a structure, it works but because there is no carriage returns it saves the rest of the signal in each char variable. So, there is always garbage at the end.
you could..
convert your hex value in canID to float(depending on how you want to display it), e.g.
float value1 = HexToFloat(m_pg.canID); // find a conversion script for HexToFloat
CString val;
val.Format("0.3f",value1);
the garbage values aren't actually being stored in the structure, it only displays it as so, as there is no carriage return, so format the message however you want to and display it using the CString val;
If "3242C976*3F" is a c-string or std::string, you can just do:
char* str = "3242C976*3F";
char first_byte = str[0];
Or with an arbitrary memory block you can do:
SomeStruct memoryBlock;
char firstByte;
memcpy(&firstByte, &memoryBlock, 1);
Both copy the first 8bits or 1 byte from the string or arbitrary memory block just as well.
After the edit (original answer below)
Just copy by parts. In C, something like this should work (could also work in C++ but may not be idiomatic)
strncpy(m_pg.command, value, 7); // m.pg_command[7] = 0; // oops
strncpy(m_pg.comma, value+7, 1); // m.pg_comma[1] = 0; // oops
strncpy(m_pg.groupID, value+8, 1); // m.pg_groupID[1] = 0; // oops
strncpy(m_pg.comma2, value+9, 1); // m.pg_comma2[1] = 0; // oops
// etc
Also, you don't have space for the string terminator in the members of the structure (therefore the oopses above). They are NOT strings. Do not printf them!
Don't read more than 8 characters. In C, something like
char value[9]; /* 8 characters and a 0 terminator */
int ch;
scanf("%8s", value);
/* optionally ignore further input */
while (((ch = getchar()) != '\n') && (ch != EOF)) /* void */;
/* input terminated with ch (either '\n' or EOF) */
I believe the above code also "works" in C++, but it may not be idiomatic in that language
If you have a char pointer, you can just set str[8] = '\0'; Be careful though, because if the buffer is less than 8 (EDIT: 9) bytes, this could cause problems.
(I'm just assuming that the name of the variable that already is holding the string is called str. Substitute the name of your variable.)
It looks to me like you want to split at the comma, and save up to there. This can be done with strtok(), to split the string into tokens based on the comma, or strchr() to find the comma, and strcpy() to copy the string up to the comma.
I am passing an empty char array that I need to recursively fill using strcat(). However, in the VS debugger, the array is not empty, it's full of some weird junk characters that I don't recognise. strcat() then appends to the end of these junk characters rather than at the front of the array.
I have also tried encoded[0] = '\0' to clear the junk before passing the array, but then strcat() doesn't append anything on the recursive call.
This is the code that supplies the array and calls the recursive function:
char encoded[512];
text_to_binary("Some text", encoded);
This is the recursive function:
void text_to_binary(const char* str, char* encoded)
{
char bintemp[9];
bintemp[0] = '\0';
while(*str != '\0')
{
ascii_to_binary(*str, bintemp);
strcat(encoded, bintemp);
str++;
text_to_binary(str, encoded);
}
}
What is going on?
ps. I can't use std::string - I am stuck with the char*.
Edit: This is the junk character in the array:
ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ...
You are not initialising the array. Change:
char encoded[512];
to
char encoded[512] = "";
strcat appends to the end of the string, the end is marked by a \0, it then appends a \0 to the new end position.
You should clear the destination encoded with either encoded[0]=0; or memset first.
char encoded[512];.. encoded is not initialized and will contain junk (or 0xCCCCCCCC in debug builds).
Your problem was due to encode initialization I think. A few comment on your program:
it's better to avoid recursive
function when you can do it with a
loop.
Second you should add the size of
encoded to avoid possible overflow
error (in the case the size of string
is bigger than encoded).
void text_to_binary(const char* str, char* encoded)
{
char bintemp[9];
bintemp[0] = '\0';
encode[0] = '\0';
for(const char *i = str; i!='\0'; i++)
{
ascii_to_binary(*i, bintemp);
strcat(encoded, bintemp);
}
}
PS: i didn't tried the source code, so if there is an error add a comment and I will correct it.
Good contination on your project.
The solution to your immediate problem has been posted already, but your text_to_binary is still inefficient. You are essentially calling strcat in a loop with always the same string to concatenate to, and strcat needs to iterate through the string to find its end. This makes your algorithm quadratic. What you should do is to keep track of the end of encoded on your own and put the content of bintemp directly there. A better way to write the loop would be
while(*str != '\0')
{
ascii_to_binary(*str, bintemp);
strcpy(encoded, bintemp);
encoded += strlen(bintemp);
str++;
}
You don't need the recursion because you are already looping over str (I believe this to be correct, as your original code will fill encoded pretty weirdly). Also, in the modified version, encoded is always pointing to the end of the original encoded string, so you can just use strcpy instead of strcat.
You didn't attached source of ascii_to_binary, let's assume that it will fill buffer with hex dump of the char (if this is the case it's easier to use sprintf(encoded+(i2),"%2x",*(str+i));
What's the point of recursively calling text_to_binary? I think this might be a problem.