Modify a char* string in C - c++

I have this:
char* original = "html content";
And want to insert a new
char* mycontent = "newhtmlinsert";
into the "original" above just before </body> tag in the "original".
My new orginal is now:
char* neworiginal = "html content before </body>" + "newhtmlinsert" + "html content after </body>";
Basically i want to take a char* orginal and convert it into a char* neworiginal which has the original content plus new content that i added before the </body> in the "original html content"
here is the modified code, i still need some help:
* original data */
data = _msg_ptr->buff();
data_len = _msg_ptr->dataLen();
/*location of </body> in the original */
char *insert = strstr(data, "</body>");
/*length of the new buffer string */
int length = strlen(data)+strlen(ad_content);
newdata =(char*)malloc(sizeof(char)*length);
memset(newdata, 0, length);
/*copy the original data upto </body> into newdata*/
memcpy(newdata,data,insert-data);
/*now add the ad_content */
strcat(newdata,ad_content);
/*copy the data from </body> to end of original string(data) into newdata */
memcpy(newdata,data,data_len - ending );
how do i implement the the last statement : memcpy(newdata,data,data_len - ending );
i need to copy the remainder of the data from my char* data beginning from an
the very end...how do i correctly compute the "ending" parameter in the memcpy?
here is the c++ version using strings
char *insert = strstr(_in_mem_msg_ptr->buff(), "</body>");//get pointer to </body>
string ad_data = string(_in_mem_msg_ptr->buff(),insert - _in_mem_msg_ptr->buff()) ;//insert the part of _in_mem_msg_ptr->buff() before the </body>
ad_data.append(ad_content); //add the new html content
ad_data.append(_in_mem_msg_ptr->buff(),insert- _in_mem_msg_ptr->buff(),_in_mem_msg_ptr->dataLen()); //remainder of _in_mem_msg_ptr->buff() from and including </body> to the end

Assuming that char* original is composed by two parts, one starts at 0 while the other (html content after) starts at x you can use strcat and memcpy:
int length = strlen(original)+strlen(newcontent)+1;
char *neworiginal = malloc(sizeof(char)*length);
memset(neworiginal, 0, length);
memcpy(neworiginal,original,x*sizeof(char));
strcat(neworiginal,newcontent);
strcat(neworiginal,original+x);

You need to use strcat() for this problem.
Example =
/* strcat example */
#include <stdio.h>
#include <string.h>
int main ()
{
char str[80];
strcpy (str,"these ");
strcat (str,"strings ");
strcat (str,"are ");
strcat (str,"concatenated.");
puts (str);
return 0;
}
Though you need to check the bounds so you can use the bounds variant of strncat().
#include <string.h>
char *strncat(char *restrict s1, const char *restrict s2, size_t n);
Make sure whatever buffer you are appending your string into has enough space to not cause a buffer overflow.

C++ doesn't have + operator for char * strings. You need to use std::string, i.e.
std::string neworiginal = "html content before </body>";
neworiginal += "newhtlminsert";
neworiginal += "..."

Take a look at strstr, strcat and other cstring/string.h functions.
Make sure your char arrays are large enough to hold concatenated strings. Like, you may want to do the following:
char neworiginal[1024];
etc.

The string.h function strcat will concatenate two strings, however it will fail when there is not enough space for the new string. My solution is to make your own version of strcat:
char* myStrCat(const char* str1, const char* str2)
{
char* result;
char* itr1;
char* itr2;
if (str1 == NULL || str2 == NULL)
{
return NULL;
}
result = (char*)malloc(sizeof(char) * (strlen(str1) + strlen(str2) + 1));
itr1 = result;
itr2 = (char*)str1;
while (*itr2 != '\0')
{
*itr1 = *itr2;
itr1++;
itr2++;
}
itr2 = (char*)str2;
while (*itr2 != '\0')
{
*itr1 = *itr2;
itr1++;
itr2++;
}
*itr1 = '\0';
return result;
}
This is kinda ugly, but it gets the job done :)

Attempting to modify the contents of a string literal results in undefined behavior.
You will need to allocate a target buffer (either as an auto variable or by using malloc) that's large enough to hold your final string plus a 0 terminator.
Also, you might want to use sprintf to make life a little easier, such as
sprintf(result, "%s before %s - %s - %s after %s", original,
tag, mycontent, original, tag);

Related

Copy a part of an std::string in a char* pointer

