从头开始学习C++——输入和输出

210 阅读3分钟

TLDR;这是C++从头开始系列的一部分。这一部分是关于如何编写一个可以读写控制台的程序。

参考资料

这里有一些有用的链接来了解更多。

为什么是控制台程序

控制台程序是在控制台工作的程序,它们没有用户界面,但它们仍然被大量使用,而且很有用。你说没有用户界面,这有什么好的?嗯,控制台程序很快,真的很快。此外,如果你需要自动发送args并监听输入,作为构建步骤的一部分,例如,如果你正在实践CI/CD,那么它们更容易编写脚本,并在构建服务器的背景下使用。

写入控制台

好了,我们知道为什么控制台程序有时是个好主意。我如何向控制台输出呢?有很多库可以为你做到这一点,但有两个常见的选择。

  • iostream,基于流的库,被认为更像C++。
  • cstdio,更像C语言的格式化。它的功能与iostream相似,但有人说它有一些问题,比如不能正确地检查插值,而且iostream在未来更安全。

你的第一个输出

要创建一个输出到控制台的程序,你需要iostream ,你需要cout ,像这样。

#include <iostream>

using namespace std;

int main() 
{
  cout << "Print this";
}

注意你是如何使用cout 和流操作符的,该操作符采用了<< 操作符,这意味着它写到了一个流,这个流将被送到控制台。

混合类型

好的,所以你很可能想结合数字和字符串,比如说总和是8。答案是使用流操作符<< ,像这样分开两种不同的数据类型。

#include <iostream>

using namespace std;

int main() 
{
  int sum = 8; 
  cout << "The sum is " << sum;
}

这是唯一的方法吗?如果我想写 "这个程序已经运行了8天 "这样的东西,显然是字符串、int和字符串的混合,我必须多次使用<< ?有几种不同的选择,如下面的代码所示。

#include <iostream>
#include <cstdio>

using namespace std;

int main()
{
  int sum = 8;
  cout << "The sum is " << sum << "\n";
  cout << "This program has been running for " + to_string(sum) + "days";
  printf("The program has been running for %d days", sum);
  cout << format("The program has been running for {0} days", to_string(days));
}
  • << "string" << number << "string".这是你到目前为止看到的版本,用<< ,将不同的数据类型分开。
  • "string" + to_string(number).在这个版本中,你通过使用函数to_string() ,首先转换你的数字来连接字符串。
  • printf.在这里,你使用库cstdio 一个c风格的库,它可以插值,允许你通过使用占位符{0}来混合字符串和数字。0说的是它作为参数的位置,例如。
   printf("Here's a number {0}, and another one", 3, 5);
  • format().这里我们使用的是C++20中引入的一个函数。请确保你已经升级了C++,以便能够使用这个函数。

格式化的输出

有一个库iomanip ,使你能够格式化输出,用它你可以做一些事情,例如。

  • 设置对齐方式,你可以决定你打印的内容是向左还是向右对齐
  • 设置宽度,你可以让你打印的内容占用N个字符,这样你就可以像表格一样打印输出。

下面是一个使用许多iomanips 功能的例子程序。

#include <iostream>
#include <iomanip>
using namespace std;

int main()
{
  cout << setw(10) <<  "row1col1" << setw(20) << "row1col2"  << "\n";
  cout << setw(10) <<  "row2col1" << setw(20) << "row2col2"  << "\n";
}

我们有setw() ,设置宽度,用来创建两个不同的列。如果你要运行这个,你会得到以下输出。

  row1col1            row1col2
  row2col1            row2col2

还有更多的功能,请看文章顶部的 "参考文献 "部分。

从控制台读取信息

为了从控制台读取数据,我们可以使用cin ,从iostream 。你可以这样使用它。

#include <iostream>
#include <string>
using namespace std;

int main () 
{
  string name;
  cout << "what's your name? ";
  cin >> name;

  cout << "Hi " << name << endl;
  return 0;
}

在这里,cin >> name ,等待用户输入一个字符串,然后再输入一个返回键。然后,该值将被存储在name
最后我们键入该值。要尝试这个演示,请采取以下步骤。

  1. 将上述代码保存在一个文件app.cpp
  2. g++ app.cpp编译该程序
  3. 运行该程序。
   ./a.out # macOS, Linux
   a.out.exe # windows 

