【问题标题】:How to read a complex input with istream&, string& and getline in c++?如何在 c++ 中使用 istream&、string& 和 getline 读取复杂输入?
【发布时间】:2019-04-22 01:54:48
【问题描述】:

我对 C++ 很陌生,所以如果这不是一个好问题,我深表歉意,但我确实需要帮助以了解如何使用 istream。

我必须创建一个项目,它需要多个输入,可以在一行或多个上,然后将其传递给向量(这只是项目的一部分,我想尝试其余的我自己的),例如,如果我要输入这个......

>> aaa    bb
>> ccccc
>> ddd fff  eeeee

用“aaa”、“bb”、“ccccc”、“ddd”、“fff”、“eeee”创建一个字符串向量

输入可以是字符或字符串,当按下返回键时程序停止请求输入。

我知道 getline() 获取一行输入,我可能会使用一个 while 循环来尝试获取输入,例如...(如果我错了,请纠正我)

while(!string.empty())
     getline(cin, string);

但是,我并不真正了解 istream,而且我的类没有遍历指针也无济于事,所以我不知道如何使用 istream& 或 string& 并将其传递到向量中。在项目描述中,它说不要使用 stringstream,而是使用 getline(istream&, string&) 中的功能。谁能详细解释一下如何使用 getline(istream&, string&) 制作函数,然后如何在主函数中使用它?

任何一点帮助!

【问题讨论】:

  • 解决方案将取决于 aaa bb ccccc ddd fff eeeee 被允许是什么。您必须具体了解您的约束/要求。
  • 实际上公平地说,如果您被禁止使用流格式的 I/O,那么无论如何您都必须自己进行解析,所以这并不重要。事实上,如果没有细节,我们无法真正回答这个问题。
  • 不太确定您是在问如何解析数据或如何使用getline 或如何制作自己的getline(采用istream&, string& 的函数已经存在!)
  • @LightnessRacesinOrbit "如果你被禁止使用流格式的 I/O" 我在 q 中找不到那部分?
  • 你怎么知道用户已经完成了输入?是否有固定数量的要读取的字符串?还是一些结束输入的魔法值?

标签: c++


【解决方案1】:

您已经走在正确的道路上;单独地,您必须用一些虚拟对象预先填充字符串才能完全进入 while 循环。更优雅:

std::string line;
do
{
    std::getline(std::cin, line);
}
while(!line.empty());

如果用户输入一个空行,这应该已经完成​​了逐行读取(但可能一行中有多个单词!)并退出的技巧(请注意,空格后跟换行符不会被识别为这样!) .

但是,如果流中出现任何问题,您将陷入无限循环中,一次又一次地处理先前的输入。所以最好也检查一下流状态:

if(!std::getline(std::cin, line))
{
    // this is some sample error handling - do whatever you consider appropriate...
    std::cerr << "error reading from console" << std::endl;
    return -1;
}

由于一行中可能有多个单词,因此您必须将它们拆分。 有几种方法可以做到这一点,一个很简单的方法是使用std::istringstream - 您会发现它与您可能习惯使用的std::cin 相似:

    std::istringstream s(line);
    std::string word;
    while(s >> word)
    {
        // append to vector...
    }

请注意,使用operator&gt;&gt; 会忽略前导空格并在第一个尾随空格(或流的结尾,如果到达)后停止,因此您不必显式处理。

好的,你不能使用std::stringstream(好吧,我使用了std::istringstream,但我想这个小区别不算数,不是吗?) .变化有点重要,它变得更复杂,另一方面,我们可以自己决定什么是单词,什么是分隔符......我们可以将标点符号视为分隔符,就像空格一样,但允许数字成为单词的一部分,所以我们会接受 e。 G。 ab.7c d"ab", "7c", "d"