Let's suppose I've this code snippet in C++
char* str;
std::string data = "This is a string.";
I need to copy the string data (except the first and the last characters) in str.
My solution that seems to work is creating a substring and then performing the std::copy operation like this
std::string substring = data.substr(1, size - 2);
str = new char[size - 1];
std::copy(substring.begin(), substring.end(), str);
str[size - 2] = '\0';
But maybe this is a bit overkilling because I create a new string. Is there a simpler way to achieve this goal? Maybe working with offets in the std:copy calls?
Thanks
As mentioned above, you should consider keeping the sub-string as a std::string and use c_str() method when you need to access the underlying chars.
However-
If you must create the new string as a dynamic char array via new you can use the code below.
It checks whether data is long enough, and if so allocates memory for str and uses std::copy similarly to your code, but with adapted iterators.
Note: there is no need to allocate a temporary std::string for the sub-string.
The Code:
#include <string>
#include <iostream>
int main()
{
std::string data = "This is a string.";
auto len = data.length();
char* str = nullptr;
if (len > 2)
{
auto new_len = len - 2;
str = new char[new_len+1]; // add 1 for zero termination
std::copy(data.begin() + 1, data.end() - 1, str); // copy from 2nd char till one before the last
str[new_len] = '\0'; // add zero termination
std::cout << str << std::endl;
// ... use str
delete[] str; // must be released eventually
}
}
Output:
his is a string
There is:
int length = data.length() - 1;
memcpy(str, data.c_str() + 1, length);
str[length] = 0;
This will copy the string in data, starting at position [1] (instead of [0]) and keep copying until length() - 1 bytes have been copied. (-1 because you want to omit the first character).
The final character then gets overwritten with the terminating \0, finalizing the string and disposing of the final character.
Of course this approach will cause problems if the string does not have at least 1 character, so you should check for that beforehand.

preprending and appending to a cstring

I have the following string tok_str which is like "default.png" I would like to preprend char ' and append char ' too.
That's what I have done, but the chars are appended and prepended in the wrong places
char *tok_str = const_cast<char*>(mReader->getAttributeValue(pAttrIdx));
char * mod_tok = new char[tok_str_len+2];
mod_tok[0] = '\'';
size_t len = strlen(tok_str);
size_t i;
memmove(mod_tok + len, mod_tok, strlen(mod_tok) + 1);
for (i = 0; i < len; ++i)
{
mod_tok[i] = tok_str[i];
}
char *dup;
char *cstr="'";
sprintf(mod_tok,"%s%s",cstr,(dup=strdup(mod_tok)));
free(dup);
If you want to continue using null-terminated byte strings there are a few things you need to think of and do.
The first is of course the null-terminated part. A string of X characters needs space for X+1 to include the terminator.
The second is that all you need is really a single sprintf (or better yet snprintf) call (once you allocated memory):
char* mod_tok = new char[strlen(tok_str) + 3]; // +2 for the extra characters, +1 for terminator
snprintf(mod_tok, strlen(tok_str) + 3, "'%s'", tok_str);
That is it, now you have added the single quotes in front and at the end of the original string.
There are a couple of things to improve:
usage of const when possible
len vs tok_str_len, use only one.
the memmove done in the middle seems to have no effect on the final result
pay attention to the indexes in the for loop
be aware that strlen doesn't count the NULL terminator
if your code starts to mix new/delete with free try to refactor it
That's my proposal:
//keep it const and protect your data
const char *tok_str = mReader->getAttributeValue(pAttrIdx);
//retrive the len once for all (const, no one is supposed to change it)
const size_t len = strlen(tok_str);
char * mod_tok = new char[len+3]; // 2 "'" + '\0'
mod_tok[0] = '\'';
for (size_t i = 0; i < len; ++i)
{
mod_tok[i+1] =tok_str[i];
}
mod_tok[len+1] = '\'';
mod_tok[len+2] = '\0';
//done.
//later...
delete[] mod_tok;
Enjoy your coding!
Stefano
PS: I agree, though, that a use of std::string is reccomended.

Convert char * to QString and remove zeros

