Reputation: 977
Parsing the given string to be able to construct a graph . I have been given this string
string str ="abc,def,0.9;ghi,jkl,109;mno,par,155";
I need to parse this string such that i can construct an edge between abc
and def
, ghi
and jkl
, mno
and pqr
with weights 0.9
, 109
, 155
.
I am stuck only in parsing part .
stringstream ss(str);
for(int i =0 ;i<str.size();i++){
string substr;
getline(ss, substr, ',');
cout <<substr<<endl;
}
But this gives 0.9;ghi
on one line.
Any suggestions will be highly appreciated.
Upvotes: 0
Views: 322
Reputation: 117258
An alternative without using stringstream
s could be using std::string_view
s and std::from_chars
Example:
#include <charconv>
#include <iostream>
#include <string>
#include <string_view>
struct edge {
std::string a, b;
double weight;
// a function to parse a string_view and to return the unconsumed part of it
std::string_view parse(std::string_view str) {
auto curr = str.substr(0, str.find(';'));
auto len = curr.size();
auto f = [](std::string_view& curr, std::string& out) {
auto comma = curr.find(',');
if (comma == std::string_view::npos) return false;
out = curr.substr(0, comma);
curr = curr.substr(comma + 1);
return true;
};
// if not both `a` and `b` be populated, return that nothing was consumed
if (!f(curr, a) || !f(curr, b)) return str; // nothing consumed
// extract the weight
auto [ptr, ec] =
std::from_chars(curr.data(), curr.data() + curr.size(), weight);
if (ec != std::errc{}) return str; // nothing consumed
// return everything but the consumed part of `str`:
return len == str.size() ? std::string_view{} : str.substr(len + 1);
}
// reuse the `parse` function when reading from a stream:
friend std::istream& operator>>(std::istream& is, edge& e) {
if (std::string full; std::getline(is, full, ';')) {
// if `parse` returns a string_view of the same size given as input,
// nothing was consumed, so set the failbit:
if (e.parse(full).size() == full.size())
is.setstate(std::ios::failbit);
}
return is;
}
friend std::ostream& operator<<(std::ostream& os, const edge& e) {
return os << e.a << ',' << e.b << ',' << e.weight;
}
};
Then, if you already have a std::string
, you can simply call parse
in a loop:
int main() {
std::string str = "abc,def,0.9;ghi,jkl,109;mno,par,155";
edge x;
for(std::string_view sv = str, newsv;
(newsv = x.parse(sv)).size() != sv.size(); sv = newsv)
{
std::cout << x << '\n';
}
}
Output:
abc,def,0.9
ghi,jkl,109
mno,par,155
If you are reading from a std::istream
:
int main() {
// std::ifstream is("some_file");
for(edge x; is >> x;) { // loop while parsing successfully
std::cout << x << '\n';
}
}
Upvotes: 1
Reputation: 310930
You can use just one more string stream as for example
#include <iostream>
#include <sstream>
#include <string>
int main()
{
std::string str ="abc,def,0.9;ghi,jkl,109;mno,par,155";
std::istringstream iss1( str );
std::string line;
while ( std::getline( iss1, line, ';' ) )
{
std::istringstream iss2( line );
std::string item;
while ( std::getline( iss2, item, ',' ) )
{
std::cout << item << ' ';
}
std::cout << '\n';
}
}
The program output is
abc def 0.9
ghi jkl 109
mno par 155
To get double values you can use standard function std:stod
.
Upvotes: 2
Reputation: 12849
There is probably a library that can do this too. Here is an approach that uses a vector of string_view
#include <iostream>
#include <string>
#include <string_view>
#include <vector>
#include <set>
auto get_substring_views(const std::string& input, const std::set<char>&& delimiters)
{
std::vector<std::string_view> views;
auto begin_of_word = input.begin();
auto end_of_word = begin_of_word;
while (end_of_word != input.end())
{
// check if input character can be found in delimiter
while (end_of_word != input.end() && (delimiters.find(*end_of_word) == delimiters.end())) ++end_of_word;
// double delimiter will result in empty view being added (should be fine)
views.emplace_back(begin_of_word, end_of_word);
// next word starts one after delimiter
if ( end_of_word != input.end() ) begin_of_word = ++end_of_word;
}
return views;
}
int main()
{
std::string input{ "abc,def,0.9;ghi,jkl,109;mno,par,155" };
// note will return views on input so input must stay in scope
auto substrings = get_substring_views(input, { ',',';' });
for (const auto& substring : substrings)
{
std::cout << substring << "\n";
}
return 0;
}
Upvotes: 0