将多个单词提取到一个字符串变量

发布于 2024-08-19 01:05:08 字数 387 浏览 5 评论 0 原文

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

convertor >> name >> age;

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

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

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

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

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

绿光 2024-08-26 01:05:08

到目前为止发布的大多数解决方案并没有真正满足规范——所有截止日期的数据都被视为名称。例如,他们会因为“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,如果你的编译器提供它,否则可能 增强)。

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).

旧故 2024-08-26 01:05:08

这有什么问题吗?

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 firstname;   
std::string surname;
int age;

convertor >> firstname >> surname >> age;
std::string name = firstname + " " + surname;
奶茶白久 2024-08-26 01:05:08

这有什么问题吗?

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

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;
}
灯角 2024-08-26 01:05:08

您可以实现的通用算法:

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

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 
醉生梦死 2024-08-26 01:05:08

一种方法是创建一个带有重载运算符>>的新类。

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

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;
}
季末如歌 2024-08-26 01:05:08

这是过度杀伤的方法(使用 Boost.Spirit< /a>) >: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

不过说真的,如果您经常在程序中进行重要的输入解析,请考虑使用 解析正则表达式库。

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.

慈悲佛祖 2024-08-26 01:05:08

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

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

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;
}
浮云落日 2024-08-26 01:05:08

这是一个带有 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

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文