auto begin = line.begin();
auto end = begin;
while(end != line.end()) // iterate over each character
{
    if(std::isalnum(static_cast<unsigned char>(*end)))
    {
        // we are inside a word; don't touch begin to remember where
        // the word started
        ++end;
    }
    else
    {
        // non-alpha-numeric character! 
        if(end != begin)
        {
            // we discovered a word already
            // (i. e. we did not move begin together with end)
            words.emplace_back(begin, end);
            // ('words' being your std::vector<std::string> to place the input into) 
        }
        ++end;
        begin = end; // skip whatever we had already
    }
}
// corner case: a line might end with a word NOT followed by whitespace
// this isn't covered within the loop, so we need to add another check:
if(end != begin)
{
    words.emplace_back(begin, end);
}

调整对什么是分隔符和什么是单词的不同解释应该不难(例如,std::isalpha(...) || *end == '_' 将下划线检测为单词的一部分,但数字不是)。有很多 helper functions 你可能会觉得有用...

【讨论】:

  • 我基本了解您的解决方案,但是,我以前从未见过 emplace 并且它似乎不适用于字符串。更不用说,现在当我输入一个输入时,我可能会陷入无限循环,因为在我显示一条消息并要求输入后程序什么也不做
  • emplace_back 自 C++11 以来就存在(如果您的编译器已经有点老化,您可能必须显式启用它)并且基本上使用提供的参数进行了新的放置,即。 e.它会根据您的需要跳过使用 words.push_back(std::string(begin, end)); 创建中间对象 - 这将是 C++11 之前的变体;此处显示的相同 std::string constructor(第 6 个)也将在 emplace_back 中使用。
  • '在 [...]' 之后什么都不做 - 这不是真的(前提是您集成了我的答案中的代码)。它将单词放在一个向量中,仅从外部是不可见的。在外部 do while 循环终止后,您可能会将向量中的所有单词打印到控制台,然后您会再次看到它们。如果您想立即对输入做出反应(不必先输入空行),您也可以在识别单词后立即将单词打印到控制台。
  • 给你一个很好的提示:不要只是复制和粘贴我的解决方案(我个人不在乎你是否这样做......),你不会从中学到任何东西。先理解它,然后自己重​​写。请注意,您不需要像我一样需要使用迭代器(当然可以),还有很多其他选项(例如使用索引而不是迭代器)。即使是基于范围的 for 循环也是可能的(for(auto c : line)),然后您可以将字符附加到中间字符串对象,如果在单词中,并且在分隔符上,将中间值推送到向量(不要 get 清除单词然后)。
  • 哦(重新阅读您的评论),只是为了明确:words 是一个std::vector&lt;std::string&gt;,需要在最外面的 do-while 循环之前定义!我认为从问题中可以清楚地看到这一点('[...] 然后将其传递给向量 [...]') - 抱歉混淆 - 在答案中添加了评论。
【解决方案2】:

您可以输入第一列的值,然后根据该值调用函数:

void Process_Value_1(std::istream& input, std::string& value);
void Process_Value_2(std::istream& input, std::string& value);

int main()
{
  // ...
  std::string first_value;
  while (input_file >> first_value)
  {
     if (first_value == "aaa")
     {
         Process_Value_1(input_file, first_value);
     }
     else if (first_value = "ccc")
     {
         Process_Value_2(input_file, first_value);
     }
     //...
  }
  return 0;
}

示例函数可以是:

void Process_Value_1(std::istream& input, std::string& value)
{
  std::string b;
  input >> b;
  std::cout << value << "\t" << b << endl;
  input.ignore(1000, '\n'); // Ignore until newline.
}

还有其他方法可以执行该过程,例如使用函数指针表和std::map

【讨论】:

  • 这看起来很像特定输入的解析器 - 但是,如果我理解正确的问题,它是关于读取分布在任意行数上的任意单词,并且只提取这些单词中的每一个,而不应用任何对他们来说意义...
猜你喜欢
  • 2023-03-24
  • 1970-01-01
  • 2015-06-01
  • 2015-06-11
  • 1970-01-01
  • 2017-10-12
  • 1970-01-01
  • 2017-09-12
相关资源
最近更新 更多