Cpp: parse string snippets to tuple - c++

I want to parse string snippets to a tuple:
example string: "Dolly Davenell,8809903417,1 Toban Circle,Luozhou"
tuple<string, unsigned int, string, string>
i read the strings from a file and store them with getline in a vector (myPersVec), where each vector element is a string as dscribed above.
Now my problem is that i don't know how to seperate each string element and fill it into each tuple element.
I know i can seperate the string elements with a delimeter character but how do i parse it into the tuple?
I then want to save each tuple into another Vector (my2ndVec)
My question is: Once i have the string tokens, how can i parse them to ONE tuple in the correct order?
auto makeT([](std::string line, std::vector<std::tuple<std::string, unsigned int, std::string, std::string>>& my2ndVec, std::vector<std::string> myPersVec) {
std::string token;
std::string deli = ",";
int pos = 0;
while ((pos = line.find(deli)) != std::string::npos) {
token = line.substr(0, pos);
std::cout << token << std::endl;
line.erase(0, pos + deli.length());
}
//how can i parse the tokens now into the tuple? and how do i parse them since i need to get multiple tokens
});
edit: typo

There are many ways to parse the data. You can use std::stringstream or find or whatever. I believe the question you are asking is how to store the values directly into a tuple. For that, use std::get which returns a reference to the value in the tuple.
// Parameter s is the line to parse. Ex: "Dolly Davenell,8809903417,1 Toban Circle,Luozhou"
std::tuple<std::string, long, std::string, std::string> parse_line(std::string s)
{
std::stringstream ss(s);
std::tuple<std::string, long, std::string, std::string> t;
if (std::getline(ss, std::get<0>(t), ',')) {
if (ss >> std::get<1>(t)) {
ss.ignore(1); // Skip comma
if (std::getline(ss, std::get<2>(t), ',') && std::getline(ss, std::get<3>(t))
return t;
}
}
}
// throw here or handle error somehow
}
I changed int to long as the value in the example is too large for 32-bit int.

Related

How can I erase first and last character in string?

This function returns an array of strings with a list of files in a folder. It looks like this:
"folder//xyz.txt"
How can I make it look like this?
folder//xyz.txt
Its the same but without "".
vector<string> list_of_files(string folder_name)
{
vector<string> files;
string path = folder_name;
for (const auto& entry : fs::directory_iterator(path))
{
stringstream ss;
ss << entry.path(); //convert entry.path() to string
string str = ss.str();
files.push_back(ss.str());
}
return files;
}
Erasing the first and last characters of a string is easy:
if (str.size() >= 1)
str.erase(0, 1); // from 1st char (#0), len 1; bit verbose as not designed for this
if (str.size() >= 1)
str.pop_back(); // chop off the end
Your quotes have come from inserting the path to a stream (quoted is used to help prevent bugs due to spaces down the line).
Fortunately, you don't need any of this: as explored in the comments, the stringstream is entirely unnecessary; the path already converts to a string if you ask it to:
vector<string> list_of_files(string folder_name)
{
vector<string> files;
for (const auto& entry : fs::directory_iterator(folder_name))
files.push_back(entry.path().string());
return files;
}

How to replace multiple sets of keywords in a string?

So I have a file of strings that I am reading in, and I have to replace certain values in them with other values. The amount of possible replacements is variable. As in, it reads the patterns to replace with in from a file. Currently I'm storing in a vector<pair<string,string>> for the patterns to find and match. However I run into issues:
Example:
Input string: abcd.eaef%afas&333
Delimiter patterns:
. %%%
% ###
& ###
Output I want: abcd%%%eaef###afas###333
Output I get: abcd#########eaef###afas###333
The issue being it ends up replacing the % sign or any other symbol that was already a replacement for something else, it should not be doing that.
My code is (relevant portions):
std::string& replace(std::string& s, const std::string& from, const std::string& to){
if(!from.empty())
for(size_t pos = 0; (pos = s.find(from, pos)) != std::string::npos; pos += to.size()) s.replace(pos, from.size(), to);
return s;
}
string line;
vector<pair<string, string>> myset;
while(getline(delimiterfile, line)){
istringstream is(line);
string delim, pattern;
if(is >> delim >> pattern){
myset.push_back(make_pair(delim, pattern));
} else {
throw runtime_error("Invalid pattern pair!");
}
}
while(getline(input, line)){
string temp = line;
for(auto &item : myset){
replace(temp, item.first, item.second);
}
output << temp << endl;
}
Can someone please tell me what I'm messing up and how to fix it?
In pseudo-code a simple replacement algorithm could look something like this:
string input = getline();
string output; // The string containing the replacements
for (each char in input)
{
if (char == '.')
output += "%%%";
// TODO: Other replacements
else
output += char;
}
If you implement the above code, once it's done the variable output will contain the string with all replacements made.
I would suggest you use stringstream. This way you will be able to achieve what you are looking for very easily.

