【问题标题】:Extract multiple words to one string variable将多个单词提取到一个字符串变量
【发布时间】:2010-01-22 17:19:04
【问题描述】:
std::stringstream convertor("Tom Scott 25");
std::string name;   
int age;

convertor >> name >> age;

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

我想将两个或多个单词提取到一个字符串变量中。到目前为止,我已经阅读,似乎这是不可能的。如果是这样,还有什么办法呢?我希望name 获取数字(年龄)之前的所有字符。

我觉得使用 sscanf 最舒服,但我显然不能。

例如,我需要能够提取age 之前的所有单词。

【问题讨论】:

    标签: c++ string stringstream


    【解决方案1】:

    到目前为止发布的大多数解决方案并不真正符合规范——直到年龄的所有数据都被视为名称。例如,他们会以“Richard Van De Rothstyne”之类的名字失败。

    正如 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,否则可能是 @987654321 @)。

    【讨论】:

    • 我已经说过我不能使用sscanf,因为它不支持std::string,到目前为止我已经阅读了。所以在sscanf(temp.c_str(), "%[^0123456789] %d", name, &age); 中,name 变量不能是std::string
    • @Balon:没错——你必须分配一个临时缓冲区,读入它,然后从缓冲区创建一个字符串。或者,您可以使用带有 scanf 的字符串缓冲区,但这有点棘手。
    【解决方案2】:

    这有什么问题?

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

    【讨论】:

    • 因为这只是一个例子。当我需要提取未知数量的单词时,我会遇到一些情况。
    • I'd like to extract two words or more to one string variable. 无论如何,我的错误:) 我可以更好地写我的问题。
    • @Neil,他确实明确表示他需要阅读直到年龄的所有内容,不一定只有两个字。
    【解决方案3】:

    这是怎么回事?

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

    【讨论】:

    • 我喜欢你的第一句话。 :-)
    【解决方案4】:

    您可以实现的通用算法:

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

    【讨论】:

      【解决方案5】:

      一种方法是使用重载运算符创建一个新类>>

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

      【讨论】:

      • 这有同样的问题——它仍然只读取两个单词。顺便说一句,它还有一个小错误/错字(您从 istream 中提取,而不是从 ostream 中提取)。
      【解决方案6】:

      这是矫枉过正的方式(使用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;
      }
      

      输出:

      姓名:Buzz Killington 爵士,先生。

      年龄:25

      说真的,如果您经常在程序中进行重要的输入解析,请考虑使用 parsingregular expressions 库。

      【讨论】:

        【解决方案7】:

        这是我刚做的作业。 但是 int 或 double 类型必须放在字符串的前面。因此,您可以阅读不同大小的多个单词。 希望对您有所帮助。

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

        【讨论】:

          【解决方案8】:

          这是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
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2021-05-07
            • 2016-09-10
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-05-08
            相关资源
            最近更新 更多