你应该看到程序像这样运行。

   what's your name? chris
   Hi chris

多重输入

想象一下,你有一个需要收集多个输入的情况。在下面的程序中,我们有一个计算程序。

#include <iostream>
#include <string>
using namespace std;

int main()
{
  int no, no2;
  cout << "--Add two numbers--\n";
  cout << "First number: ";
  cin >> no;
  cout << "Second number: ";
  cin >> no2;

  cout << "The sum is: " << no + no2 << "\n";
  return 0;
}

在上面的代码中,你要求一个数字,然后是下一个。但如果你想在一行中收集所有的东西呢?我们可以这样做,只要把代码改成下面的样子。

#include <iostream>
#include <string>
using namespace std;

int main()
{
  int no, no2;
  cout << "--Add two numbers--\n";
  cout << "Input numbers (separated by space): ";
  cin >> no >> no2;
  cout << "The sum is: " << no + no2 << "\n";
  return 0;
}

注意这一行cin >> no >> no2; ,你用>> 来分隔用户的输入。我们这样做的原因是,cin 只取第一个值,直到第一个空格。

我们可以使用的另一种方法是getline() ,读取整行,就像下面的代码。

#include <iostream>
#include <string>
using namespace std;

int main()
{
  int sum;
  string s_numbers;
  cout << "--Add numbers--\n";
  cout << "Input numbers (separated by space): ";
  getline(cin, s_numbers);
  // add code to split and convert numbers to int
  cout << "The sum is: " << sum << "\n";
  return 0;
}

getline() 读取整行,不管你是否使用空格字符。然而,现在你遇到的情况是,你需要解析 ,因为该行是以字符串形式存储的。如何解决?s_numbers

首先我们需要一个分割函数。

vector<int> split(const string line, char delimiter) 
{
  stringstream ss(line);
  string item;
  vector<int> numbers;
  while(getline(ss, item, delimiter)) 
  {
    numbers.push_back(stoi(item));
  }
  return numbers;
}

我们要做的第一件事是把来自用户输入的line ,读到一个stringstream 对象中。想象一下,用户的输入是这样的:"1 2 3 4"。

现在我们使用while结构在字符串流对象上进行迭代。这使我们能够读取用户字符串中的每一个子串,所以每次迭代都会得到 "1""2""3""4"。然而,我们需要将其转换为一个int,我们用stoi() ,它将一个字符串转换成一个int。此外,我们需要将每个值添加到vector 类型的numbers (向量是一个动态列表,可以接受任何数量的项目)。

好了,我们解决了一半的问题。我们现在需要对向量中的值进行总结,像这样。

int sumNumbers(vector<int> v) 
{
  int sum = 0;
  for(int i=0; i< v.size(); i++) 
  {
    sum += v[i];
  }
  return sum;
}

很好,所以我们有东西在向量中循环,最后得到一个整数。现在来看看完整的程序。

#include <iostream>
#include <string>
#include <vector>
#include <sstream>

using namespace std;

vector<int> split(const string line, char delimiter) 
{
  stringstream ss(line);
  string item;
  vector<int> numbers;
  while(getline(ss, item, delimiter)) 
  {
    numbers.push_back(stoi(item));
  }
  return numbers;
}

int sumNumbers(vector<int> v) 
{
  int sum = 0;
  for(int i=0; i< v.size(); i++) 
  {
    sum += v[i];
  }
  return sum;
}

int main()
{
  string s_numbers;
  cout << "--Add numbers--\n";
  cout << "Input numbers (separated by space): ";
  getline(cin, s_numbers);
  vector<int> numbers = split( s_numbers, ' ' );
  cout << "Sum is: " << sumNumbers( numbers ) << "\n";
}

你通过cingetline ,正如你所看到的,如果知道输入的数量,有时只用cin 和一些运算符>> ,会更容易。然而,如果我们需要用户写一个未知数量的输入,我们需要写更多的代码,并使用像stringstream和vector这样的结构来处理它。

小结

这篇文章就写到这里。使用iostream和iomanip的输入和输出可以让你走得更远,而使用stringstream和vector,你可以走得更远,也可以创建灵活的程序。