In my app I read a string field from a file in local (not Unicode) charset.
The field is a 10 bytes, the remainder is filled with zeros if the string < 10 bytes.
char str ="STRING\0\0\0\0"; // that was read from file
QByteArray fieldArr(str,10); // fieldArr now is STRING\000\000\000\000
fieldArr = fieldArr.trimmed() // from some reason array still containts zeros
QTextCodec *textCodec = QTextCodec::codecForLocale();
QString field = textCodec->ToUnicode(fieldArr).trimmed(); // also not removes zeros
So my question - how can I remove trailing zeros from a string?
P.S. I see zeros in "Local and Expressions" window while debuging
I'm going to assume that str is supposed to be char const * instead of char.
Just don't go over QByteArray -- QTextCodec can handle a C string, and it ends with the first null byte:
QString field = textCodec->toUnicode(str).trimmed();
Addendum: Since the string might not be zero-terminated, adding storage for a null byte to the end seems to be impossible, and making a copy to prepare for making a copy seems wasteful, I suggest calculating the length ourselves and using the toUnicode overload that accepts a char pointer and a length.
std::find is good for this, since it returns the ending iterator of the given range if an element is not found in it. This makes special-case handling unnecessary:
QString field = textCodec->toUnicode(str, std::find(str, str + 10, '\0') - str).trimmed();
Does this work for you?
#include <QDebug>
#include <QByteArray>
int main()
{
char str[] = "STRING\0\0\0\0";
auto ba = QByteArray::fromRawData(str, 10);
qDebug() << ba.trimmed(); // does not work
qDebug() << ba.simplified(); // does not work
auto index = ba.indexOf('\0');
if (index != -1)
ba.truncate(index);
qDebug() << ba;
return 0;
}
Using fromRawData() saves an extra copy. Make sure that the str
stays around until you delete the ba.
indexOf() is safe even if you have filled the whole str since
QByteArray knows you only have 10 bytes you can safely access. It
won't touch 11th or later. No buffer overrun.
Once you removed extra \0, it's trivial to convert to a QString.
You can truncate the string after the first \0:
char * str = "STRING\0\0\0\0"; // Assuming that was read from file
QString field(str); // field == "STRING\0\0\0\0"
field.truncate(field.indexOf(QChar::Null)); // field == "STRING" (without '\0' at the end)
I would do it like this:
char* str = "STRING\0\0\0\0";
QByteArray fieldArr;
for(quint32 i = 0; i < 10; i++)
{
if(str[i] != '\0')
{
fieldArr.append(str[i]);
}
}
QString can be constructed from a char array pointer using fromLocal8Bit. The codec is chosen the same way you do manually in your code.
You need to set the length manually to 10 since you say you have no guarantee that an terminating null byte is present.
Then you can use remove() to get rid of all null bytes. Caution: STRI\0\0\0\0NG will also result in STRING but you said that this does not happen.
char *str = "STRING\0\0\0\0"; // that was read from file
QString field = QString::fromLocal8Bit(str, 10);
field.remove(QChar::Null);

C++ std::string length() or size() not working on method args

I have a method that that creates a MatLab array name from a file path and returns it as a std::string. the resulting string is passed into another method that writes the name to the file. When I try to get the length of the passed in string, it displays 0 when the length of the string is 12 or 13 chars.
My code:
bool MyClass::masterMethod(std::string fileURI){
FILE* dataStream;
// Assume dataStream is set up correctly
// Get arrayName from File URI
std::string arrayName = this->makeArrayNameFromPath( fileURI);
//Write array name to file
this->writeArrayName(arrayName , dataStream)
}
std::string MyClass::makeArrayNameFromPath(std::string filePathURI){
std::string tempString = filePathURI.substr(filePathURI.find_last_of('/')+1);
std::string returnString = "";
long index = 0;
for(long i = 0; i < tempString.length(); i++){
if((tempString[i] != ' ') && (tempString[i] != '.')){
returnString[index++] = tempString[i];
}
}
return returnString;
}
void MyClass::writeArrayName(std::string name , FILE *nameStream){
// long testLength = name.length();
// long testLength2 = name.size();
// const char* testChar = nam.c_str();
// long testCharLen = strlen(testChar);
// The size of the name is the number of Chars * sizeof(int8_t)
int32_t sizeOfName = (int32_t)(name.length() * sizeof(int8_t));
int32_t nameType = miINT8;
fwrite(&nameType , sizeof(int32_t) , 1 , nameStream);
fwrite(&sizeOfName, sizeof(sizeOfName), 1, nameStream);
fwrite(&name , sizeof(name[0]), sizeOfName , nameStream);
}
So I'm not sure why string::length is not working. If a create a std::string test = name, and print it , I can get the value of the string but can not get its length or size.
If I use const char* testName = name.c_str(); long test = strlen(testName), I get a the
correct value, but thought that wasn't necessary.
So any advice or suggestion is appreciated.
returnString[index++] = tempString[i]; doesn't do what you think it does. It's not adding additional space or length to the string, only overwriting memory at a location that the string doesn't actually own. I think returnString.append(1, tempString[i]) or similar should do it.
You never give the string a size, just trying to assign positions that isn't there.
Try this instead to add characters to the return value
returnString += tempString[i];

