目录
一、输入输出操作
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;//十进制显示n cout << oct << n << endl;//八进制显示n cout << 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);//设置浮点数有效位数为6 cout << 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 215 cout << "1) " << hex << i << " " << dec << i << " " << oct << i << endl; //2) 1.2346e+006 12.346 cout << "2) " << setprecision(5) << x << " " << y << endl; //3) 1234567.89000 12.34567 cout << "3) " << fixed << setprecision(5) << x << " " << y << endl; //4) 1.23457e+006 1.23457e+001 cout << "4) " << scientific << setprecision(5) << x << " " << y << endl; //5) ***+12.10000 cout << "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.10000 cout << "7) " << noshowpos << right << fixed << setprecision(5) << setfill('*') << setw(12) << 12.1 << endl; //8) -***12.10000 cout << "8) " << showpos << right << fixed << setprecision(5) << internal << setfill('*') << setw(12) << -12.1 << endl; //9) 12.10000 cout << "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.txt ifstream 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.txt ofstream 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的字节就行了。