I wrote the function readChar() which is designed to read the characters send by my WiFi module one by one(function works has advertised) and pass them to a char buffer of increasing size. The function should stop when char *endChar (multiple characters) have been detected or the number of character returned by timedRead() has exceeded size_t length.
I have several issues:
1/. I don't understand the syntax (found inside the Arduino Stream library) :
*buffer++ = (char)c;
Can you explain how the array buffer gets filled?
And why buffer[index] = (char)c; doesn't work here?
2/. I would like to compare buffer and endChar in the loop, possibly by using strcmp(buffer,endChar) (maybe there is a better way). But that doesn't seem to work. In fact when printing the ASCII values of my char *buffer then seem to increment from the end of the buffer. E.G.:
So what is the best way to do that comparison?
The code, inserted in the loop:
_dbgSerial->println("buffer");
for (int i = 0; i < 32; i++){
_dbgSerial->print(buffer[i], DEC);
_dbgSerial->print(",");
}
_dbgSerial->println("");
prints:
buffer
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,10,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,10,13,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,10,13,10,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,10,13,10,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,10,13,10,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,10,13,10,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,10,13,10,0,0,0,0,
Here is the function readChar():
size_t Debugwifi::readChar(char *endChar, char *buffer, size_t length) {
if (length < 1) return 0;
size_t index = 0;
while (index < length) {
int c = timedRead();
if (c < 0 ) break;
//buffer[index] = (char)c;
*buffer++ = (char)c;
_dbgSerial->println("buffer");
for (int i = 0; i < 32; i++){
_dbgSerial->print(buffer[i], DEC);
_dbgSerial->print(",");
}
_dbgSerial->println("");
if (strcmp(buffer,endChar)==0) {
break;
_dbgSerial->println("brk");}
index++;
}
return index;
}
As Rickard has explained, *buffer++ = (char)c; is how you assign a character to the memory a pointer points at, and then increment the pointer.
However, your function has a lot of problems - you keep comparing unset memory with *endChar. I suggest:
size_t Debugwifi::readChar(const char * const endStr, // const pointer to const.
char * const buffer, const size_t length) {
if (length < 1) return 0;
const size_t endLen = strlen(endStr);
for (size_t index = 0; index < length; index++) {
const int c = timedRead();
if (c < 0 ) break;
buffer[index] = (char)c;
// Debug
_dbgSerial->println("buffer");
for (size_t i = 0; i < length; i++){ // Better to use size_t here,
// and compare against length not 32
_dbgSerial->print(buffer[i], DEC);
_dbgSerial->print(",");
}
_dbgSerial->println("");
// Finished?
if (index >= endLen) {
if (memcmp(&buffer[index-endLen], endStr, endLen)==0) {
_dbgSerial->println("brk"); // Must do this *before* "break"!
break;
}
}
}
return index;
}
I have added a lot of consts. It's hard to have too many.
The important point is that once you have read enough characters, to start comparing the last characters you have read to the end marker.
Note that this function does not remove the end marker, and if you pass a 32-byte zero-filled array and it reads 32 characters, the result will NOT be zero terminated.
Finally, I changed the argument name to endStr because I had expected endChar to be a pointer to a single character - not a NUL-terminated string.
*buffer++ = (char) c;
First writes the value of c to what buffer is currently pointing to.
Then it increments the buffer
This is also why your loop to print buffer doesn't work.
You start printing from the position after what was just filled.
This is also why your strcmp doesn't work. It doesn't actually compare what you have filled your buffer with. It compares the content beyond what have been filled.
If you want your printing code to work you should save the initial value of buffer before the loop;
const char *buffer_start = buffer;
Then use that in your printing code instead of buffer.
I want to remove double-quotes from a string, for example 13.3" Rentina becomes 13.3 Rentina
const char* s = sheet->readStr(row, col);
int ii = strlen(s);
char* b;
b=(char*)s;
char ch;
for (int i = 0; i < ii ;++i) {
strncpy(&ch, b+ii, 1);
if(ch == '\"'){
ch = '\"';
memcpy(b+i, &ch, 1);
}
}
myfile << b;
If you deal with strings in C++, you should use character arrays and functions like strncpy only when you have a strong reason to use them. By default you should use standard string, which makes e.g. memory management much easier. The solution to your problem with std::string is
std::string s = sheet->readStr(row, col);
size_t pos = 0;
while ((pos = s.find('"', pos)) != std::string::npos)
s = s.erase(pos, 1);
myfile << s;
You cannot do b=(char*)s!!!
The compiler lets you in on this one, but you will get a runtime exception as soon as you attempt to write into the memory address space pointed by b.
The variable s is possibly pointing to an address in the code-section, which is a read-only memory address space within your program ("possibly", because perhaps that const declaration of s is just something you added on your own initiative).
You should allocate a new char array, and copy the output string into that array instead.
So first of all, change the above statement to b = (char*)malloc(strlen(s)).
In addition, do not pass to strncpy (or any other str function for that matter) an address of a char variable. These functions operate on char arrays, and either assume that the array ends with a 0 character, or set the character at the end of the array to 0.
You can try the following piece of code (assuming that your purpose is to remove the '"'):
const char* s = sheet->readStr(row, col);
int ii = strlen(s);
char* b = (char*)malloc(ii+1);
if (b != NULL)
{
int i,j;
for(i=0,j=0; i<ii; i++)
{
if (s[i] != '"')
b[j++] = s[i];
}
b[j] = 0;
// Add your code here (do whatever you wanna do with 'b')
free(b);
}
else
{
printf("Out of memory\n");
}
Is there a faster/ more efficient way to split a char array into chunks (say 21 chars/ array) other than looping over the whole array?
This is my attempt right now
const char* arr = line.c_str();
char smallArr[(int)ceil((double)strlen(arr)/(double)21)][21];
int arrSisze[(int)ceil((double)strlen(arr)/(double)21)][1];
int m=0;int n=0;
for (int i=0; i<strlen(arr); i++) {
smallArr[m][i]=arr[i];
arrSisze[m][0]=(i+1)-n;
if ((i-n)==19) {
m++;
n+=20;
}
}
1) Using memcpy
char myname[] = "hello";
char dest[20] = {0};
/* using memcpy to copy string: */
memcpy ( dest, myname, 5);
2) using strncpy
char str1[]= "To be or not to be";
char str2[40];
strncpy ( str2, str1, sizeof(str2) );
Yes, pointer arithmetic and memcpy. However, seeing as you're using C++, let's stick to std::strings and assume they do a memcpy for us.
std::vector<std::string> output;
output.reserve(line.length() / 21 + (line.length() % 21) ? 1 : 0);
auto i = line.begin(), j = i + 21;
for(; line.end() - j > 21; i = j, j+= 21)
{
output.emplace(i, j)
}
if(j != line.end())
{
output.emplace(j, line.end());
}
So, what is going on here? For our purpose, it is good enough to think of a std::string as a char array and a length variable.
First, we reserve enough space for our output. You did this too.
Next, we define 2 variables i and j. i represents the beginning of the current substring and j the one-past-the-end iterator. Iterators here can be thought of as pointers into the internal structure of the string's char array - they may even be char*s!
We then iterate over the original string a line at a time. emplace just constructs a new element of a vector in the correct place. The call is equivalent to output.push_back(std::string(i, j)) and is only available in C++11.
To finish, we check whether there is another whole chunk using line.end() - j > 21; line.end() is the one-past-the-end iterator for the char array - it points at the NUL character (if there is one). If there is not a whole chunk, we check for a partial chunk using j != line.end().
Don't re-optimize the standard library.
If you have a std::string, just use it.
Don't use floating point for integer calculations: just use integer arthmetic porperly
the problem complexity is O(1). No other solution will cover it with less of one string walk span, and related copies
Use C++ properly, and forget C.
=
std::vector<std::string> chunks;
chunks.resize(21);
size_t chunksize = line.size()/21+1;
for(size_t i=0,j=0; i<line.size(); i+=chunksize, ++j)
{ chunks[j] = line.substr(i,chunksize); }
Note that sting::size takes N^0, while strlen takes N^1 complexity (it has a loop inside).
In this code, my loop is 21^1 on chunks and substr is (N/21)^1 each chunk content, giving N^1 on the whole length.
There is no need to track string length and null terminate the strings. All is handled by std::string class.
I meet a problem with the char array size. I pass an char array into the function and after run the function, I still want to use sizeof to check the size of the array, it won't give me the new size of the array, but the old size? I would like to know why? Thank you very much!
#include<iostream>
using namespace std;
void replacement(char* arr, int len){
int count=0;
for(int i=0; i<len; i++){
if(arr[i]==' '){
count++;
}
}
int newlen=count*2+len;
//arr[newlen]='\0';
int k=newlen-1;
for(int i=len-1; i>=0; i--){
if(arr[i]!=' '){
arr[k--]=arr[i];
}
else{
arr[k--]='0';
arr[k--]='2';
arr[k--]='%';
}
}
}
int main(){
char arr[]="ab c d e g ";
cout<<sizeof(arr)<<endl;
replacement(arr, sizeof(arr));
int i=0;
while(arr[i]!=NULL) cout<<arr[i];
}
You can't change an array's size. If you want to know the length of the string in the array, use strlen() -- this counts the number of characters before the null terminator.
Even better would be to use C++ std::string class.
Right, so you are trying to replace spaces with "%20", right?
Since C++ (or C) doesn't allow an existing array to be resized, you will either need to have enough space in the first place, or use an array allocated on the heap. Then allocate a new "replacement" string in the replacement function and return that.
The proper C++ method of doing this is of course to use std::string, in which case you could just pass it in as a reference, and do the replacement in the existing variable:
void replacement(std::string* str, int len){
std::string perc20 = "%20";
std::string space = " ";
while((pos = str.find(space, pos)) != std::string::npos)
{
str.replace(pos, space.length(), perc20);
pos += perc20.length();
}
}
Much easier...
You can use sizeof() to find the size of only static arrays when the size is known at compile time. Hence it will always return the size of the array as determined at compile time.
Your program technically has Undefined Behavior because your use of sizeof returns the size in bytes of your char array. But a char implicitly contains a null byte \0. That means the for loop is iterating 1 past the length of the array.
It's recommended that you use std::string along with its size member function instead.