How to extract specific elements from a string?

I am trying to extract the first numbers from each block of numbers from the next string.
string s = "f 1079//2059 1165//2417 1164//2414 1068//1980";
In this example I need to extract 1079, 1165, 1164 and 1068
I have tried with getline and substr but I have not been able to.
You can utilize the <regex>(C++ regular expression library) with pattern (\\d+)//. Locate the numbers before double slashes. Also using the parentheses to extract the numbers only by submatch.
Here is usage.
string s = "f 1079//2059 1165//2417 1164//2414 1068//1980";
std::regex pattern("(\\d+)//");
auto match_iter = std::sregex_iterator(s.begin(), s.end(), pattern);
auto match_end = std::sregex_iterator();
for (;match_iter != match_end; match_iter++)
{
const std::smatch& m = *match_iter;
std::cout << m[1].str() << std::endl; // sub-match for token in parentheses, the 1079, 1165, ...
// m[0]: whole match, "1079//"
// m[1]: first submatch, "1070"
}
I usually reach for istringstream for this kind of thing:
std::string input = "f 1079//2059 1165//2417 1164//2414 1068//1980";
std::istringstream is(input);
char f;
if (is >> f)
{
int number, othernumber;
char slash1, slash2;
while (is >> number >> slash1 >> slash2 >> othernumber)
{
// Process 'number'...
}
}
here is an attempt with getline and substring which works.
auto extractValues(const std::string& source)
-> std::vector<std::string>
{
auto target = std::vector<std::string>{};
auto stream = std::stringstream{ source };
auto currentPartOfSource = std::string{};
while (std::getline(stream, currentPartOfSource, ' '))
{
auto partBeforeTheSlashes = std::string{};
auto positionOfSlashes = currentPartOfSource.find("//");
if (positionOfSlashes != std::string::npos)
{
target.push_back(currentPartOfSource.substr(0, positionOfSlashes));
}
}
return target;
}
Or there is another split way to extract tokens, but it may involve some string copy.
Consider a split_by function like
std::vector<std::string> split_by(const std::string& str, const std::string& delem);
Possible implementations in Split a string in C++?
Make string be splitted by first, then splitted by // and extract first item.
std::vector<std::string> tokens = split_by(s, " ");
std::vector<std::string> words;
std::transform(tokens.begin() + 1, tokens.end(), // drop first "f"
std::back_inserter(words),
[](const std::string& s){ return split_by(s, "//")[0]; });

Pairs and stringstream

