一文带你掌握C++IO流

115 阅读6分钟

10. C++IO流

C语言的输入输出

在C语言中我们使用最频繁的输入输出方式是 scanf () 与 printf()

  • scanf():从标准输入设备 (键盘) 读取数据,并将值存放在变量对应的内存空间中。
  • printf():将指定的文字/字符串输出到标准输出设备 (显示器)。(注意宽度输出和精度输出控制)

实际上C语言借助了相应的缓冲区来进行输入与输出。如下图所示:

img

对输入输出缓冲区的理解

  • 缓冲区的存在可以屏蔽掉低级I/O的实现;由于低级I/O的实现依赖操作系统本身内核的实现,所以如果能够屏蔽这部分的差异,就可以很容易写出可移植的程序。
  • 可以使用这部分的内容实现 “行” 读取的行为;对于计算机而言是没有 “行” 这个概念,有了这部分,我们就可以定义 “行” 的概念,然后解析缓冲区的内容,返回一个 “行”。

注:C语言除了有输入输出接口 scanf 和 printf,还有文件操作读写接口 fread/fwrite、fscanf/fprintf,以及字符串序列化反序列化接口 sprintf/snprintf/sscanf。

流的概念

流就是若干字节组成字节序列,流操作从一个到另一个移动的过程.是对一种有序连续且具有方向性的数据( 其单位可以是bit/byte/packet )的抽象描述流的特点是有序连续且具有方向性

C++流是指信息从外部输入设备(如键盘)向计算机内部(如内存)输入和从内存向外部输出设备(显示器)输出的过程。这种输入输出的过程被形象的比喻为 “流”

为了实现这种流动,C++定义了I/O标准类库,这些每个类都称为流/流类,用以完成某方面的功能。

流中的内容:二进制数据 ASCII码

流类体系

C++中用类实现所有流类操作

  • 标准的输入输出流

    • C++格式控制
  • 字符流

  • 文件流

#include <iostream>	 //istream ostream
#include <fstream>   //ifstream ofstream
#include <strstream> //istringstream  ostringstream
using namespace std;
int main() 
{
	fstream out;
	ifstream iin;
	return 0;
}

标准输入输出流

  1. 重定向: C语言 freopen函数介绍

对象类型作用
cin标准输入从键盘读取,可以重定向
cout标准输出输出到控制台,可以重定向
cerr标准错误输出输出到控制台,不可以重定向
clog标准错误输出输出到控制台,可重定向
#include <iostream>
using namespace std;
int main() 
{
	cout << "标准输出" << endl;
	cerr << "标准错误" << endl;
	clog << "标准错误" << endl;
	return 0;
}
  • 字符和字符串输入
    • cout成员函数
      • put() : 输出一个字符
      • write(): 输出字符串
    • cin成员函数
      • get():输入一个字符
      • getline: 输入一个字符串
#include <iostream>
using namespace std;
int main() 
{

	cout << "标准输出" << endl;
	cerr << "标准错误" << endl;
	clog << "标准错误" << endl;
	//字符输入
	cout << "字符输入:" << endl;
	int userKey = cin.get();
	cout.put(userKey);
	char str[10] = { "" };
	cout << "字符串输入:" << endl;
	while (getchar() != '\n');
	cin.getline(str, 10);  //10个长度包含\0
	cout << str << endl;
	cout.write(str, 10);
	return 0;
}
  • C++格式控制
    • 包含头文件: iomanip
    • 通过对象的形式,一种通过成员的函数形式
对象形式实际含义
setbase(n)设置多少进制输出整数(参数是8和16)
setw(n)设置输出数据宽度(默认对齐是右对齐,不足补空格)
setiosflags(ios::left)设置对齐方式: ios::left ,ios::right
setprecition(n)单纯使用是控制有效位数,如果控制小数位数结合fixed
setfill(n)填充字符
#include <iostream>
#include <iomanip>
using namespace std;
int main() 
{
	//进制输出
	cout << setbase(16) << 32 << endl;
	cout << setbase(8) << 32 << endl;
	cout <<showbase<< hex << 32 << endl;
	cout << dec << 32 << endl;
	cout << noshowbase<<oct << 32 << endl;

	//cout << setbase(2) << 32 << endl;  //无效
	//默认右对齐
	cout << setw(10) << "姓名" << setw(10) << "年龄" << setw(10) << "编号" << endl;
	cout << setw(10) << "小芳" << setw(10) << 17 << setw(10) << 119911 << endl;
	cout << setiosflags(ios::left);
	cout << setw(10) << "姓名" << setw(10) << "年龄" << setw(10) << "编号" << endl;
	cout << setw(10) << "小芳" << setw(10) << 17 << setw(10) << 119911 << endl;
	cout << setprecision(4) << 300.12345 << endl;  //直接用控制的是有效位数
	cout << fixed<<setprecision(4) << 300.12349 << endl;  //小数位数
	cout << setiosflags(ios::scientific) << 300.12345 << endl;

	//所有的格式控制存在成员函数的调用形式
	cout.width(8);
	cout << "姓名";
	cout.width(8);
	cout << 1 << endl;
	cout.precision(4);
	//取消上述格式
	cout <<resetiosflags <<300.333 << endl;
	bool num = 1;
	cout << boolalpha << num << endl;		//true和false形式输出bool类型
	return 0;
}

