I am trying to split string using delimiters but I want to keep the delimiters in the array. Code :
QRegExp rx("(\\+|\\-|\\*|\\/)");
QStringList query = text.split(rx);
Input:
2+3
This will give me an array
2,3 but I want 2,+,3
Is there any way to do that ?
You can have a work around solution for your problem.
Try this code :
#include <iostream>
#include <QStringList>
#include <QRegExp>
int main()
{
QString text = "2+3-3-4/5+9+0"; // Input, you can write you own code to take input
QRegExp rx("(\\+|\\-|\\*|\\/)");
QStringList query = text.split(rx);
int count = 0;
int pos = 0;
while ((pos = rx.indexIn(text, pos)) != -1) {
++count;
pos += rx.matchedLength();
query.insert(count * 2-1, QString(text[pos - 1]));
}
return 0;
}
I don't think there's a function in Qt that does it for you, but you could easily reconstruct it. Pseudo code, because I don't know the exact syntax:
QStringList query = text.split(rx);
QStringList queryWithSeparators;
size_t pos = 0;
for (const auto part : query) {
queryWithSeparators.append(part);
pos += part.length;
if (pos + 1 < text.length) {
// we know that the separators are all 1 character long
queryWithSeparators.append(text.substring(pos, 1));
pos += 1;
}
}
This is ugly and difficult to understand. From you example it seems that you are trying to parse a mathematical expression. It's much easier to create a tokenizer which reads character-by-character than trying to use regular expressions for this task.
(If you really want to use split, you could first split it for all +, then split these strings at all - etc. This way you would know exactly what the separators are.)
Related
I am writing what amounts to a tiny DSL in which each script is read from a single string, like this:
"func1;func2;func1;4*func3;func1"
I need to expand the loops, so that the expanded script is:
"func1;func2;func1;func3;func3;func3;func3;func1"
I have used the C++ standard regex library with the following regex to find those loops:
regex REGEX_SIMPLE_LOOP(":?[0-9]+)\\*([_a-zA-Z][_a-zA-Z0-9]*;");
smatch match;
bool found = std::regex_search(*this, match, std::regex(REGEX_SIMPLE_LOOP));
Now, it's not too difficult to read out the loop multiplier and print the function N times, but how do I then replace the original match with this string? I want to do this:
if (found) match[0].replace(new_string);
But I don't see that the library can do this.
My backup place is to regex_search, then construct the new string, and then use regex_replace, but it seems clunky and inefficient and not nice to essentially do two full searches like that. Is there a cleaner way?
You can also NOT use regex, the parsing isn't too difficult.
So regex might be overkill. Demo here : https://onlinegdb.com/RXLqLtrUQ-
(and yes my output gives an extra ; at the end)
#include <string>
#include <sstream>
#include <iostream>
int main()
{
std::istringstream is{ "func1;func2;func1;4*func3;func1" };
std::string split;
// use getline to split
while (std::getline(is, split, ';'))
{
// assume 1 repeat
std::size_t count = 1;
// if split part starts with a digit
if (std::isdigit(split.front()))
{
// look for a *
auto pos = split.find('*');
// the first part of the string contains the repeat count
auto count_str = split.substr(0, pos);
// convert that to a value
count = std::stoi(count_str);
// and keep the rest ("funcn")
split = split.substr(pos + 1, split.size() - pos - 1);
}
// now use the repeat count to build the output string
for (std::size_t n = 0; n < count; ++n)
{
std::cout << split << ";";
}
}
// TODO invalid input string handling.
return 0;
}
Given the code:
procedure example {
x=3;
y = z +c ;
while p {
b = a+c ;
}
}
I would like to split the code by using the delimiters {, ;, and }.
After splitting, I would like to get the information before it together with the delimiter.
So for example, I would like to get procedure example {, x=3;, y=z+c;, }. Then I would like to push it into a list<pair<int, string>> sList. Could someone explain how this can be done in c++?
I tried following this example: Parse (split) a string in C++ using string delimiter (standard C++), but I could only get one token. I want the entire line. I am new to c++, and the list, splitting, etc. is confusing.
Edit: So I have implemented it, and this is the code:
size_t openCurlyBracket = lines.find("{");
size_t closeCurlyBracket = lines.find("}");
size_t semiColon = lines.find(";");
if (semiColon != string::npos) {
cout << lines.substr(0, semiColon + 1) + "\n";
}
However, it seems that it can't separate based on semicolon separately, openBracket and closeBracket. Anyone knows how to separate based on these characters individually?
2nd Edit:
I have done this (codes below). It is separating correctly, I have one for open curly bracket. I was planning on adding the value to the list in the commented area below. However, when i think about it, if i do that, then the order of information in the list will be messed up. As i have another while loop which separates based on open curly bracket. Any idea on how i can add the information in an order?
Example:
1. procedure example {
2. x=3;
3. y = z+c
4. while p{
and so on.
while (semiColon != string::npos) {
semiColon++;
//add list here
semiColon = lines.find(';',semiColon);
}
I think that you should read about std::string::find_first_of function.
Searches the string for the first character that matches any of the characters specified in its arguments.
I have a problem to understand what you really want to achieve. Let's say this is an example of the find_first_of function use.
list<string> split(string lines)
{
list<string> result;
size_t position = 0;
while((position = lines.find_first_of("{};\n")) != string::npos)
{
if(lines[position] != '\n')
{
result.push_back(lines.substr(0, position+1));
}
lines = lines.substr(position+1);
}
return result;
}
I have a string as follows,
"0/41/9/71.94 PC:0x82cc (add)"
The desired output is the text between the brackets ( )
Ex: output = add,
for the string specified above
How is this done using sscanf?
Is there a better way to do it in C++?
With string operations exclusively:
std::string text = "0/41/9/71.94 PC:0x82cc (add)";
auto pos = text.find('(') + 1;
auto opcode = text.substr(pos, text.find(')', pos) - pos);
Demo.
With sscanf it would look something like this:
std::string opcode(5, '\0'); // Some suitable maximum size
sscanf(text.c_str(), "%*[^(](%[^)]", &opcode[0]);
Demo.
Its very easy, u should try yourself, think how to search in an array, then think if i could compare the content of an array or not, then every thing would be possible, as a programmer u have to create ideas, however if i were asked to write a program like this i would do that as follows:
int i=0, p=0;
char string="0/41/9/71.94 PC:0x82cc (add)", nstr[100];
while(string[i]!='\0')
{
while(string[i]!='(')
i++;
if (string[i]=='(')
{
i++;
goto end;
}
end:
while (string[i]!=')' || string[i]!='\0')
{
nstr[p]=string[i];
p++;
i++;
}
nstr[p]='\0';
cout<<Output = "<<nstr<<"\n";
I know this is very long, but this will give you deeper understanding of parsing or spliting a string, hope i help u, thank u...
I have a single-space delimited string and I want to replace field x.
I can repeatedly use find to locate the x - 1 and x spaces, then use substr to grab the two strings on either side, then concatenate the two sub strings and my replacement text.
But man that seems like an awful lot of work for something that should be simple. Is there a better solution-- one that doesn't require Boost?
Answer
I've cleaned up #Domenic Lokies answer below:
sting fieldReplace( const string input, const string outputField, int index )
{
vector< char > stringIndex( numeric_limits< int >::digits10 + 2 );
_itoa_s( index, stringIndex.begin()._Ptr, stringIndex.size(), 10 );
const string stringRegex( "^((?:\\w+ ){" ); //^((?:\w+ ){$index})\w+
return regex_replace( input, regex( stringRegex + stringIndex.begin()._Ptr + "})\\w+" ), "$1" + outputField );
}
(_itoa_s and _Ptr are MSVS only I believe, so you'll need to clean those up if you want code portability. )
You can do it using one of the string::replace methods:
Locate the position of the x-1-st space. You can do it by calling string::find repeatedly
Locate the position of the x-th space by calling string::find one more time
Calculate the length of the word being replaced by subtracting the first index from the second one
Call string::replace passing the first index, the length, and the replacement string.
Here is how you can implement this:
#include <iostream>
#include <string>
using namespace std;
int main() {
string s = "quick brown frog jumps over the lazy dog";
size_t start = -1;
int cnt = 3; // Word number three
do {
start = s.find(' ', start+1);
} while (start != string::npos && --cnt > 1);
size_t end = s.find(' ', start+1);
s.replace(start+1, end-start-1, "fox");
cout << s << endl;
return 0;
}
Demo on ideone.
Since C++11 you should use a Regular Expression for your purposes. If you are not using a compiler which supports C++11, you can take a look at Boost.Regex.
Never combine std::string::find with std::string::replace, that is just not a good style in a language like C++.
I have written a short example for you to show you how to use Regular Expressions in C++.
#include <string>
#include <regex>
#include <iostream>
int main()
{
std::string subject = "quick brown frog jumps over the lazy dog";
std::regex pattern("frog");
std::cout << std::regex_replace(subject, pattern, "fox");
}
I want to replace some words without using external libraries.
My first attempt was to make a copy of the string, but it was not efficient, so this is another attempt where I use addresses:
void ReplaceString(std::string &subject, const std::string &search, const std::string &replace)
{
size_t position = 0;
while ((position = subject.find(search, position)) != std::string::npos) //if something messes up --> failure
{
subject.replace(position, search.length(), replace);
position = position + replace.length();
}
}
Because this is not very efficient either, I want to use another thing, but I got stuck; I want to use a function like replace_stuff(std::string & a); with a single parameter using string.replace() and string.find() (parsing it with a for loop or something) and then make use of std::map <std::string,std::string>; which is very convenient for me.
I want to use it for a large number of input words. (let's say replacing many bad words with some harmless ones)
The problem with your question is the lack of the necessary components in the Standard library. If you want an efficient implementation, you'd probably need a trie for efficient lookups. Writing one as part of the answer would be way to much code.
If you use a std::map or, if C++11 is available in your environment, a std::unordered_map, you will need to utilitize additional information about the input string and the search-replace pairs from the map. You'd then tokenize the string and check each token if it has to be replaced. Using positions pointing in the input string is a good idea since it avoids copying data. Which brings us to:
Efficiency will depend on memory access (reads and writes), so you should not modify the input string. Create the output by starting with an empty string and by appending pieces from the input. Check each part of the input: If it is a word, check if it needs to be replaced or if it is appended to the output unmodified. If it is not part of a word, append it unmodified.
It sounds like you want to replace all the "bad" words in a string with harmless ones, but your current implementation is inefficient because the list of bad words is much larger than the length of your input string (subject). Is this correct?
If so, the following code should make it more efficient. As you can see, I had to pass the map as a parameter, but if your function is going to be part of a class, you don't need to do so.
void ReplaceString(std::string &subject, const std::map<std::string, std::string>& replace_map)
{
size_t startofword = 0, endofword = 0;
while(startofword < subject.size())
{
size_t length = std::string::npos;
//get next word in string
endofword = subject.find_first_of(" ", startofword);
if(endofword != std::string::npos)
length = endofword-startofword;
std::string search = subject.substr(startofword, length);
//try to find this word in the map
if(replace_map.find(search) != replace_map.end())
{
//if found, replace the word with a new word
subject.replace(startofword, length, replace_map[search]);
startofword += replace_map[search].length();
}
else
{
startofword += length;
}
}
}
I use the following functions, hope it helps:
//=============================================================================
//replaces each occurence of the phrase in sWhat with sReplacement
std::string& sReplaceAll(std::string& sS, const std::string& sWhat, const std::string& sReplacement)
{
size_t pos = 0, fpos;
while ((fpos = sS.find(sWhat, pos)) != std::string::npos)
{
sS.replace(fpos, sWhat.size(), sReplacement);
pos = fpos + sReplacement.length();
}
return sS;
}
//=============================================================================
// replaces each single char from sCharList that is found within sS with entire sReplacement
std::string& sReplaceChars(std::string& sS, const std::string& sCharList, const std::string& sReplacement)
{
size_t pos=0;
while (pos < sS.length())
{
if (sCharList.find(sS.at(pos),0)!=std::string::npos) //pos is where a charlist-char was found
{
sS.replace(pos, 1, sReplacement);
pos += sReplacement.length()-1;
}
pos++;
}
return sS;
}
You might create a class, say Replacer:
class Replacer
{
std::map<std::string,> replacement;
public:
Replacer()
{
// init the map here
replacement.insert ( std::pair<std::string,std::string>("C#","C++") );
//...
}
void replace_stuff(std::string & a);
}
Then the replace_stuff definition would be very similar to your original ReplaceString (it would use map entries instead of the passed parameters).