In a previous question I have asked how I could read and comfortably transform a vector of strings into a vector of integers or doubles.
Now I am expanding that vector of simple types to vector of pairs of types (consider int, double, or even std::string, all stream-enabled types) separated by colons.
Example:
time:5, length:10
Should be read as a std::pair<std::string, unsigned int>, with something like this:
// Here declare what to parse: a string and an uint, separated by a colon
get_vector<std::pair<std::string, unsigned int>>(s);
How can I write a std::stringstream operator that does the trick?
I cannot figure how (or even if) I could play with getline (my code follows). I'd really like to leave the get_vector function as it is, if possible.
Thanks!
template <class F, class S>
std::stringstream& operator >> (std::stringstream& in, std::pair<F, S> &out)
{
in >> out.first >> out.second;
return in;
}
template <class T>
auto get_vector(std::string s) -> std::vector<T>
{
std::vector<T> v;
// Vector of strings
auto tmp = split(s);
// Magic
std::transform(tmp.begin(), tmp.end(), std::back_inserter(v),
[](const std::string& elem) -> T
{
std::stringstream ss(elem);
T value;
ss >> value;
return value;
});
return v;
}
Your operator>> receives a std::stringstream which in that moment contains a string like "time:5". The operator needs to parse that string by splitting it up into two tokens, then convert the first token to an F and the second to an S.
One way to do this is to use std::getline with a custom delimiter. You will end up with two std::string tokens. These can be converted, for example, with two internal std::istringstreams.
Here is some code you can try:
template <class F, class S>
std::stringstream& operator >> (std::stringstream& in, std::pair<F, S> &out)
{
std::string first_token;
std::string second_token;
std::getline(in, first_token, ':');
std::getline(in, second_token);
std::istringstream first_converter(first_token);
std::istringstream second_converter(second_token);
first_converter >> out.first;
second_converter >> out.second;
return in;
}
You may want to add some error handling for the case when there is no ':' in the string.
Note that you should not use std::stringstream if you do not want to combine input and output. Most of the times, you will want either std::istringstream or std::ostringstream.
Because I am new in English I am not sure to understand what you want.
I jest seeing that #Christian Hackl do what. So if this answer does not
relate to what you want please tell me and I will delete it.
I assume you need to extract a string from string or stream and then put them into a vector that contains a std::pair< std::string, std::size_t >
Say you have this string that can be an input-stream, too:
std::string string( "one:1, two:20, three:300, four:40000, five:500000" );
There is more than one way to do it, but I say it in this way!
So first you need to split this string and then put what has split, in to vector of pair.
std::string string( "one:1, two:20, three:300, four:40000, five:500000" );
std::stringstream io_stream;
std::basic_regex< char > regex( ", " );
std::regex_token_iterator< std::string::const_iterator > last;
std::regex_token_iterator< std::string::const_iterator > first( string.begin(), string.end(), regex, -1 );
std::vector< std::pair< std::string, std::size_t > > vector_of_pair;
std::string str_pair( "" );
std::size_t ui_pair( 0 );
while( first != last ){
io_stream.clear();
io_stream.seekg( 0 );
io_stream.seekp( 0 );
io_stream << *first << '\n';
std::getline( io_stream, str_pair, ':' );
// error:
// no match function to call: stream and int
// std::getline( io_stream, ui_pair);
io_stream >> ui_pair;
vector_of_pair.emplace_back( str_pair, ui_pair );
++first;
}
test
for( std::size_t index = 0; index < vector_of_pair.size(); ++index ){
std::cout << vector_of_pair[ index ].first << " and " << vector_of_pair[ index ].second << '\n';
}
output
one and 1
two and 20
three and 300
four and 40000
five and 500000
how it work
There is no difficult code to understand. It just by std::regex_token_iterator splits the string, then in a while loop send the value of it to is_stream, then read the line and send it to str_pair, but here because std::getline has not function to extracts int type, I used >> of stringstream itself, then emplace_back to vector and that's it.
NOTE
For:
How can I write a std::stringstream operator that does the trick?
I think now you can do it by yourself. Since I was not sure, I have not written the code with operator>>.

Converting string to char and int data types

I've populated a string vector with with numbers and characters (*,+,-,/). I want to assign each number and character to two new vector, and int vector and a char vector. Is there a way to convert the everything from string to the desired data type?
You can use string stream in the <sstream> header.
string myString = "123";
stringstream sStream( myString );
int convertedInt;
sStream >> convertedInt.
Include the <sstream> header and you can do something like this:
std::vector<std::string> stringVector = /* get data from somewhere */
std::vector<int> intVector;
std::vector<char> charVector;
for (std::vector<std::string>::const_iterator it = stringVector.begin(); it != stringVector.end(); it++)
{
if (it->length() == 0)
continue; // ignore any empty strings
int intValue;
std::istingstream ss(*it);
if (ss >> someValue) // try to parse string as integer
intVector.push_back(someValue); // int parsed successfully
else
charVector.pushBack((*it)[0]);
}
This assumes anything that cannot be parsed as an integer should be pushed into the char vector instead (so, 234, 100000 and -34 will be put into intVector, and /, + etc will be put into charVector). Only the first character of a non-integer value is pushed, so if you have *hello or *123, only * will be put into the charVector.
If you are using C++11, you can swap the std::vector<std::string>::const_iterator with auto to make it look a bit nicer.