I am having a string that has some binary data in it. The string in xml format, so before i am gonna proccess it i need to convert the binary data in the base64 format.
I am using a function called findXMLTag that will find the position of the start and the end of the data given the xml tag that contains it.
Now i am able to convert that data into base64 but i am having problems replacing the old binary data with my new base64 data.
The thing is that i can't use any type of string because when it locates a null char it will consider it as the terminating point of the string but in fact since i have binary data stored in the string then that null char can be part of my binary data.
So i guess i am looking for some kind of binary replacement and i can't figure out how to make it work.
Thanks in advance for any kind of help.
This is the code that i am using to locate the start and the end of the data in an xml string.
std::vector<TForm1::Pair> TForm1::findXMLTag(char *XMLString, char* XMLTag, int XMLSize)
{
void *found = XMLString;
int XMLTagLen = strlen(XMLTag);
std::vector<TForm1::Pair> result;
TForm1::Pair pair;
AnsiString XMLTagEnd = "</";
XMLTagEnd += &XMLTag[1];
while(found = memmem(XMLString, XMLSize - ((char*)found - XMLString), XMLTag, XMLTagLen))
{
if(found == NULL)
return result;
found = (char*)found + XMLTagLen;
pair.start = int((char*)found - XMLString);
found = memmem(found, XMLSize - ((char*)found - XMLString), XMLTagEnd.c_str(), XMLTagEnd.Length());
pair.end = int((char*)found - XMLString);
found = (char*)found + XMLTagEnd.Length();
result.push_back(pair);
}
return result;
}
Translating your C-style answer to C++, we are left with a one-liner which is safe (for valid indices), efficient and readable:
std::string binary_replace(
std::string const& bin, unsigned bin_start, unsigned bin_end,
std::string const& replace_with
) {
assert(bin_start < bin.size() and bin_end < bin.size());
return bin.substr(0, bin_start) + replace_with + bin.substr(bin_end);
}
This can be made even simpler by using the replace function for this purpose:
std::string binary_replace(
std::string bin, unsigned bin_start, unsigned bin_end,
std::string const& replace_with
) {
assert(bin_start < bin.size() and bin_end < bin.size());
return bin.replace(bin_start, bin_end - bin_start, replace_with);
}
(Take heed that bin is passed by value here since replace modifies it.)
Essentially there’s a direct substitute for most C-string functions in C++ – in this case, have a look at the documentation of std::basic_string::substr.
Here's a little self-contained example that might help you. Note that there is no error or bounds checking, it's just meant to demonstrate a concept.
#include <iostream>
#include <vector>
#include <string>
using namespace std;
// stub for real base64_encode
std::string base64_encode(const string &data)
{
return "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF";
}
// search without terminating on NULL
size_t binary_find(const string &str, size_t offset, const string &s)
{
for (size_t i=offset; i<str.length(); i++)
if (str.compare(i, string::npos, s) == 0)
return i;
return string::npos;
}
int main()
{
string tag = "<data>";
string endtag = "</data>";
string xml("<data>\0\0\0\0\0\0\0\0\0\0</data>", 23);
size_t start = xml.find(tag) + tag.length();
size_t end = binary_find(xml, start, endtag);
string binary = xml.substr(start, end-start);
string base64 = base64_encode(binary);
xml.replace(start, end-start, base64);
cout << xml << endl;
}
char *binary_replace(char *binString, int _strlen, int binDataStart, int binDataEnd, char* replaceWith)
{
char *buffer = (char*)malloc( (strlen(replaceWith)+(_strlen - (binDataEnd-binDataStart)))*sizeof(char) );
memcpy(buffer, binString, binDataStart);
strcat(buffer, replaceWith);
memmove(buffer+binDataStart+strlen(replaceWith), binString+binDataEnd, _strlen - binDataEnd);
return buffer;
}
I know that this is not c++ but it solved my problem.
Related
I have a runtime problem with code below.
The purpose is to "recognize" the formats (%s %d etc) within the input string.
To do this, it returns an integer that matches the data type.
Then the extracted types are manipulated/handled in other functions.
I want to clarify that my purpose isn't to write formatted types in a string (snprintf etc.) but only to recognize/extract them.
The problem is the crash of my application with error:
Debug Assertion Failed!
Program:
...ers\Alex\source\repos\TestProgram\Debug\test.exe
File: minkernel\crts\ucrt\appcrt\convert\isctype.cpp
Line: 36
Expression: c >= -1 && c <= 255
My code:
#include <iostream>
#include <cstring>
enum Formats
{
TYPE_INT,
TYPE_FLOAT,
TYPE_STRING,
TYPE_NUM
};
typedef struct Format
{
Formats Type;
char Name[5 + 1];
} SFormat;
SFormat FormatsInfo[TYPE_NUM] =
{
{TYPE_INT, "d"},
{TYPE_FLOAT, "f"},
{TYPE_STRING, "s"},
};
int GetFormatType(const char* formatName)
{
for (const auto& format : FormatsInfo)
{
if (strcmp(format.Name, formatName) == 0)
return format.Type;
}
return -1;
}
bool isValidFormat(const char* formatName)
{
for (const auto& format : FormatsInfo)
{
if (strcmp(format.Name, formatName) == 0)
return true;
}
return false;
}
bool isFindFormat(const char* strBufFormat, size_t stringSize, int& typeFormat)
{
bool foundFormat = false;
std::string stringFormat = "";
for (size_t pos = 0; pos < stringSize; pos++)
{
if (!isalpha(strBufFormat[pos]))
continue;
if (!isdigit(strBufFormat[pos]))
{
stringFormat += strBufFormat[pos];
if (isValidFormat(stringFormat.c_str()))
{
typeFormat = GetFormatType(stringFormat.c_str());
foundFormat = true;
}
}
}
return foundFormat;
}
int main()
{
std::string testString = "some test string with %d arguments"; // crash application
// std::string testString = "%d some test string with arguments"; // not crash application
size_t stringSize = testString.size();
char buf[1024 + 1];
memcpy(buf, testString.c_str(), stringSize);
buf[stringSize] = '\0';
for (size_t pos = 0; pos < stringSize; pos++)
{
if (buf[pos] == '%')
{
if (buf[pos + 1] == '%')
{
pos++;
continue;
}
else
{
char bufFormat[1024 + 1];
memcpy(bufFormat, buf + pos, stringSize);
bufFormat[stringSize] = '\0';
int typeFormat;
if (isFindFormat(bufFormat, stringSize, typeFormat))
{
std::cout << "type = " << typeFormat << "\n";
// ...
}
}
}
}
}
As I commented in the code, with the first string everything works. While with the second, the application crashes.
I also wanted to ask you is there a better/more performing way to recognize types "%d %s etc" within a string? (even not necessarily returning an int to recognize it).
Thanks.
Let's take a look at this else clause:
char bufFormat[1024 + 1];
memcpy(bufFormat, buf + pos, stringSize);
bufFormat[stringSize] = '\0';
The variable stringSize was initialized with the size of the original format string. Let's say it's 30 in this case.
Let's say you found the %d code at offset 20. You're going to copy 30 characters, starting at offset 20, into bufFormat. That means you're copying 20 characters past the end of the original string. You could possibly read off the end of the original buf, but that doesn't happen here because buf is large. The third line sets a NUL into the buffer at position 30, again past the end of the data, but your memcpy copied the NUL from buf into bufFormat, so that's where the string in bufFormat will end.
Now bufFormat contains the string "%d arguments." Inside isFindFormat you search for the first isalpha character. Possibly you meant isalnum here? Because we can only get to the isdigit line if the isalpha check passes, and if it's isalpha, it's not isdigit.
In any case, after isalpha passes, isdigit will definitely return false so we enter that if block. Your code will find the right type here. But, the loop doesn't terminate. Instead, it continues scanning up to stringSize characters, which is the stringSize from main, that is, the size of the original format string. But the string you're passing to isFindFormat only contains the part starting at '%'. So you're going to scan past the end of the string and read whatever's in the buffer, which will probably trigger the assertion error you're seeing.
Theres a lot more going on here. You're mixing and matching std::string and C strings; see if you can use std::string::substr instead of copying. You can use std::string::find to find characters in a string. If you have to use C strings, use strcpy instead of memcpy followed by the addition of a NUL.
You could just demand it to a regexp engine which bourned to search through strings
Since C++11 there's direct support, what you have to do is
#include <regex>
then you can match against strings using various methods, for instance regex_match which gives you the possibility, together with an smatch to find out your target with just few lines of codes using standard library
std::smatch sm;
std::regex_match ( testString.cbegin(), testString.cend(), sm, str_expr);
where str_exp is your regex to find what you want specifically
in the sm you have now every matched string against your regexp, which you can print in this way
for (int i = 0; i < sm.size(); ++i)
{
std::cout << "Match:" << sm[i] << std::endl;
}
EDIT:
to better express the result you would achieve i'll include a simple sample below
// target string to be searched against
string target_string = "simple example no.%d is: %s";
// pattern to look for
regex str_exp("(%[sd])");
// match object
smatch sm;
// iteratively search your pattern on the string, excluding parts of the string already matched
cout << "My format strings extracted:" << endl;
while (regex_search(target_string, sm, str_exp))
{
std::cout << sm[0] << std::endl;
target_string = sm.suffix();
}
you can easily add any format string you want modifying the str_exp regex expression.
I am trying to find a string which is inside 2D char array and return it's index. For example:
char idTable[255][32];
char tester[] = { 't','e','s','t','e','r','\0' };
memcpy(idTable[43], tester, 7);
uint8_t id = getID(name[0]);
//name is returned from function "char **name = func();"
//but I have the same results when I try using normal char array...
I've had partial success with the first part of the below code, but it is finding a match if a part of the word is the same (one, oneTwo). If I add "else if" to the first "if" it always goes to the "else if".
The rest of the file prints different results for
printf("idTable string lenght:\t %u\n", strlen(idTable[index]));
and
printf("foundMatch string lenght:\t %u\n", strlen(foundMatch));
, unless I add printf("Index:\t %i\n", index);.
uint8_t getID(char *name) {
printf("\nInserted name:\t %s\n", name);
uint8_t index;
for (uint8_t r = 0; r < 255; r++) {
if (strstr(idTable[r], name) != NULL) {
printf("Found '%s' in position:\t %d\n", name, r);
index = r;
}
}
printf("Index:\t %i\n", index); // THIS LINE
char foundMatch[strlen(idTable[index])];
printf("idTable string lenght:\t %u\n", strlen(idTable[index]));
for (uint8_t c=0; c<strlen(idTable[index]); c++) {
foundMatch[c] = idTable[index][c];
}
printf("foundMatch string lenght:\t %u\n", strlen(foundMatch));
if (strcmp(foundMatch, nodeName) == 0) {
printf("Confirmed\n");
return index;
} else {
printf("Second test failed\n");
return 0;
}
}
Why am I getting this strange results and is there a better way to do this?
I don't know how you are initializing your idTable entries, but if you are using the method that you showed at the start of the question you'll have problems. You can't assume all of the space reserved by idTable is initialed to 0's, so idTable[43] isn't a null terminated string. Therefore idTable[43] need not compare equal to the null terminated string "tester".
Also your getID function doesn't return anything despite its signature. So it won't even compile as-is.
Here's a solution in actual C++, not C.
std::array<std::string, 255> idTable;
idTable.at(43) = "tester";
std::pair<std::size_t, std::size_t> findInIdTable(std::string const& what) {
for (unsigned i = 0; i < idTable.size(); ++i) {
std::size_t pos = idTable.at(i).find(what);
if (pos != std::string::npos) {
return std::make_pair(i, pos);
}
}
// if the code reaches this place, it means "not found". Choose how you want to deal with it
// my personal suggestion would be to return std::optional<std::pair<...> instead.
}
If you want to discard the pos value, it's easy to change as well.
Live On Coliru
In the category: Use C++
Of course, use std::array<char, 32> or std::string if possible. I stuck with your choices for this answer:
Live On Coliru
#include <algorithm>
#include <iostream>
#include <cstring>
char idTable[255][32] = { };
int main() {
using namespace std;
// initialize an entry
copy_n("tester", 7, idTable[43]);
// find match
auto match = [](const char* a) { return strcmp(a, "tester") == 0; };
auto index = find_if(begin(idTable), end(idTable), match) - idTable;
// print result
cout << "match at: " << index;
}
Prints
match at: 43
You need to add a nul to the end of the foundMatch array after copying in the idTable row:
foundMatch[strlen(idTable[index])] = '\0';
right before the 'foundMatch string lenght' (length) message.
strlen is an expensive function that walks the string every time. You should call that once, store it in a local variable, then reference that variable rather than calling strlen repeatedly.
I am working to validate that a string is utf8.
I have found method g_utf8_validate from glib, which returns:
true/false
the location of the last valid data that was read from the string
Is there a posibility to ge beyond this, and also get the valid data after the non-utf8 portion? Example:
std::string invalid = "okdata\xa0\xa1morevalid";
Currenlty I am able to save "okdata" but I would like to get "okdatamorevalid".
Any ideas? Thank you.
You could keep calling g_utf8_validate on the remaining string (skipping the first byte every time) to find more valid sections:
#include <iostream>
#include <string>
#include <glib.h>
int main() {
char const *data = "okdata\xa0\xa1morevalid";
std::string s;
// Under the assumption that the string is null-terminated.
// Otherwise you'll have to know the length in advance, pass it to
// g_utf8_validate and reduce it by (pend - p) every iteration. The
// loop condition would then be remaining_size > 0 instead of *pend != '\0'.
for(char const *p = data, *pend = data; *pend != '\0'; p = pend + 1) {
g_utf8_validate(p, -1, &pend);
s.append(p, pend);
}
std::cout << s << std::endl; // prints "okdatamorevalid"
}
You can call it in a loop. Something like this:
std::string sanitize_utf8(const std::string &in) {
std::string result;
const char *ptr = in.data(), *end = ptr + in.size();
while (true) {
const char *ptr2;
g_utf8_validate(ptr, end - ptr, &ptr2);
result.append(ptr, ptr2);
if (ptr2 == end)
break;
ptr = ptr2 + 1;
}
return result;
}
I'm stuck at designing this function:
//Turns "[0-9]+,[0-9]+" into two integers. Turns "[0-9]+" in two *equal* integers
static void parseRange(const std::string, int&, int&);
I don't have access to regular expressions (which would require either C++11 or Boost library). I need to somehow find out if the string contains 2 integers and split it, then get each integer.
I guess I'd need strstr version that uses std::string to find out if there's a comma and where. I could, probably, operate with std::string::c_str value. Extensive searching led me to this (but I want to use std::string, not C string):
void Generator::parseRange(const std::string str, int& min, int& max) {
const char* cstr = str.c_str();
const char* comma_pos;
//There's a comma
if((comma_pos=strstr(cstr, ","))!=NULL) { //(http://en.cppreference.com/w/cpp/string/byte/strstr)
//The distance between begining of string and the comma???
//Can I do this thing with pointers???
//Is 1 unit of pointer really 1 character???
unsigned int num_len = (comma_pos-cstr);
//Create new C string and copy the first part to it (http://stackoverflow.com/q/8164000/607407)
char* first_number=(char *)malloc((num_len+1)*sizeof(char));//+1 for \0 character
//Make sure it ends with \0
first_number[num_len] = 0;
//Copy the other string to it
memcpy(first_number, cstr, num_len*sizeof(char));
//Use atoi
min = atoi(first_number);
max = atoi(comma_pos+1);
//free memory - thanks #Christophe
free(first_number);
}
//Else just convert string to int. Easy as long as there's no messed up input
else {
min = atoi(cstr); //(http://www.cplusplus.com/reference/cstdlib/atoi/)
max = atoi(cstr);
}
}
I Googled a lot. You can't really say I didn't try. The function above works, but I'd prefer some less naive implementation, because what you see above is hardcore C code from the old times. And it all relies on fact that nobody messes up with input.
You can accomplish this by using the built in search facilities provided by std::string along with std::atoi without making copies or the need to use malloc or new to store parts of the string.
#include <cstdlib>
#include <string>
void Generator::parseRange(const std::string &str, int& min, int& max)
{
// Get the first integer
min = std::atoi(&str[0]);
// Check if there's a command and proces the second integer if there is one
std::string::size_type comma_pos = str.find(',');
if (comma_pos != std::string::npos)
{
max = std::atoi(&str[comma_pos + 1]);
}
// No comma, min and max are the same
else
{
max = min;
}
}
Alternatively as others have pointed out you can use std::istringstream to handle the integer parsing. This will allow you to do additional input validation when parsing the integer values
#include <sstream>
#include <string>
bool Generator::parseRange(const std::string& str, int& min, int& max)
{
std::istringstream sst(str);
// Read in the first integer
if (!(sst >> min))
{
return false;
}
// Check for comma. Could also check and error out if additional invalid input is
// in the stream
if (sst.get() != ',')
{
max = min;
return true;
}
// Read in the second integer
if (!(sst >> max))
{
return false;
}
return true;
}
What with this more native version:
void Generator::parseRange(const std::string str, int& min, int& max) {
stringstream sst(str);
if (!(sst>>min && sst.get()==',' && sst>>max))
cerr<<"String has an invalid format\n";
}
You can do all the searching and separating pretty easily with std::string functionality.
int pos = str.find(',');
assert(pos != std::string::npos);
std::string first = str.substr(0, pos);
std::string second = str.substr(pos+1, -1);
Alternatively, you can pretty easily do the parsing with a stringstream. For example:
std::istringstream s(str);
int one, two;
char ch;
s >> one >> ch >> two;
assert(ch == ',');
Note that this also makes it easy to combine separating the strings and converting the individual pieces into numbers.
No need for std::whatever, it will only consume more memory for a no less unreadable code.
Try this circa 1980 C code, it should do the trick:
void generator::parse_range (const std::string input, int & min, int & max)
{
const char * scan = input.c_str();
min = (int) strtol (scan, &scan, 0);
max = (*scan == ',') ? (int)strtol (scan+1, &scan, 0) : min;
if (errno || *scan != '\0') panic ("you call that numbers?");
}
This will accept hex or octal inputs, though you can fix the base with the 3rd parameter.
You could also check errno after first conversion or test for long integer overflow, but I assume this is not the worst part of your problem :)
What is the effective way to replace all occurrences of a character with another character in std::string?
std::string doesn't contain such function but you could use stand-alone replace function from algorithm header.
#include <algorithm>
#include <string>
void some_func() {
std::string s = "example string";
std::replace( s.begin(), s.end(), 'x', 'y'); // replace all 'x' to 'y'
}
The question is centered on character replacement, but, as I found this page very useful (especially Konrad's remark), I'd like to share this more generalized implementation, which allows to deal with substrings as well:
std::string ReplaceAll(std::string str, const std::string& from, const std::string& to) {
size_t start_pos = 0;
while((start_pos = str.find(from, start_pos)) != std::string::npos) {
str.replace(start_pos, from.length(), to);
start_pos += to.length(); // Handles case where 'to' is a substring of 'from'
}
return str;
}
Usage:
std::cout << ReplaceAll(string("Number Of Beans"), std::string(" "), std::string("_")) << std::endl;
std::cout << ReplaceAll(string("ghghjghugtghty"), std::string("gh"), std::string("X")) << std::endl;
std::cout << ReplaceAll(string("ghghjghugtghty"), std::string("gh"), std::string("h")) << std::endl;
Outputs:
Number_Of_Beans
XXjXugtXty
hhjhugthty
EDIT:
The above can be implemented in a more suitable way, in case performance is of your concern, by returning nothing (void) and performing the changes "in-place"; that is, by directly modifying the string argument str, passed by reference instead of by value. This would avoid an extra costly copy of the original string by overwriting it.
Code :
static inline void ReplaceAll2(std::string &str, const std::string& from, const std::string& to)
{
// Same inner code...
// No return statement
}
Hope this will be helpful for some others...
I thought I'd toss in the boost solution as well:
#include <boost/algorithm/string/replace.hpp>
// in place
std::string in_place = "blah#blah";
boost::replace_all(in_place, "#", "#");
// copy
const std::string input = "blah#blah";
std::string output = boost::replace_all_copy(input, "#", "#");
Imagine a large binary blob where all 0x00 bytes shall be replaced by "\1\x30" and all 0x01 bytes by "\1\x31" because the transport protocol allows no \0-bytes.
In cases where:
the replacing and the to-replaced string have different lengths,
there are many occurences of the to-replaced string within the source string and
the source string is large,
the provided solutions cannot be applied (because they replace only single characters) or have a performance problem, because they would call string::replace several times which generates copies of the size of the blob over and over.
(I do not know the boost solution, maybe it is OK from that perspective)
This one walks along all occurrences in the source string and builds the new string piece by piece once:
void replaceAll(std::string& source, const std::string& from, const std::string& to)
{
std::string newString;
newString.reserve(source.length()); // avoids a few memory allocations
std::string::size_type lastPos = 0;
std::string::size_type findPos;
while(std::string::npos != (findPos = source.find(from, lastPos)))
{
newString.append(source, lastPos, findPos - lastPos);
newString += to;
lastPos = findPos + from.length();
}
// Care for the rest after last occurrence
newString += source.substr(lastPos);
source.swap(newString);
}
A simple find and replace for a single character would go something like:
s.replace(s.find("x"), 1, "y")
To do this for the whole string, the easy thing to do would be to loop until your s.find starts returning npos. I suppose you could also catch range_error to exit the loop, but that's kinda ugly.
For completeness, here's how to do it with std::regex.
#include <regex>
#include <string>
int main()
{
const std::string s = "example string";
const std::string r = std::regex_replace(s, std::regex("x"), "y");
}
If you're looking to replace more than a single character, and are dealing only with std::string, then this snippet would work, replacing sNeedle in sHaystack with sReplace, and sNeedle and sReplace do not need to be the same size. This routine uses the while loop to replace all occurrences, rather than just the first one found from left to right.
while(sHaystack.find(sNeedle) != std::string::npos) {
sHaystack.replace(sHaystack.find(sNeedle),sNeedle.size(),sReplace);
}
As Kirill suggested, either use the replace method or iterate along the string replacing each char independently.
Alternatively you can use the find method or find_first_of depending on what you need to do. None of these solutions will do the job in one go, but with a few extra lines of code you ought to make them work for you. :-)
What about Abseil StrReplaceAll? From the header file:
// This file defines `absl::StrReplaceAll()`, a general-purpose string
// replacement function designed for large, arbitrary text substitutions,
// especially on strings which you are receiving from some other system for
// further processing (e.g. processing regular expressions, escaping HTML
// entities, etc.). `StrReplaceAll` is designed to be efficient even when only
// one substitution is being performed, or when substitution is rare.
//
// If the string being modified is known at compile-time, and the substitutions
// vary, `absl::Substitute()` may be a better choice.
//
// Example:
//
// std::string html_escaped = absl::StrReplaceAll(user_input, {
// {"&", "&"},
// {"<", "<"},
// {">", ">"},
// {"\"", """},
// {"'", "'"}});
#include <iostream>
#include <string>
using namespace std;
// Replace function..
string replace(string word, string target, string replacement){
int len, loop=0;
string nword="", let;
len=word.length();
len--;
while(loop<=len){
let=word.substr(loop, 1);
if(let==target){
nword=nword+replacement;
}else{
nword=nword+let;
}
loop++;
}
return nword;
}
//Main..
int main() {
string word;
cout<<"Enter Word: ";
cin>>word;
cout<<replace(word, "x", "y")<<endl;
return 0;
}
Old School :-)
std::string str = "H:/recursos/audio/youtube/libre/falta/";
for (int i = 0; i < str.size(); i++) {
if (str[i] == '/') {
str[i] = '\\';
}
}
std::cout << str;
Result:
H:\recursos\audio\youtube\libre\falta\
For simple situations this works pretty well without using any other library then std::string (which is already in use).
Replace all occurences of character a with character b in some_string:
for (size_t i = 0; i < some_string.size(); ++i) {
if (some_string[i] == 'a') {
some_string.replace(i, 1, "b");
}
}
If the string is large or multiple calls to replace is an issue, you can apply the technique mentioned in this answer: https://stackoverflow.com/a/29752943/3622300
here's a solution i rolled, in a maximal DRI spirit.
it will search sNeedle in sHaystack and replace it by sReplace,
nTimes if non 0, else all the sNeedle occurences.
it will not search again in the replaced text.
std::string str_replace(
std::string sHaystack, std::string sNeedle, std::string sReplace,
size_t nTimes=0)
{
size_t found = 0, pos = 0, c = 0;
size_t len = sNeedle.size();
size_t replen = sReplace.size();
std::string input(sHaystack);
do {
found = input.find(sNeedle, pos);
if (found == std::string::npos) {
break;
}
input.replace(found, len, sReplace);
pos = found + replen;
++c;
} while(!nTimes || c < nTimes);
return input;
}
I think I'd use std::replace_if()
A simple character-replacer (requested by OP) can be written by using standard library functions.
For an in-place version:
#include <string>
#include <algorithm>
void replace_char(std::string& in,
std::string::value_type srch,
std::string::value_type repl)
{
std::replace_if(std::begin(in), std::end(in),
[&srch](std::string::value_type v) { return v==srch; },
repl);
return;
}
and an overload that returns a copy if the input is a const string:
std::string replace_char(std::string const& in,
std::string::value_type srch,
std::string::value_type repl)
{
std::string result{ in };
replace_char(result, srch, repl);
return result;
}
This works! I used something similar to this for a bookstore app, where the inventory was stored in a CSV (like a .dat file). But in the case of a single char, meaning the replacer is only a single char, e.g.'|', it must be in double quotes "|" in order not to throw an invalid conversion const char.
#include <iostream>
#include <string>
using namespace std;
int main()
{
int count = 0; // for the number of occurences.
// final hold variable of corrected word up to the npos=j
string holdWord = "";
// a temp var in order to replace 0 to new npos
string holdTemp = "";
// a csv for a an entry in a book store
string holdLetter = "Big Java 7th Ed,Horstman,978-1118431115,99.85";
// j = npos
for (int j = 0; j < holdLetter.length(); j++) {
if (holdLetter[j] == ',') {
if ( count == 0 )
{
holdWord = holdLetter.replace(j, 1, " | ");
}
else {
string holdTemp1 = holdLetter.replace(j, 1, " | ");
// since replacement is three positions in length,
// must replace new replacement's 0 to npos-3, with
// the 0 to npos - 3 of the old replacement
holdTemp = holdTemp1.replace(0, j-3, holdWord, 0, j-3);
holdWord = "";
holdWord = holdTemp;
}
holdTemp = "";
count++;
}
}
cout << holdWord << endl;
return 0;
}
// result:
Big Java 7th Ed | Horstman | 978-1118431115 | 99.85
Uncustomarily I am using CentOS currently, so my compiler version is below . The C++ version (g++), C++98 default:
g++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-4)
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
This is not the only method missing from the standard library, it was intended be low level.
This use case and many other are covered by general libraries such as:
POCO
Abseil
Boost
QtCore
QtCore & QString has my preference: it supports UTF8 and uses less templates, which means understandable errors and faster compilation. It uses the "q" prefix which makes namespaces unnecessary and simplifies headers.
Boost often generates hideous error messages and slow compile time.
POCO seems to be a reasonable compromise.
How about replace any character string with any character string using only good-old C string functions?
char original[256]="First Line\nNext Line\n", dest[256]="";
char* replace_this = "\n"; // this is now a single character but could be any string
char* with_this = "\r\n"; // this is 2 characters but could be of any length
/* get the first token */
char* token = strtok(original, replace_this);
/* walk through other tokens */
while (token != NULL) {
strcat(dest, token);
strcat(dest, with_this);
token = strtok(NULL, replace_this);
}
dest should now have what we are looking for.