Replace Function C++ vb.net - c++

i´m new in C++ and my first function is the replace function from vb.net.
I want to know how to make this function perfect.
Should i wrote a "function" with return value instead of void?
(Sorry about my english)
void ReplaceAll(std::string &source, std::string find, std::string replace) {
if (find.length() == 0) {
return;
}
int _offset = 0;
while (true) {
_offset = source.find(find, _offset);
if (_offset == -1) {
return;
}
source.replace(_offset, find.length(), replace);
}
}

Related

How can I speed up parsing of large strings?

So I've made a program that reads in various config files. Some of these config files can be small, some can be semi-large (largest one is 3,844 KB).
The read in file is stored in a string (in the program below it's called sample).
I then have the program extract information from the string based on various formatting rules. This works well, the only issue is that when reading larger files it is very slow....
I was wondering if there was anything I could do to speed up the parsing or if there was an existing library that does what I need (extract string up until a delimiter & extract string string in between 2 delimiters on the same level). Any assistance would be great.
Here's my code & a sample of how it should work...
#include "stdafx.h"
#include <string>
#include <vector>
std::string ExtractStringUntilDelimiter(
std::string& original_string,
const std::string& delimiter,
const int delimiters_to_skip = 1)
{
std::string needle = "";
if (original_string.find(delimiter) != std::string::npos)
{
int total_found = 0;
auto occurance_index = static_cast<size_t>(-1);
while (total_found != delimiters_to_skip)
{
occurance_index = original_string.find(delimiter);
if (occurance_index != std::string::npos)
{
needle = original_string.substr(0, occurance_index);
total_found++;
}
else
{
break;
}
}
// Remove the found string from the original string...
original_string.erase(0, occurance_index + 1);
}
else
{
needle = original_string;
original_string.clear();
}
if (!needle.empty() && needle[0] == '\"')
{
needle = needle.substr(1);
}
if (!needle.empty() && needle[needle.length() - 1] == '\"')
{
needle.pop_back();
}
return needle;
}
void ExtractInitialDelimiter(
std::string& original_string,
const char delimiter)
{
// Remove extra new line characters
while (!original_string.empty() && original_string[0] == delimiter)
{
original_string.erase(0, 1);
}
}
void ExtractInitialAndFinalDelimiters(
std::string& original_string,
const char delimiter)
{
ExtractInitialDelimiter(original_string, delimiter);
while (!original_string.empty() && original_string[original_string.size() - 1] == delimiter)
{
original_string.erase(original_string.size() - 1, 1);
}
}
std::string ExtractStringBetweenDelimiters(
std::string& original_string,
const std::string& opening_delimiter,
const std::string& closing_delimiter)
{
const size_t first_delimiter = original_string.find(opening_delimiter);
if (first_delimiter != std::string::npos)
{
int total_open = 1;
const size_t opening_index = first_delimiter + opening_delimiter.size();
for (size_t i = opening_index; i < original_string.size(); i++)
{
// Check if we have room for opening_delimiter...
if (i + opening_delimiter.size() <= original_string.size())
{
for (size_t j = 0; j < opening_delimiter.size(); j++)
{
if (original_string[i + j] != opening_delimiter[j])
{
break;
}
else if (j == opening_delimiter.size() - 1)
{
total_open++;
}
}
}
// Check if we have room for closing_delimiter...
if (i + closing_delimiter.size() <= original_string.size())
{
for (size_t j = 0; j < closing_delimiter.size(); j++)
{
if (original_string[i + j] != closing_delimiter[j])
{
break;
}
else if (j == closing_delimiter.size() - 1)
{
total_open--;
}
}
}
if (total_open == 0)
{
// Extract result, and return it...
std::string needle = original_string.substr(opening_index, i - opening_index);
original_string.erase(first_delimiter, i + closing_delimiter.size());
// Remove new line symbols
ExtractInitialAndFinalDelimiters(needle, '\n');
ExtractInitialAndFinalDelimiters(original_string, '\n');
return needle;
}
}
}
return "";
}
int main()
{
std::string sample = "{\n"
"Line1\n"
"Line2\n"
"{\n"
"SubLine1\n"
"SubLine2\n"
"}\n"
"}";
std::string result = ExtractStringBetweenDelimiters(sample, "{", "}");
std::string LineOne = ExtractStringUntilDelimiter(result, "\n");
std::string LineTwo = ExtractStringUntilDelimiter(result, "\n");
std::string SerializedVector = ExtractStringBetweenDelimiters(result, "{", "}");
std::string SubLineOne = ExtractStringUntilDelimiter(SerializedVector, "\n");
std::string SubLineTwo = ExtractStringUntilDelimiter(SerializedVector, "\n");
// Just for testing...
printf("LineOne: %s\n", LineOne.c_str());
printf("LineTwo: %s\n", LineTwo.c_str());
printf("\tSubLineOne: %s\n", SubLineOne.c_str());
printf("\tSubLineTwo: %s\n", SubLineTwo.c_str());
system("pause");
}
Use string_view or a hand rolled one.
Don't modify the string loaded.
original_string.erase(0, occurance_index + 1);
is code smell and going to be expensive with a large original string.
If you are going to modify something, do it in one pass. Don't repeatedly delete from the front of it -- that is O(n^2). Instead, procceed along it and shove "finished" stuff into an output accumulator.
This will involve changing how your code works.
You're reading your data into a string. "Length of string" should not be a problem. So far, so good...
You're using "string.find().". That's not necessarily a bad choice.
You're using "string.erase()". That's probably the main source of your problem.
SUGGESTIONS:
Treat the original string as "read-only". Don't call erase(), don't modify it.
Personally, I'd consider reading your text into a C string (a text buffer), then parsing the text buffer, using strstr().
Here is a more efficient version of ExtractStringBetweenDelimiters. Note that this version does not mutate the original buffer. You would perform subsequent queries on the returned string.
std::string trim(std::string buffer, char what)
{
auto not_what = [&what](char ch)
{
return ch != what;
};
auto first = std::find_if(buffer.begin(), buffer.end(), not_what);
auto last = std::find_if(buffer.rbegin(), std::make_reverse_iterator(first), not_what).base();
return std::string(first, last);
}
std::string ExtractStringBetweenDelimiters(
std::string const& buffer,
const char opening_delimiter,
const char closing_delimiter)
{
std::string result;
auto first = std::find(buffer.begin(), buffer.end(), opening_delimiter);
if (first != buffer.end())
{
auto last = std::find(buffer.rbegin(), std::make_reverse_iterator(first),
closing_delimiter).base();
if(last > first)
{
result.assign(first + 1, last);
result = trim(std::move(result), '\n');
}
}
return result;
}
If you have access to string_view (c++17 for std::string_view or boost::string_view) you could return one of these from both functions for extra efficiency.
It's worth mentioning that this method of parsing a structured file is going to cause you problems down the line if any of the serialised strings contains a delimiter, such as a '{'.
In the end you'll want to write or use someone else's parser.
The boost::spirit library is a little complicated to learn, but creates very efficient parsers for this kind of thing.

Find a number inside a QString

I have a QString with some number inside it, for example
first_34.33string
second-23.4string // In this case number is negative
How can I extract number from the string?
EDIT:
This function seems to work, using regexp in replies:
float getNumberFromQString(const QString &xString)
{
QRegExp xRegExp("(-?\\d+(?:[\\.,]\\d+(?:e\\d+)?)?)");
xRegExp.indexIn(xString);
QStringList xList = xRegExp.capturedTexts();
if (true == xList.empty())
{
return 0.0;
}
return xList.begin()->toFloat();
}
This should work for valid numbers: QRegExp("(-?\\d+(?:[\\.,]\\d+(?:e\\d+)?)?)")
edit: sorry, messed up with the brackets, now it should work.
I would write a simple function for that:
static double extractDouble(const QString &s)
{
QString num;
foreach(QChar c, s) {
if (c.isDigit() || c == '.' || c == '-') {
num.append(c);
}
}
bool ok;
double ret = num.toDouble(&ok);
if (ok) {
return ret;
} else {
throw "Cannot extract double value";
}
}

For the life of me, I can't compare the first character of this array

All I want to do is pass in a char* buffer and compare that to a literal string "#" -- why is this so difficult for me.
char* buffer = "#3702";
string b(buffer);
string c("#");
if (strncmp(b.c_str(), c.c_str(), 1) == 0)
{
perror("Buffer malformated!");
return false;
}
What do I not understand about this?
Edit: haaaa, != not == whoops :)
If you just want to compare char*and use strncmp(), you don't need to use stl string for this.
int main()
{
char* buffer = "#3702";
char* c = "#";
if (strncmp(buffer, c, strlen(c)) == 0)
{
//same string
return true;
}
else
{
//not same string
return false;
}
getchar();
}
And, remember char[] can convert to char*, so in this case, above code is similar to below code.
int main()
{
char buffer[] = "#3702";
char c[] = "#";
if(buffer[0] == c[0])
{
//same string
return true;
}
else
{
//not same string
return false;
}
getchar();
}

C++, How do get this function to delete the string stored in the array?

I have a delete function that is supposed to delete a string in an array by writing over it with the previous strings.
The look function see's that Overide matches and should be deleted. But the code i wrote for the loop in Delete is not removing that first spot in the array that Overide has taken up, and the output remains unchanged.
Also each phrase after + is being added into the array so four spots are taken in the array, and sorry i could not make that part look better the formatting screwed it up.
int AR::Look(const std::string & word)
{
int result = -1;
for(int i=0; i<counter; ++i)
{
if( con[i].find(word) != std::string::npos)
result = i;
}
return result;
}
void AR::Delete(const string & word)
{
int loc = Look(word);
if (loc == -1)
{
cout<<"word not found\n";
}
else
{
for(int i=0; i<counter-1,i++;)
{
con[i]= con[i+1];
}
}
}
AR their
Ar(1);
theirAr + "Overload the +" + " operator as a member function " + "with chaining to add a string " + "to an Arrary object.";
cout<<theirAr<<endl<<endl;
cout<<"testing Delete and Look. <<endl;
theirAr.Delete("XXXXXX");
theirAr.Delete("Overload");
cout<<"Output after Delete and Look called\n";
cout<<theirArray<<endl<<endl;
You are locating the String but only use the value to write an error if it does not appear; if you find the string at pos N you will delete the first string anyway:
void AR::Delete(const string & word)
{
int loc = Look(word);
if (loc == -1)
{
cout<<"word not found\n";
}
else
{
for(int i=0;i<counter-1,i++;) <--- Why don't you use loc here???
{
con[i]= con[i+1];
}
}
}
Also, your Look method would be better returning after the first match:
for ... {
if( con[i].find(word) != std::string::npos)
return i;
}
return -1;
Not sure if this is your problem, but shouldn't this be like so?
void AR::Delete(const string & word)
{
int loc = Look(word);
if (loc == -1)
{
cout<<"word not found\n";
}
else
{
for(int i=loc;i<counter-1,i++;) // changes in this line
{
con[i]= con[i+1];
}
}
}
Start at where you found the string and start shuffling them backwards. Also, what shortens the array? i.e. drops the last element off. Looks like that is missing too.
Try this instead:
int AR::Look(const std::string & word)
{
for (int i = 0; i < counter; ++i)
{
if (con[i].find(word) != std::string::npos)
return i;
}
return -1;
}
void AR::Delete(const string & word)
{
int loc = Look(word);
if (loc == -1)
{
cout << "word not found" << endl;
}
else
{
for (int i = loc+1; i < counter; ++i)
{
con[i-1] = con[i];
}
--counter;
}
}

Why does the compiler think a std::string& is passed not a std:string

My Question is why in processSymbol(const string, enumDataType) does the compiler think I am passing a std::string&. I used processSymbol(const string, enumDataType) earlier in my code and work just as intended, but in this function I am getting this erro
error: call of overloaded 'processSymbol(std::string&, symsErrorCode&)' is ambiguous|
note: candidates are:
note: void processSymbol(std::string, symsErrorCode&)|
note: void processSymbol(std::string, symsErrorCode)|
Here is the function I am wondering about.
void processSearchForSymbols(ifstream & inSyms, BST & symTable){
symsErrorCode symsError;
string symbol;
while(inSyms >> symbol){
symsError = NO_ERROR;
processSymbol(symbol, symsError);
}
}
The function where the processSymbol() compiled how I thought it should is this:
void processInsertSymbolData(ifstream & inFile, BST & symTable){
string symbol, value, rFlag, fourCharSymbol;
symsErrorCode symsError;
while(inFile >> symbol >> value >> rFlag){
symsError = NO_ERROR;
processSymbol(symbol, symsError);
if(symsError == NO_ERROR)
{
fourCharSymbol = createFourCharSymbol(symbol);
processValue(value, symsError);
if(symsError == NO_ERROR)
{
processRFlag(rFlag, symsError);
if(symsError == NO_ERROR)
{
insertIntoSymTable(symsError, fourCharSymbol, value, rFlag, symTable);
}
}
}
errorOutput( symsError, symbol, fourCharSymbol, value);
}
return;
}
Here is the processSymbol(const string symbol, symsErrorCode symsError) function
void processSymbol(const string symbol, symsErrorCode symsError){
if(symbol.length() > 10)
{
symsError = LENGTH;
}
else if(!isalpha(symbol[0]))
{
symsError = START_CHAR;
}
else
{
for(unsigned int i = 1; i < symbol.length(); i++)
{
if( !isalnum(symbol[i]) && symbol[i] != '_' )
{
symsError = INVALID_CHAR;
}
}
}
return;
}
The compiler can't tell the difference between the two declarations of processSymbol that you are trying to call. I would suggest using a pointer (*) instead of & for the enum. That will make it clear to the compiler which version of the function you are intending to call.
processSymbol(const string symbol, symsErrorCode* symsError) ...