字符流

  • 包含头文件: sstream

    • istringstream

    • ostringstream

    • stringstream

  • 一般处理字符流的时候用的是stringstream类型的对象

  • 获取字符流中的stirng

    • string str(); //获取string

    • void str(const string& str) //重置流对象中字符串

  • 字符流做什么

    • 数据类型的转换
    • 数据的分割
#include <sstream>
#include <iostream>
#include <string>
using namespace std;
int main() 
{
	stringstream stream("ILoveyou");
	cout << stream.str() << endl;
	char str[20] = "";
	stream >> str;
	cout << str << endl;
	stream.str("");		  //清除

	//数据类型转换
	//整数转字符串,字符串转数字
	string num = to_string(123);
	cout << num << endl;
	int inumber = 12123;
	char result[20] = { "" };
	stringstream buf(result);
	buf << inumber;
	buf >> result;
	cout << result << endl;

	stringstream strNum("12345435");
	int dataNum = 0;
	strNum >> dataNum;
	cout << dataNum << endl;
	//数据切割(流中默认空格作为单一数据的间隔)
	stringstream ip("ip: 192.168.1.1");
	char strip[20] = { "" };
	ip >> strip;		//ip: 拿出来
	int ipNum[4];
	char userKey;
	ip >> ipNum[0];
	ip >> userKey;

	ip >> ipNum[1];
	ip >> userKey;

	ip >> ipNum[2];
	ip >> userKey;

	ip >> ipNum[3];
	for (int i = 0; i < 4; i++) 
	{
		cout << ipNum[i] << "\t";
	}
	cout << endl;
	//注意点: 流在做转换的,必须调用clear清除处理
	buf.clear();
	buf << inumber;
	buf >> result;
	cout << result << endl;

	return 0;
}

文件操作流

  • 包含头文件: fstream
    • ofstream: 打开文件只能写操作
    • ifstream: 打开文件只读操作
    • 一般大家创建一个fstream对象,可读可写
  • 打开文件
    • 构造的方式,带参数构造函数:const char* URL,ios::openmode mode
    • 成员函数方式: void open(const char* URL,ios::openmode mode)
    • 判断文件打开是否成功
      • !is_open()函数判断是否打开成功 ,!is_open()是1的打开失败
      • !文件对象 .!对象是1打开失败
读写方式作用
ios::in读的方式打开文件
ios::out写的方式打开文件
ios::app追加写文件
ios::ate打开已有文件,指针在文件末位
ios::trunc文件不存在具有创建方式
ios::binary二进制打开,默认打开方式ASCII码
ios::nocreate不创建
ios::noreplace不替换

组合方式: 用位或, 可读可写: ios::in|ios::out

  • 关闭文件
    • close关闭文件
  • 文件读写
    • 直接采用>> <<符号进行读写
    • 采用成员函数读写:read函数和write成员函数
  • 文件指针移动
    • ifstream文件指针
      • ifstream& seekg(long int pos);
      • ifstream& seekg(long int pos,ios_base::seekdir begin);
    • ofstream文件指针
      • ofstream& seekp(long int pos);
      • ofstream& seekp(long int pos,ios_base::seekdir begin);
    • begin:
      • ios::beg 开始位置
      • ios::cur 当前位置
      • ios::end 结束位置
#include <iostream>
		}
		file  << name<<" "<< age<<" "<< num<<endl;
		file.close();
	}
	void readFile(string fileName) 
	{
		fstream file(fileName, ios::in);
		if (!file)
		{
			cout << "打开文件失败!" << endl;
			return;
		}
		while (true) 
		{
			MM temp;
			file >> temp.name >> temp.age >> temp.num;
			if (file.eof()) 
			{
				break;
			}
			temp.print();
		}
		file.close();
	}
protected:
	string name;
	int age;
	int num;
};

void asciiRWFile(string readFile, string writeFile) 
{
	//流的方式
	//字符或者字符串的
	fstream read(readFile, ios::in);
	fstream write(writeFile, ios::out);
	while (!read.eof())
	{
		char userkey = read.get();   //getline()
		write.put(userkey);			 //write()函数
	}
	read.close();
	write.close();
}
//二进制读写
void binaryRWFile(string readFile, string writeFile) 
{
	fstream r(readFile, ios::in | ios::binary);
	fstream w(writeFile, ios::out | ios::binary);
	while (!r.eof()) 
	{
		char str[1024] = {""};  //缓冲区
		r.read(str, 1024);
		w.write(str, strlen(str));  //长度写多少就写入文件多少
	}
	r.close();
	w.close();
}
//文件指针移动
int getSize(string fileName) 
{
	fstream read(fileName, ios::in | ios::binary);
	read.seekg(0, ios::end);
	int size = read.tellg();
	read.close();
	return size;
}

int main() 
{
	//打开文件测试
	//fstream file("xxoo.txt",ios::in|ios::out|ios::trunc);
	//等效下面两行
	fstream file;
	file.open("xxoo.txt", ios::in | ios::out | ios::trunc);
	if (!file || !file.is_open()) 
	{
		cerr << "文件打开失败" << endl;
	}
	file.close();
	MM mm("xxx", 18, 1001);
	mm.saveFile("mm.txt");
	mm.readFile("mm.txt");

	asciiRWFile("mm.txt", "xxoo.txt");
	binaryRWFile("xxoo.txt", "rw.txt");
	cout << "size:" << getSize("size.txt") << endl;
	return 0;
}