Извлечь несколько слов в одну строковую переменную - PullRequest
2 голосов
/ 22 января 2010
std::stringstream convertor("Tom Scott 25");
std::string name;   
int age;

convertor >> name >> age;

if(convertor.fail())
{
    // it fails of course
}

Я хотел бы извлечь два или более слов в одну строковую переменную. Пока я прочитал, кажется, что это невозможно. Если так, как еще это сделать? Я бы хотел name, чтобы получить все символы до числа (возраст).

Я бы чувствовал себя наиболее комфортно при использовании sscanf, но, очевидно, не могу.

Мне нужна возможность извлечь все слова, например, до age.

Ответы [ 8 ]

3 голосов
/ 22 января 2010

Большинство решений, опубликованных до сих пор, на самом деле не соответствуют спецификации - что все данные до возраста должны рассматриваться как имя. Например, они потерпят неудачу с таким именем, как «Ричард Ван Де Ротстайн».

Как отмечалось в OP, с scanf вы можете сделать что-то вроде: scanf("%[^0-9] %d", name, &age);, и это будет прекрасно читать. Предполагая, что это линейно-ориентированный ввод, я бы все равно сделал это:

std::string temp;
std::getline(infile, temp);

// technically "[^0-9]" isn't required to work right...
sscanf(temp.c_str(), "%[^0123456789] %d", name, &age);

К сожалению, iostreams не предоставляют прямого аналога преобразования набора сканирования, подобного этому - getline может считывать до разделителя, но вы можете указать только один символ в качестве разделителя. Если вы действительно не можете использовать scanf и company, следующей остановкой будет либо кодирование вручную (начало эры будет temp.find_first_of("0123456789");), либо использование пакета RE (TR1, если ваш компилятор его предоставит, в противном случае, вероятно, Повышение ).

3 голосов
/ 22 января 2010

Что с этим не так?

std::stringstream convertor("Tom Scott 25");
std::string firstname;   
std::string surname;
int age;

convertor >> firstname >> surname >> age;
std::string name = firstname + " " + surname;
2 голосов
/ 22 января 2010

Общий алгоритм, который вы можете реализовать:

read word into name
loop
   try reading integer
   if success then break loop
   else
      clear error flag
      read word and attach to name 
2 голосов
/ 22 января 2010

Что с этим не так?

std::stringstream convertor("Tom Scott 25");


std::string first, last;
int age;

convertor >> first >> last >> age

Если вы действительно хотите читать первым и последним за один раз, что-то вроде этого будет работать

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; 

Более общий пример, в котором вы хотите прочитать N слов, может работать следующим образом:

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;
}
1 голос
/ 22 января 2010

Один из подходов - создать новый класс с перегруженным оператором >>

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;
}
0 голосов
/ 28 июня 2015

Вот решение с std::regex (любое количество имен):

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]));
}

Тест:

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;
  }
}

Выход:

Jonathan Vincent Voight - 76
Donald McNichol Sutherland - 79
Scarlett Johansson - 30
0 голосов
/ 28 июня 2015

Это домашняя работа, которую я только что сделал. Но int или double должны быть помещены перед строкой. для этого вы можете прочитать несколько слов разного размера. Надеюсь, это поможет вам немного.

string words;
sin>>day>>month>>year;
sin>>words;
watch = words;
while(sin>>words)
{
watch += " "+words;
}
0 голосов
/ 23 января 2010

Вот способ избыточного убийства (используя 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;
}

Выход:

Имя: сэр Базз Киллингтон, эсквайр.

Возраст: 25

Если серьезно, если вы часто выполняете нетривиальный синтаксический анализ входных данных в вашей программе, попробуйте использовать синтаксический анализ или регулярных выражений library.

...