【问题标题】:How to properly terminate input如何正确终止输入
【发布时间】:2020-10-09 07:20:23
【问题描述】:

目前正在研究 Stroustrup 的 C++ 简介中的一个练习,它要求:编写一个函数,给定两个向量 price 和 weight 计算一个值(一个“索引”),它是所有 price[i]*weight[ 的总和一世]。确保有 weight.size()==price.size()

vector<double> weight;
vector<double> price;

void read_weight() {
  cout << "Input weight" << '\n';

  for (double weights; cin >> weights; ) {
    weight.push_back(weights);

    if (!(cin >> weights)) {
      cin.clear();
      cin.ignore(numeric_limits<double>::max(), '\n' );
      break;
    }
  }
}

void read_price() {
  cout << "Input price" << '\n';

  for (double prices; cin >> prices; ) {
    price.push_back(prices);

    if (!( cin >> prices)) {
      cin.clear();
      cin.ignore( numeric_limits<double>::max(), '\n' );
      break;
    }
  }
}

void calculate() {
  double sum = 0;
  for (int i = 0; i < price.size(); ++i)
    sum += price[i] * weight[i];

  cout << "Sum is " << sum << '\n';
}


int main() {
  read_weight();
  read_price();
  calculate();
}

这是我目前拥有的代码,但我无法弄清楚如何正确终止输入。在前面的章节中,他的书提到您可以使用除 double 之外的输入来终止(如 |)。但是,我了解到这只会使 cin 进入失败状态,并且不允许我进入价格函数。不幸的是,这本书还没有介绍如何处理这个问题,所以我只是复制了关于 cin.clear 和 cin.ignore 的代码,希望它能起作用。但这无济于事。我注意到如果我要将向量更改为 int 而不是 double 并且对行为的差异感到困惑,我实际上被允许使用单个输入。我想知道是否有人可以给我有关如何解决此问题的提示?

【问题讨论】:

  • 旁白:避免使用全局变量。

标签: c++ c++11 vector input cin


【解决方案1】:

您可以使用 std::getline 将整行从 cin 读入字符串。此后,您需要确定输入的行是否为双精度。我通常不建议使用正则表达式过于疯狂(我在 C++ 中不经常使用它们),但在这种情况下,我认为这是一个非常简单和合理的解决方案。如果字符串的格式为“32.23”(数字点数字),则使用 std::stod 将其转换为双精度,将其推送到向量并继续从 cin 读取。如果不是,则中断循环并继续程序流程。

远离使用全局变量,使用局部变量并传递它们。

另外,请注意您的函数 read_price 和 read_weight 几乎相同。在这种情况下,您肯定只想编写一个(参数化的)函数。在这种情况下,你甚至不需要参数,你可以对两者使用相同的函数。

(您也可以直接从流(std::cin)中将您的值读入双变量,这可能会被认为更优雅,因为您需要较少的转换,但是下面的方法很简单,您不必担心std::cin 中输入了什么)

#include <vector>
#include <string>
#include <iostream>
#include <regex>


std::vector<double> get_doubles_from_cin(){
    std::vector<double> doubles;
    std::regex double_regex ("\\d+\\.\\d+");
    std::string input;
    while(std::getline(std::cin, input)){
       if (std::regex_match(input, double_regex)){
           doubles.push_back(std::stod(input));
       }
       else{
           break;
       }
   }
    return doubles;
}


void calculate(std::vector<double>& weights, std::vector<double>& prices) {
  double sum = 0;
  for (int i = 0; i < prices.size(); ++i) {
      sum += weights[i] * prices[i];
  }
  std::cout << "Sum is " << sum << '\n';
}


int main() {
    std::cout << "Enter weights" << std::endl;
    auto weights = get_doubles_from_cin();
    std::cout << "Enter prices" << std::endl;
    auto prices = get_doubles_from_cin();
    calculate(weights, prices);
}

【讨论】:

  • while(true){ std::getline(std::cin, input); 有这么多赞?永远不应该使用它。始终使用while(std::getline(std::cin, input))
  • 是的,好多了,感谢您的更正。我已经相应地编辑了我的答案。但是,由于我只是一个新手,我只能假设使这是唯一可接受的编写方式的原因(除了优雅),您介意解释一下优点吗?
【解决方案2】:

你已经成功了一半。我已经为read_weights() 写了一个评论替换;你应该可以从那里拿走它。

#include <vector>
#include <limits>
#include <iostream>

// Use return values instead of working on global variables.
// Avoid using global variables whenever possible, they are a 
// maintenance pain.
std::vector< double > read_weights()
{
    std::vector< double > weights;
    double weight;

    // You can embed the \n right in the string, no need to put it as a
    // separate character.
    std::cout << "Input weights; enter a non-number to terminate input.\n";

    // If anything not double is entered, cin goes into fail state --
    // that is our terminating condition right there, so use it!
    while ( std::cin >> weight )
    {
        weights.push_back( weight );
    }

    // Clean up cin
    std::cin.clear();
    // Use the correct type for max(); you had 'double' here...
    cin.ignore( numeric_limits< std::streamsize >::max(), '\n' );

    // Don't worry about the apparent copying of the vector upon return.
    // Any modern compiler should be able to optimize this away.
    return weigths;
}

一个简单的 main() 用于测试:

int main()
{
    std::vector< double > weights = read_weights();

    std::cout << "Vector contents:\n";

    for ( auto & v : weights )
    {
        std::cout << v << "\n";
    }
}

现在你只需要添加一个read_price()...现在等等,你没有,是吗?因为您实际上所做的一切与read_weights() 中的完全相同,所以输入双打!因此,将输入提示移出read_weights() 并使其成为one 函数read_values(),您调用两次,一次获取weights,一次获取prices... p>

int main()
{
    std::cout << "Enter weights; enter a non-number to terminate input.\n";
    std::vector< double > weights = read_values();
    std::cout << "Enter prices; enter a non-number to terminate input.\n";
    std::vector< double > prices = read_values();
    // ...
}

对于calculate 函数,使用参数引用,这样就不必复制向量:

void calculate( std::vector<double> & weights, std::vector<double> & prices )

一旦你让这一切运行起来,请记住,稍后,你将(或至少应该)学习&lt;algorithm&gt;、函子和 lambda...应该消除对calculate 的需求,并用优雅的单线代替它......但这还没有到来,我不想在这一点上混淆你。

【讨论】:

    猜你喜欢
    • 2021-12-12
    • 1970-01-01
    • 1970-01-01
    • 2016-05-09
    • 2013-05-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多