为什么 std::getline() 在格式化提取后跳过输入?

新手上路,请多包涵

我有以下代码提示用户输入猫的年龄和名称:

 #include <iostream>
#include <string>

int main()
{
    int age;
    std::string name;

    std::cin >> age;
    std::getline(std::cin, name);

    if (std::cin)
    {
        std::cout << "My cat is " << age << " years old and their name is " << name << std::endl;
    }
}

我发现已经成功读取了年龄,但没有读取名称。这是输入和输出:

 Input:

"10"
"Mr. Whiskers"

Output:

"My cat is 10 years old and their name is "

为什么输出中省略了名称?我已经给出了正确的输入,但是代码以某种方式忽略了它。为什么会这样?

原文由 David G 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 386
1 个回答

为什么会这样?

这与您自己提供的输入无关,而是与默认行为 std::getline() 有关系。当您提供年龄输入时( std::cin >> age ),您不仅提交了以下字符,而且当您输入 Enter 时,还会在流中附加一个隐式换行符:

>  "10\n"
>
> ```

当您从终端提交时选择 `Enter` 或 `Return` 时,总是会在您的输入中附加一个换行符。它也用于文件中以移动到下一行。提取到 `age` 后,换行符留在缓冲区中,直到下一次 I/O 操作被丢弃或读取。当控制流到达 `std::getline()` 时,会看到 `"\nMr. Whiskers"` 并且开头的换行符会被丢弃,但输入操作会立即停止。发生这种情况的原因是因为 `std::getline()` 的工作是尝试读取字符并在找到换行符时停止。所以你输入的其余部分留在缓冲区中未读。

# 解决方案

## `cin.ignore()`

要解决此问题,一种选择是在执行 `std::getline()` 之前跳过换行符。您可以在第一次输入操作后调用 `std::cin.ignore()` 来执行此操作。它将丢弃下一个字符(换行符),使其不再碍事。

std::cin >> age; std::cin.ignore(); std::getline(std::cin, name);

assert(std::cin); // Success!


## `std::ws`

丢弃空格的另一种方法是使用 `std::ws` 函数,该函数是一个操纵器,旨在从输入流的开头提取和丢弃前导空格:

std::cin >> age; std::getline(std::cin >> std::ws, name);

assert(std::cin); // Success!


`std::cin >> std::ws` 表达式在 `std::getline()` 调用之前(以及在 `std::cin >> age` 调用之后)执行,以便删除换行符。

不同之处在于 `ignore()` 仅丢弃 1 个字符(或在给定参数时丢弃 N 个字符),而 `std::ws` 继续忽略空格,直到找到非空格字符。因此,如果您不知道下一个令牌之前会有多少空格,您应该考虑使用它。

## 匹配操作

当您遇到这样的问题时,通常是因为您将格式化的输入操作与未格式化的输入操作相结合。格式化输入操作是指您接受输入并将其格式化为某种类型。这就是 `operator>>()` 的用途。未格式化的输入操作是除此之外的任何其他操作,例如 `std::getline()` , `std::cin.read()` , `std::cin.get()` 等这些函数只关心输入和格式处理原始文本。

如果您坚持使用单一类型的格式,那么您可以避免这个烦人的问题:

// Unformatted I/O std::string age, name; std::getline(std::cin, age); std::getline(std::cin, name);


或者

// Formatted I/O int age; std::string firstName, lastName; std::cin >> age >> firstName >> lastName;

”`

如果您选择使用未格式化的操作将所有内容读取为字符串,您可以在之后将它们转换为适当的类型。

原文由 David G 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题