Extract multiple words to one string variable
std::stringstream convertor("Tom Scott 25");
std::string name;
int age;
convertor >> name >> age;
if(convertor.fail())
{
// it fails of course
}
开发者_运维知识库I'd like to extract two or more words to one string variable. So far I've read, it seems that it is not possible. If so, how else to do it? I'd like name
to get all characters before number (the age).
I'd feel most comfortable using sscanf, but I obviously can't.
What I need is ability to extract all words before age
for example.
Most of the solutions posted so far don't really meet the specification -- that all the data up to the age be treated as the name. For example, they would fail with a name like "Richard Van De Rothstyne".
As the OP noted, with scanf you could do something like: scanf("%[^0-9] %d", name, &age);
, and it would read this just fine. Assuming this is line oriented input, I'd tend to do that anyway:
std::string temp;
std::getline(infile, temp);
// technically "[^0-9]" isn't required to work right...
sscanf(temp.c_str(), "%[^0123456789] %d", name, &age);
Unfortunately, iostreams don't provide a direct analog to a scanset conversion like that -- getline can read up to a delimiter, but you can only specify one character as the delimiter. If you really can't use scanf and company, the next stop would be either code it by hand (the beginning of the age would be temp.find_first_of("0123456789");
) or use an RE package (TR1 if your compiler supplies it, otherwise probably Boost).
What’s wrong with this?
std::stringstream convertor("Tom Scott 25");
std::string firstname;
std::string surname;
int age;
convertor >> firstname >> surname >> age;
std::string name = firstname + " " + surname;
What's wrong with this?
std::stringstream convertor("Tom Scott 25");
std::string first, last;
int age;
convertor >> first >> last >> age
If you really want to read first and last in one go, something like this will work
class Name {
std::string first, last;
public:
std::istream& read(std::istream& in) {
return in >> first >> last;
}
operator std::string() const { return first + " " + last; }
};
std::istream& operator>>(std::istream& in, Name& name) {
return name.read(in);
}
/* ... */
Name name;
int age;
converter >> name >> age;
std::cout << (std::string)name;
A more generic example where you wanted to read N words could function like this:
class Reader {
int numWords;
std::vector<std::string> words;
// ...
std::istream& read(std::istream& in) {
std::vector<std::string> tmp;
std::string word;
for (int i = 0; i < numWords; ++i) {
if (!in >> word)
return in;
tmp.push_back(word);
}
// don't overwrite current words until success
words = tmp;
return in;
}
General algorithm that you could implement:
read word into name
loop
try reading integer
if success then break loop
else
clear error flag
read word and attach to name
One approach would be to make a new class with an overloaded operator>>
class TwoWordString {
public:
std::string str;
};
istream& operator>>(istream& os; TwoWordString& tws) {
std::string s1, s2;
os >> s1;
os >> s2;
tws.str = s1 + s2;
return os;
}
Here's the overkill way (using Boost.Spirit) >:D
#include <iostream>
#include <string>
#include <boost/format.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
int main()
{
namespace qi = boost::spirit::qi;
namespace phoenix = boost::phoenix;
namespace ascii = boost::spirit::ascii;
using ascii::char_; using ascii::digit; using ascii::blank;
using qi::_1; using qi::int_; using phoenix::ref; using phoenix::at_c;
std::string input("Sir Buzz Killington, esq. 25");
std::string name;
int age = 0;
qi::rule<std::string::const_iterator, std::string()> nameRule;
nameRule %= (+(char_ - digit - blank));
std::string::const_iterator begin = input.begin();
std::string::const_iterator end = input.end();
qi::parse(begin, end,
(
nameRule[ref(name) += _1]
>> *( ((+blank) >> nameRule)[ref(name) += ' ']
[ref(name) += at_c<1>(_1)] )
>> *blank
>> int_[ref(age) = _1]
)
);
std::cout << boost::format("Name: %1%\nAge: %2%\n") % name % age;
return 0;
}
Output:
Name: Sir Buzz Killington, esq.
Age: 25
Seriously though, if you often do non-trivial input parsing in your program, consider using a parsing or regular expressions library.
This is an homework i just did. But int or double types have to placed in front of the the string. therefor you can read multiple words at different size. Hope this can help you a little bit.
string words;
sin>>day>>month>>year;
sin>>words;
watch = words;
while(sin>>words)
{
watch += " "+words;
}
Here it is an solution with std::regex
(any number of names):
auto extractNameAndAge(std::string const &s) -> std::tuple<std::string, int> {
using namespace std::string_literals;
static auto const r = std::regex{"(.*)\\s+(\\d+)\\s*$"s};
auto match = std::smatch{};
auto const matched = std::regex_search(s, match, r);
if (!matched)
throw std::invalid_argument{"Invalid input string \""s + s +
"\" in extractNameAndAge"s};
return std::make_tuple(match[1], std::stoi(match[2]));
}
Test:
auto main() -> int {
using namespace std::string_literals;
auto lines = std::vector<std::string>{"Jonathan Vincent Voight 76"s,
"Donald McNichol Sutherland 79"s,
"Scarlett Johansson 30"s};
auto name = ""s;
auto age = 0;
for (auto cosnt &line : lines) {
std::tie(name, age) = extractNameAndAge(line);
cout << name << " - " << age << endl;
}
}
Output:
Jonathan Vincent Voight - 76
Donald McNichol Sutherland - 79
Scarlett Johansson - 30
精彩评论