目录
一、输入输出操作
1. 相关的类
2. 标准流对象
3. istream类的成员函数
二、流操纵算子
1. 整数流的基数
2. 浮点数精度的流操纵算子
3. 域宽的流操纵算子
4. 其他的流操纵算子
5. 用户自定义流操纵算子
三、文件读写
1. 文本文件的读写
2. 二进制文件的读写
3. 文件读写指针
4. 文本文件和二进制文件区别
一、输入输出操作
1. 相关的类
- ios基类派生出istream和ostream。
- istream派生出ifstream,ostream派生出ofstream。
- iostream继承自istream和ostream。
- fstream继承自iostream。
注意:cin就是istream类的对象,cout就是istream类的对象。
2. 标准流对象
(1) 输入流对象:cin与标准输入设备相连(从键盘获取数据)。也可以被重定向为从文件中读取数据。
(2) 输出流对象:
- cout与标准输出设备相连(在屏幕上打印数据)。也可以被重定向为向文件写入数据(freopen函数)。
- cerr与标准错误输出设备相连。
- clog与标准错误输出设备相连。
- cerr和clog的区别在于cerr不使用缓冲区,直接向显示器输出信息;而clog采用缓冲区,只有缓冲区满或者刷新时才会输出到屏幕上。
(3) 代码示例:
【1】输出重定向
#define _CRT_SECURE_NO_WARNINGS #include <iostream> using namespace std;int main() {double x, y;cin >> x >> y;//freopen函数:输出重定向到文件//原型:FILE *freopen(const char *path, const char *mode, FILE * stream);//path是目标文件//mode为文件打开模式"r"或"w"//stream为标准流文件,例如stdin标准输入流, stdout标准输出流, stderr标准错误流freopen("cout.txt", "w", stdout);if (y == 0) {cerr << "error!" << endl;}else {cout << x / y;//输出到文件cout.txt中}return 0; }
显然,屏幕上并没有输出计算结果0.5,该结果输出到cout.txt文件中。
【2】输入重定向
#define _CRT_SECURE_NO_WARNINGS #include <iostream> using namespace std;int main() {int n1 = 0, n2 = 0;//freopen函数:输入重定向freopen("input.txt", "r", stdin);cin >> n1 >> n2;//由于重定向了,不会在终端等用户输入cout << "结果:" << n1 << ", " << n2 << endl;return 0; }
【3】判断输入流结束
#define _CRT_SECURE_NO_WARNINGS #include <iostream> using namespace std;int main() {int x;freopen("test.txt", "r", stdin);while (cin >> x) {cout << "读入:" << x << endl;}return 0; }
注意:①这里将cin重定向为从文件读取数据,那么读到文件末尾即结束。若未重定向,从键盘中输入则需要单独一行输入Ctrl+Z代表输入流结束。②cin >> x返回值为istream&,而istream会有对象的重载,将其转换成bool类型,因此可以作为循环条件。
3. istream类的成员函数
(1) getline函数:
- 原型:①istream& getline(char* buf, int bufSize); ②istream& getline(char* buf, int bufSize, char delim);
- 对于第一个原型:从输入流中读取bufSize-1个字符到缓冲区buf,或读到'\n'为止(哪个先到算哪个)。
- 对于第二个原型:从输入流中读取bufSize-1个字符到缓冲区buf,或读到delim字符为止(哪个先到算哪个)。
- 注意:
- 两个函数都会自动在buf中读入数据的结尾添加'\0'。
- ‘\0’和delim都不会被读入buf,但会被输入流中取走。
- 如果输入流中'\n'或delim之前的字符个数达到或超过了bufSize个,就导致读入错误,即虽然本次读入已经完成,但是之后的读入就都失败了。
- 可以用if (!cin.getline(...))判断输入是否结束。
(2) bool eof(); 用于判断输入流是否结束。
(3) int peek(); 返回下一个字符,但不从流中去除。
(4) istream& putback(char c); 将字符ch放回输入流的头部。
(5) istream& ignore(int nCount = 1, int delim = EOF); 从流中删掉最多nCount个字符,遇到EOF结束。
(6) getline函数代码示例:
#include <iostream>
using namespace std;int main()
{int x;char buf[90];cin >> x;cin.getline(buf, 90, '\n');cout << buf << endl;return 0;
}
二、流操纵算子
1. 整数流的基数
(1) 说明:在cout时,可以指定输出的整数的进制:dec(十进制)、oct(八进制)、hex(十六进制)。
(2) 注意:①使用流操纵算子,需要添加头文件iomanip。②一旦设置,就会持续有效直到新的操纵算子出现。
(3) 代码示例:
#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <iomanip> using namespace std;int main() {int n = 10;cout << dec << n << endl;//十进制显示ncout << oct << n << endl;//八进制显示ncout << hex << n << endl;//十六进制显示n//一旦设置dec、oct或者hex,就会持续有效直到写新操纵算子。cout << n << endl;return 0; }
2. 浮点数精度的流操纵算子
(1) precision:是ostream的成员函数,调用方式为:cout.precision(5); 用于指定浮点数的有效位数(非定点方式)。系统默认是非定点方式。
(2) setprecision:流操纵算子,调用方式为:cout << setprecision(5); 用于指定浮点数的有效位数(非定点方式)和指定浮点数的小数后的有效位数(非定点方式)。定点方式:小数点必须出现在个位数后面。
(3) 注意:
- 当位数超过精度时会四舍五入。
- setprecision方式会持续有效,直到设置新的精确度。
- setprecision操纵算子也需要添加头文件iomanip。
- 在非定点方式下,setprecision可能会使用科学计数法来表示。
(4) setiosflags(ios::fixed):以小数点位置固定的方式来输出(可以简单用cout << fixed;)。另外,cout << scientific表示采用科学计数法。
(5) resetiosflags(ios::fixed):取消以小数点位置固定的方式来输出。
(6) 代码示例:
#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <iomanip> using namespace std;int main() {float f = 1234567.89;int n = 123456;//默认是非定点方式cout << "*****非定点******" << endl;cout << setprecision(6);//设置浮点数有效位数为6cout << f << endl;//由于非定点方式且规定只能有6位,因此用科学计数法显示cout << n << endl;//浮点数的操纵算子不影响整数//修改为定点方式cout << "*****定点********" << endl;cout << setiosflags(ios::fixed);cout << f << endl;//小数点后有6位,不够就补0//修改为非定点方式cout << "*****非定点******" << endl;cout << resetiosflags(ios::fixed);cout << f << endl;//由于非定点方式且规定只能有6位,因此用科学计数法显示return 0; }
3. 域宽的流操纵算子
(1) setw:流操纵算子,用于设置域宽。调用方式:cout << setw(5)或cin >> setw(5)。
(2) width:成员函数,用于设置域宽。调用方式:cout.width(5)或cin.width(5)。
(3) 注意:域宽的流操纵算子是一次性的,每次输入输出前都需要指定宽度。
(4) 代码示例:
#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <iomanip> using namespace std;int main() {int m = 4;char string[10];cin >> setw(5);//设置输入宽度while (cin >> string) {cout << setw(m++);//设置输出宽度cout << string << endl;cin >> setw(5);}return 0; }
(5) 结果解释:
- 第一行是我们输入的数据1234567890.
- 第二行输出1234。首先,设置输入宽度为5(实际上只接收4个字符,最后自动加'\0'),那么string里就是1234‘\0’。其次,设置输出宽度为4(设置完后m自增为5),那么输出4个字符即1234.
- 第三行输出" 5678"。首先,设置输入宽度为5(实际上只接收4个字符,最后自动加'\0'),那么string里就是5678‘\0’。其次,设置输出宽度为5(设置完后m自增为6),那么输出5个字符即空格+5678.
- 第四行输出" 90"。首先,设置输入宽度为5(实际上只接收4个字符,最后自动加'\0'),那么string里就是90‘\0’。其次,设置输出宽度为6(设置完后m自增为7),那么输出6个字符即4个空格+90.
4. 其他的流操纵算子
(1) cout << showpos; 非负数显示正号。
(2) cout << noshowpos; 非负数不显示正号。
(3) cout << setfill(*); 宽度不足用*填补。
(4) cout << right; 右对齐,宽度不足则左边填充。
(5) cout << left; 左对齐,宽度不足则右边填充。
(6) cout << internal; 在负号和数字之间填充。
(7) 综合案例:
#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <iomanip> using namespace std;int main() {int i = 141;double x = 1234567.89, y = 12.34567;//1) 8d 141 215cout << "1) " << hex << i << " " << dec << i << " " << oct << i << endl;//2) 1.2346e+006 12.346cout << "2) " << setprecision(5) << x << " " << y << endl;//3) 1234567.89000 12.34567cout << "3) " << fixed << setprecision(5) << x << " " << y << endl;//4) 1.23457e+006 1.23457e+001cout << "4) " << scientific << setprecision(5) << x << " " << y << endl;//5) ***+12.10000cout << "5) " << showpos << right << fixed << setprecision(5) << setfill('*') << setw(12) << 12.1 << endl;//6) 12.10000****cout << "6) " << noshowpos << left << fixed << setprecision(5) << setfill('*') << setw(12) << 12.1 << endl;//7) ****12.10000cout << "7) " << noshowpos << right << fixed << setprecision(5) << setfill('*') << setw(12) << 12.1 << endl;//8) -***12.10000cout << "8) " << showpos << right << fixed << setprecision(5) << internal << setfill('*') << setw(12) << -12.1 << endl;//9) 12.10000cout << "9) " << noshowpos << fixed << setprecision(5) << 12.1 << endl;return 0; }
5. 用户自定义流操纵算子
(1) 格式:
ostream& 函数名(ostream& cout) {//执行的操作return cout; }
(2) 代码示例:定义tab流操纵算子。
#define _CRT_SECURE_NO_WARNINGS #include <iostream> using namespace std;ostream& tab(ostream& output) {return output << '\t'; }int main() {cout << "a" << tab << "b" << endl;return 0; }
(3) 底层解释:由于iostream里对<<进行了重载(使用成员函数方式),即ostream& operator << (ostream& (*p) (ostream&)); 其中ostream& (*p) (ostream&)是函数指针,能够根据函数名找到对应的函数并执行函数体,同时内部还返回了*this就是对象本身。因此,在上述示例中,tab就是一个函数名,会执行tab的函数体。
三、文件读写
1. 文本文件的读写
(1) 创建读文件对象:ifstream srcFile("in.txt", ios::in);
(2) 创建写文件对象:ofstream destFile("out.txt", ios::out);
(3) 从文件中读取字符:srcFile >> x;
(4) 将字符写入文件:destFile << x;
(5) 注意:
- 写文件对象中ios::out选项:删除原有内容,写入新内容。
- 写文件对象中ios::app选项:在原内容后面追加新内容。
- 读写文件对象中ios::binary选项:以二进制方式写入/读取。
- ifstream和ofstream换成fstream也行。
(6) 代码示例:将in.txt中的内容1 234 9 45 6 879排序,并存到out.txt中。
#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <fstream> #include <vector> #include <algorithm> using namespace std;int main() {//1.打开文件in.txtifstream srcFile("./in.txt", ios::in);if (!srcFile) {cout << "srcFile文件打开失败!" << endl;return -1;}//2.读取文本字符并加入vector中int readInt;vector<int> v;while (srcFile >> readInt) {v.push_back(readInt);}//3.对vector中元素排序sort(v.begin(), v.end());//4.将排序结果写入out.txtofstream outFile("./out.txt", ios::out);if (!outFile) {cout << "outFile文件打开失败!" << endl;return -1;}for (int i = 0; i < v.size(); i++) {outFile << v[i] << " ";}//5.关闭文件srcFile.close();outFile.close();return 0; }
2. 二进制文件的读写
(1) 创建读写对象:方式与文本文件相同,但是选项要或(|)上ios::binary选项!
(2) 读文件:使用ifstream和fstream中的成员函数read函数。
- 函数原型:istream& read(char* s, long n);
- 功能:将文件读指针指向的地方的n个字节内容,读取到内存地址s,然后读指针向后移动n字节。
(3) 写文件:使用ofstream和fstream中的成员函数write函数。
- 函数原型:istream& write(const char* s, long n);
- 功能:将内存地址s处的n个字节内容,写入到写指针指向的位置,然后写指针向后移动n字节。
(4) 代码示例:将整数120写入二进制文件,再从该二进制文件中读取整数。
#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <fstream> using namespace std;int main() {//写入ret于二进制文件out.dat中int ret = 120;ofstream outFile("./out.dat", ios::out | ios::binary);outFile.write((const char*)(&ret), sizeof(int));outFile.close();//从out.dat中读取一个整数int readInt;ifstream inFile("./out.dat", ios::in | ios::binary);inFile.read((char*)(&readInt), sizeof(int));inFile.close();//显示读取的整数cout << readInt << endl;return 0; }
3. 文件读写指针
(1) 介绍:
- 对于输入文件,有一个读指针。
- 对于输出文件,有一个写指针。
- 对于输入输出文件,有一个读写指针。
- 读/写指针标识文件操作的当前位置。
(2) 读指针相关操作:
- tellg():获取读指针的位置。调用方式:"输出对象.tellg()"。
- seekg(int i):移动读指针至第i个字节处。调用方式:"输出对象.seekg(i)"。
- seekg(int i, ios::beg):移动至距文件开始的第i个位置。
- seekg(int i, ios::cur):移动至当前位置后的第i位置。
- seekg(int i, ios::end):移动至距文件末尾的第i个位置。(常用于统计文本的字符数)
(3) 写指针相关操作:
- tellp():获取写指针的位置。调用方式:"输出对象.tellp()"。
- seekp(int i):移动写指针至第i个字节处。调用方式:"输出对象.seekp(i)"。
- seekp(int i, ios::beg):移动至距文件开始的第i个位置。
- seekp(int i, ios::cur):移动至当前位置后的第i位置。
- seekp(int i, ios::end):移动至距文件末尾的第i个位置。
(4) 代码示例:输出A~Z于test.txt中,并且每两个字符间空两格。
#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <fstream> using namespace std;int main() {ofstream outFile("./test.txt", ios::out);/* 输出A~Z于test.txt中,并且每两个字符间空两格*/for (char i = 'A'; i <= 'Z'; i++) {//获取写文件指针位置int location = outFile.tellp();//写入字符outFile << i;//文件指针会自动+1,再使用seekp跳到后两个位置outFile.seekp(2, ios::cur);//打印测试信息cout << location << endl;}//关闭文件outFile.close();return 0; }
4. 文本文件和二进制文件区别
(1) 不同操作系统下换行符号区别:
- Linux下的换行:'\n'(ASCII:0x0a)
- Windows下的换行:'\r\n'(ASCII:0x0d0a)
- MacOS下的换行:'\r'(ASCII:0x0d)
- 由于ASCII码不同,Linux和MacOS的文本文件在Windows中的记事本打开时不换行。
(2) 文本文件和二进制文件区别:
- Linux下打开文件,用不用ios::binary没区别。
- Windows下打开文件,如果不用ios::binary,则会造成①读取文件时,所有的'\r\n'都会被当作'\n'处理,因此会少读了一个字符'\r'。②写入文件时,写入单独的'\n'时,系统会自动加'\r',因此多写了一个字符'\r'。
- 一般来说,使用二进制文件更加节省空间。例如,保存123456,若保存于文本文件则需要6个字节,而保存于二进制文件用一个int的字节就行了。