Combining std::string and std::vector<char>

This is not the actual code, but this represents my problem.
std::string str1 = "head";
char *buffer = "body\0body"; // Original code has nullbytes;
std::string str2 = "foot";
std::vector<char> mainStr(buffer, buffer + strlen(buffer));
I want to put str1 and str2 to mainStr in an order:
headbody\0bodyfoot
So the binary data is maintained. Is this possible to do this?
PS: Thanks for telling the strlen part is wrong. I just used it to represent buffer's length. :)
There should be some way of defining length of data in "buffer".
Usually character 0 is used for this and most of standard text functions assume this. So if you use character 0 for other purposes, you have to provide another way to find out length of data.
Just for example:
char buffer[]="body\0body";
std::vector<char> mainStr(buffer,buffer+sizeof(buffer)/sizeof(buffer[0]));
Here we use array because it provides more information that a pointer - size of stored data.
You cannot use strlen as it uses '\0' to determine the end of string. However, the following will do what you are looking for:
std::string head = "header";
std::string foot = "footer";
const char body[] = "body\0body";
std::vector<char> v;
v.assign(head.begin(), head.end());
std::copy(body, body + sizeof(body)/sizeof(body[0]) - 1, std::back_inserter<std::vector<char> >(v));
std::copy(foot.begin(), foot.end(), std::back_inserter<std::vector<char> >(v));
Because the character buffer adds an NUL character at the end of the string, you'll want to ignore it (hence the -1 from the last iterator).
btw. strlen will not work if there are nul bytes in your string!
The code to insert into the vector is:
front:
mainStr.insert(mainStr.begin(), str1.begin(), str1.end());
back:
mainStr.insert(mainStr.end(), str2.begin(), str2.end());
With your code above (using strlen will print)
headbodyfoot
EDIT: just changed the copy to insert as copy requires the space to be available I think.
You could use std::vector<char>::insert to append the data you need into mainStr.
Something like this:
std::string str1 = "head";
char buffer[] = "body\0body"; // Original code has nullbytes;
std::string str2 = "foot";
std::vector<char> mainStr(str1.begin(), str1.end());
mainStr.insert(mainStr.end(), buffer, buffer + sizeof(buffer)/sizeof(buffer[0]));
mainStr.insert(mainStr.end(), str2.begin(), str2.end());
Disclaimer: I didn't compile it.
You can use IO streams.
std::string str1 = "head";
const char *buffer = "body\0body"; // Original code has nullbytes;
std::string str2 = "foot";
std::stringstream ss;
ss.write(str1.c_str(), str1.length())
.write(buffer, 9) // insert real length here
.write(str2.c_str(), str2.length());
std::string result = ss.str();
std::vector<char> vec(result.c_str(), result.c_str() + result.length());
str1 and str2 are string objects that write the text.
I wish compilers would fail on statements like the declaration of buffer and I don't care how much legacy code it breaks. If you're still building it you can still fix it and put in a const.
You would need to change your declaration of vector because strlen will stop at the first null character. If you did
char buffer[] = "body\0body";
then sizeof(buffer) would actually give you close to what you want although you'll get the end null-terminator too.
Once your vector mainStr is then set up correctly you could do:
std::string strConcat;
strConcat.reserve( str1.size() + str2.size() + mainStr.size() );
strConcat.assign(str1);
strConcat.append(mainStr.begin(), mainStr.end());
strConcat.append(str2);
if vector was set up using buffer, buffer+sizeof(buffer)-1
mainStr.resize(str1.length() + str2.length() + strlen(buffer));
memcpy(&mainStr[0], &str1[0], str1.length());
memcpy(&mainStr[str1.length()], buffer, strlen(buffer));
memcpy(&mainStr[str1.length()+strlen(buffer)], &str2[0], str2.length());