Pregunta

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

convertor >> name >> age;

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

Me gustaría extraer dos o más palabras a una variable de cadena. Hasta ahora he leído, parece que no es posible. Si es así, ¿cómo hacerlo? Me gustaría name para obtener todos los caracteres antes del número (la edad).

Me sentiría más cómodo usando sscanf, pero, obviamente, no puede.

Lo que necesito es la capacidad de extraer todas las palabras antes de age por ejemplo.

¿Fue útil?

Solución

La mayoría de las soluciones publicado hasta ahora en realidad no cumplir con las especificaciones - que todos los datos hasta la edad ser tratados como el nombre. Por ejemplo, podrían fallar con un nombre como "Richard Van De Rothstyne".

Como señaló el PO, con scanf que podría hacer algo como: scanf("%[^0-9] %d", name, &age);, y sería leer esto muy bien. Asumiendo que esto se orienta la línea de entrada, yo tiendo a hacer eso de todos modos:

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

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

Por desgracia, iostreams no proporcionan un análogo directo a una conversión scanset así - getline puede leer hasta un delimitador, pero sólo se puede especificar un carácter como delimitador. Si realmente no puede usar scanf y compañía, la siguiente parada sería o bien el código a mano (el comienzo de la edad sería temp.find_first_of("0123456789");) o utilizar un paquete de RE (TR1 si su compilador suministros, de lo contrario, probablemente Boost ).

Otros consejos

¿Qué hay de malo en esto?

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

convertor >> firstname >> surname >> age;
std::string name = firstname + " " + surname;

¿Qué hay de malo en esto?

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


std::string first, last;
int age;

convertor >> first >> last >> age

Si realmente desea leer primero y el último de una sola vez, algo como esto funcionará

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; 

Un ejemplo más genérica en la que quería leer N palabras podría funcionar así:

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

algoritmo general que se puede aplicar:

read word into name
loop
   try reading integer
   if success then break loop
   else
      clear error flag
      read word and attach to name 

Un enfoque sería hacer una nueva clase con un operador sobrecargado >>

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

Esta es la forma exageración (usando 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;
}

Salida:

  

Nombre:. Sir zumbido Killington, esq

     

Edad: 25

En serio, si usted a menudo hace el análisis sintáctico de entrada no trivial en su programa, considere el uso de un análisis sintáctico o expresiones regulares biblioteca.

Esta es una tarea que acabo de hacer. Pero int o tipos dobles tienen que colocarse delante de la la cadena. para ello se puede leer varias palabras en diferentes tamaños. Espero que esto le puede ayudar un poco.

string words;
sin>>day>>month>>year;
sin>>words;
watch = words;
while(sin>>words)
{
watch += " "+words;
}

Aquí se trata de una solución con std::regex (cualquier número de nombres):

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

Prueba:

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

Salida:

Jonathan Vincent Voight - 76
Donald McNichol Sutherland - 79
Scarlett Johansson - 30
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top