C++强大的功能来源于其丰富的类库及库函数资源。C++标准库的内容总共在50个标准头文件中定义。C++ 标准库包含输入/输出 I/O,字符串和字符处理等等。其中I/O库,就是从键盘输入数据,输出至显示器屏幕。如果想要使用某标准库(I/O库),需要引入其头文件即可,如下:
#include <iostream>
控制台输入和输出是一种简便的数据交互方式。C++语言没有专门的输入和输出语句,而是面向对象的流技术实现数据的输入和输出功能。头文件<iostream>中定义了标准输入/输出流对象,cin和cout是我们使用最多的!
cin是在标准流类中定义的关键字,用于在程序运行期间向变量输入数据。用in与提取运算符”>>”,就能实现从键盘输入数字或字符等数据。语法格式如下:
cin >> 变量名1 [ >> 变量名2 >> 变量名3 … >> 变量名n ];
// 数值类型输入
int a;
std::cin >> a; // 数据输入格式:200回车
std::cout << "a=" << a << std::endl; // 输出 a=200
输入数值之后,按下回车键,代表输入完毕,数值和回车键之间不要再输入任何字符。因为我们使用一个int整型接收键盘的输入,因此如果我们输入一个字符的话,那么它就不能被正确赋值给a,这样的话,a就是默认的0
// 多个数值类型输入
int b, c;
std::cin >> b >> c; // 使用空格键隔开两次输入
std::cout << "b=" << b << ",c=" << c << std::endl;
这里我们需要注意的是,多个数值的录入我们使用空格键隔开,当然也可以使用回车。
当我们将上面的两段代码同时执行,且在第一段代码(数值类型输入)输入的时候,录入一个字母,而不是数值的话,就会发生奇怪的事情,我们的第二段代码(多个数值类型输入)的代码失效了,没有让我们输入,直接到了cout位置,且输入一些不明的数值。
程序的输入都建有一个缓冲区,即输入缓冲区。每次输入过程是这样的,当一次键盘输入结束时会将输入的数据存入输入缓冲区,而cin函数直接从输入缓冲区中取数据。正因为cin函数是直接从缓冲区取数据的,所以有时候当缓冲区中有残留数据时,cin函数会直接取得这些残留数据而不会请求键盘输入。在多个数值类型输入的代码中,由于cin在遇到空格时,就会停止读取,所以如果我们在第一次输入时,利用空格隔开两个数值,那么cin在第一次取数据的时候,只会读取第一个数值,到空格结束,此时缓冲区还保留着第二个数值,那么cin就会直接从缓冲区取残留数据赋值给第二个数值,而不会请求输入。如果我们将两段代码同时执行,且在第一段输入数值的时候,输入了字符,那么cin不会将字符赋值给数值,且输入的字符将停留在缓冲区中。那么再执行第二段代码的时候,这个输入的字符就会起作用,导致第二段代码cin直接取这个错误数据,而不会请求输入。我们可以验证一下:
// 一个数值一个字符输入
int d;
char e;
std::cin >> d; // 输入:2回车(直接输入2e回车)
std::cout << "d=" << d << std::endl;
std::cin >> e; // 输入:e回车
std::cout << "e=" << e << std::endl;
按照正常的录入逻辑,我们应该先输入一个数字,然后回车,再输入一个字符,然后回车,这样程序就能够正常运行。但是,如果我们第一次输入的时候,直接输入一个数字和一个字符,然后回车,你就发现程序运行结束了,cin自动把数值给了d,把另一个字符给了e。因为我们第一次输入的数字和字符的时候,cin只能将数字赋值给d,剩下的字符就残留在输入缓冲区中,然后cin发现有残留数据,就不会让我们再次输入,而是直接给到e。如何解决输入缓冲区残留问题,我们只需要再每次cin输入之后,使用ignore方法抛弃到残留的数据即可,代码如下:
// 一个数值一个字符输入
int d;
char e;
std::cin >> d; // 输入:2回车(直接输入2e回车)
std::cout << "d=" << d << std::endl;
// 回车(包括回车)之前的所以字符从输入缓冲流中清除出去
std::cin.ignore(1024, '\n');
std::cin >> e; // 输入:e回车
std::cout << "e=" << e << std::endl;
再次运行,当我们输入”2e回车”的时候,就不会造成之前的情况发生了。我们必须输入两次分别对数值d和字符e进行两次输入的赋值操作。
// 字符串的输入
char f[10];
std::cin >> f; // 输入字符串:richie回车
std::cout << "f=" << f << std::endl; // 输出 f = richie
这里涉及到一个数组长度的问题,如果我们输入字符的长度超过定义数组的长度,那么程序在运行的时候,将会出错。解决这个问题,就可以通过get方法来指定赋值长度。
// 指定长度的字符串输入
char g[5];
std::cin.get(g, 5); // 输入字符串:123456789
std::cout << "g=" << g << std::endl; // 输入: g=1234
注意:字符串的末尾是一个空字符,标识字符串的结束,占据数组一个元素位置。
C++标准IO库不仅仅支持键盘和显示器的输入输出,还支持文件的输入输出。使用C++标准流库操作文件,需要在源文件的开头引入 “include <fstream>” 头文件。文本文件的输入和输出操作分为3个主要步骤:打开,操作和关闭。打开文件就是使磁盘文件与内存中的流对象相关联,文件操作就是进行数据的输入(读)和输出(写),关闭文件就是将内存缓冲区中的数据写到磁盘文件中。
// 向文本文件输出(写)数据
std::ofstream outFile;
outFile.open("./test.txt");
//outFile.open("./test.txt", std::ios::app);
outFile << "hello, c++" << "\n";
outFile.close();
Ofstream就是写入文件的操作类,它里面有open方法用于打开文件,close方法用于关闭文件。Open方法的参数就是文件的路径,可以是绝对路径(”E:/vs/test.txt”),也可以是相对路径(”./test.txt”)。相对路径就是相对于当前代码文件的路径,也就是说我们要打开的这个test.txt文件和当前的CPP代码文件在同一目录下。如果test.txt文件不存在,则自动创建。
如果重复运行上面的代码,你会发现在test.txt文件中始终只有一行” hello, c++”的文本。这是因为我们打开文件的方式是覆盖模式,也就是说,每次写入文本,都会覆盖掉之前的文本。如果我们不想覆盖,那么可以改成追加模式,这样每次写入文本就再之前的文本后面写入,改成追加模式如下:
outFile.open("./test.txt", std::ios::app);
如果是追加数据的话,我们希望每次追加的数据换行的话,就需要在数据的末尾添加换行符即可,也就是上文中的”\n”,这个非常的重要!!!
// 向文本文件输出(读)数据:
std::ifstream inFile;
inFile.open("./test.txt");
char txt[50];
inFile >> txt;
std::cout << txt << std::endl; // 输入: hello,
inFile.close();
Ifstream是用来读取文件的标准类,它同样包括open和close方法。需要注意的是,“>>”会以空格为分隔符逐个从文件中读取数据并将其保存到相应的数据变量中,这就是为什么我们只输出了”hello,”,而没有”c++”。因为他们中间有一个空格。
数据的读取和数据的存储是相对应的。当我们存储数据的时候,这些数据往往按照一定的格式去存储。例如我们存储很多学生的资料,可以使用空格间隔每个学生的数据,或者一行一个学生数据,或固定长度的存储每个学生的数据。那么当我们读取的时候,就需要按照对应的格式来正确的获取学生数据。
// 循环(空格)读取文本文件
std::ifstream inFile2;
inFile2.open("./test.txt");
std::string str;
std::string temp;
while (inFile2 >> temp) {
str += temp;
}
std::cout << str << std::endl; // 输入: hello,c++
inFile2.close();
// 循环每行读取文本文件,换行符为'\n'
std::ifstream inFile3;
inFile3.open("./test.txt");
std::string str3;
char temp3[1024];//每行不能超过1024个字符
while (inFile3.getline(temp3,1024,'\n')) {
str3.append(temp3);
}
std::cout << str3 << std::endl;
inFile3.close();
在这里,我们使用了getline方法,第一个参数就是存放读取数据的字符数组变量,第二个参数就是读取的长度,一般我们设置为数组长度即可,第三个参数就是换行符‘\n’。我们必须保证文件中的换行符是’\n’才行。
C++产生随机数的两个方法rand和srand。rand()函数不需要引入任何头文件,可直接使用,返回0~RAND_MAX(32767)的整数,不需要参数,它是根据种子生成的,根据不同的种子产生不同的随机序列。系统默认的种子是1,随机序列又是根据种子产生的,所以说每次使用的随机序列都是固定的,随机数不随机。要想使它随机,就必须使它的种子随机。srand()函数就是用来设置rand()函数的种子的。同样srand()函数也不需要头文件,可以直接使用,根据不同的参数产生不同的种子。如果参数一样,生成的种子也是一样的,rand()函数返回的值也一样。用时间值做种子,就可以产生随机数了,因为时间总是在变的嘛。将time(NULL)作为srand()的参数,更新种子,再用rand()函数产生随机数。
// 随机数案例
srand(time(NULL));
int r = rand();
std::cout << r << std::endl;
// 随机1-100以内随机数(包括1和100两个数字)
int r2 = rand() % 100 + 1;
std::cout << r2 << std::endl;
// 随机0-1之间的小数
double r3 = rand() / double(RAND_MAX);
std::cout << r3 << std::endl;
本课程的所有代码案例下载地址:
备注:这是我们游戏开发系列教程的第一个课程,主要是编程语言的基础学习,优先学习C++编程语言,然后进行C#语言,最后才是Java语言,紧接着就是使用C++和DirectX来介绍游戏开发中的一些基础理论知识。我们游戏开发系列教程的第二个课程是Unity游戏引擎的学习。课程中如果有一些错误的地方,请大家留言指正,感激不尽!