9-IO流

发布于:2024-06-12 ⋅ 阅读:(33) ⋅ 点赞:(0)

1、简介

  • 流/流数据(Stream)
    • 字节序列形式的数据如:二进制数据、文本字符等等),犹如流水一般,从一个对象流向另一个对象
  • 输入流(Input Stream)
    • 数据从表示输入设备(如:键盘、磁盘文件等)的对象流向内存对象。
  • 输出流(Output Stream)
    • 数据从内存对象流向表示输出设备(如:显示器、打印机等)的对象。
  • 流缓冲(Stream Buffer)
    • 介于各种I/O设备和内存对象之间的内存缓冲区
      • 当从键盘输入时,数据首先进入键盘缓冲区,直到按下回车键,才将键盘缓冲区中的数据灌注到输入流缓冲区,之后再通过流操作符“>>”,进入内存对象
      • 当向显示器输出时,数据首先通过流操作符“<<”从内存对象进入输出流缓冲区,直到缓冲区满或遇到换行符,才将其中的数据灌注到显示器上显示出来
  • 流对象(Stream Object)
    • 表示各种输入输出设备的对象,如键盘、显示器等,以流的方式接收或提供数据,故称为流对象
    • 向下访问各种物理设备接口,向上与应用程序交互,中间维护流缓冲区
    • 三个预定义的标准流对象
      • cin:标准输入设备 如:键盘
      • cout:标准输出设备 如:显示器
      • cerr:标准错误输出设备 如:显示器,不带缓冲
  • 流类(Stream Class)
    • 用于实例化流对象的类
    • cin和cout分别是istream_withassign和ostream_withassign类的对象
  • 流类库(Stream Class Library)
    • C++以继承的方式定义了一组流类,并将其作为标准C++库的一部分提供给用户
    • 基于流类库可以构建三种形式的流对象
      • 面向控制台的I/O流
      • 面向文件的I/O流
      • 面向内存的I/O流

2、I/O流类库

在这里插入图片描述

fstreambase:封装对文件的操作
istream:封装输入的操作
ostream:封装输出的操作
strstreambase:封装对字符串的操作

在这里插入图片描述

在这里插入图片描述

3、I/O流的打开与关闭

3.1 打开I/O流

在C++中我们一般是使用构造函数的方式打开I/O流,比如:

  • 打开输入流
ifstream (const char* filename,openmode mode);
  • 打开输出流
ofstream (const char* filename,openmode mode);
  • 打开输入输出流
fstream  (const char* filename,openmode mode);

filename:文件路径,mode:打开方式

3.2 文件的打开方式

文件的打开方式有6种,分别是:

  • 1:ios::out
    • 打开文件用于写入,文件不存在则创建该文件,文件存在的情况下会清空里面的内容
    • 适用于ofstream/fstream
  • 2:ios::app
    • 打开文件用于追加,文件不存在则创建该文件,文件存在的情况下会在文件末尾追加内容
    • 适用于ofstream/fstream
  • 3:ios::trunc
    • 打开时清空原内容,文件不存在则创建该文件
    • 适用于ofstream/fstream
  • 4:ios::in
    • 打开文件用于读取,文件不存在则打开失败,文件存在时不会清空里面的数据
    • 适用于ifstream/fstream
  • 5:ios::ate
    • 打开时定位到文件尾
    • 适用于ifstream/fstream
  • 6:ios::binary
    • 二进制模式读写
    • 适用于ifstream/ofstream/fstream
int main(void){
	ofstream ofs("./abc", ios::app);// 以追加的方式打开文件
	if (!ofs){ // 这里会调用ofs.operator bool() 函数
		cerr << "ofs流对象状态失败--读取文件失败" << endl;
	}
	ofs << "13 17.54 hello world" << endl;
	if (!ofs){ // 每一步IO操作(打开文件、写文件、设置文件等等)结束后,都可以将流对象放置在布尔上下文中,来判断IO操作是否成功
		cerr << "ofs流对象状态失败--写入数据失败" << endl;
	}
	ofs.close();
	ifstream ifs1("./abc", ios::in);// 打开文件用于读取
	if (!ifs1){
		cerr << "ifs1流对象状态失败--打开文件失败" << endl;
	}
	int a; double b; string c, d;
	ifs1 >> a >> b >> c >> d;
	if (!ifs1){ 
		cerr << "ifs1流对象状态失败--读取文件失败" << endl; 
	}
	cout << a << " " << b << " " << c << " " << d << endl;
	return 0;
}

3.3 I/O流对象的状态

I/O流类对象内部保存当前状态,其值为

  • ios::goodbit
    • 值为0:表示一切正常
  • ios::badbit
    • 值为1:表示发生致命错误
  • ios::eofbit
    • 值为2: 表示遇到文件尾
  • ios::failbit
    • 值为4: 表示打开文件失败或实际读写字节数未达到预期
      注意:I/O流对象支持到bool类型的隐式转换(利用类型转换操作符函数)
    • 当流的状态值为1、2、4等情况时,流对象返回的是false,否则返回true
    • 每一次的I/O操作(打开文件、读取数据等)结束后都可以将流对象直接放置于布尔上下文中,用来判断I/O操作是否成功
    • 处于状态1或4的流,在复位之前是无法工作的
int main(){
	ifstream if2("./ofs", ios::ate); // 打开时文件指针在文件尾
	if (!if2){ 
		cout << "if2流对象状态错误--打开文件失败" << endl; 
	}
	int a; double b; string c;
	if2 >> a >> b >> c;
	if (!if2){
	cout << "if2流对象状态错误--if2文件读取失败" << endl;
	cerr << "if2是0状态吗?" << if2.good() << ",if2是1状态吗?" << if2.bad() << ",if2是2状态吗?" << if2.eof() << ",if2是4转态吗?" << if2.fail() << endl;
	// 获取当前文件的状态
	cout <<"if2的状态码是"<< if2.rdstate() << endl;
	}
	cout << a << b << c;
	if2.close();
	return 0;
}

3.4 I/O流状态成员函数

在这里插入图片描述

注意当以ate模式打开的文件时,如果需要读取里面的内容,需要移动读取指针的位置,以保证能读取到数据

int main(){
	ifstream if2("./ofs", ios::ate); // 打开时文件指针在文件尾 
	if (!if2){ 
		cout << "if2流对象状态错误--打开文件失败" << endl;
	}
	cout << if2.rdstate()<<endl;// 读取文件的状态
	if2.seekg(0, ios::beg);// 移动读指针到文件的第一个字节
	int a; double b; string c;
	if2 >> a >> b >> c;
	if (!if2){
		cout << "if2文件读取失败" << endl;
	}
	cout << a << b << c;
	if2.close();
	return 0;
}

4、非格式化I/O

  • 写入字符
    • 函数:ostream & ostream::put(char ch);
      • 一次向输出流写入一个字符,返回流本身
  • 刷输出流
    • 函数:ostream & ostream::flush(void);
      • 将输出流缓冲区中的数据刷到设备上,返回流本身
int main(){
	ofstream of2("./out", ios::out); //
	if (!of2){ 
		cerr << "of2流对象状态错误--打开文件失败" << endl; 
	}
	for (char c = ' '; c <= '~'; c++){
		of2.put(c).flush();// 向输出流中写入一个字符,并刷新缓冲区
	}
	of2.close();
	return 0;
}
  • 读取字符
    • 无参get函数:int istream::get(void);
      • 成功返回读到的字符,失败或遇到文件尾则返回EOF
    • 单参get函数:istream& istream::get(char& ch);
      • 返回输入流本身,其在布尔上下文中的值,成功尾true,失败或遇到文件结尾为false
int main(){
	ifstream ifs("./out", ios::in);
	if (!ifs){ cerr << "ifs流对象状态错误--打开文件失败" << endl; }
	char c;
	// 单参get
	while (1){
		ifs.get(c);
		if (!ifs){
			cout << endl;
			cout << "文件读取完成" << endl;
			break;
		}
		else{
			cout << c;
		}
	}
	ifs.clear();// 清除文件状态
	ifs.seekg(0, ios::beg);
	// 无参get
	while (1){
		c = ifs.get();
		if (c == EOF){
			cout << endl;
			cout << "文件读取完成" << endl;
			break;
		}
		else{
			cout << c;
		}
	}
	ifs.close();
}
  • 读取行
    • 函数:istream& istream::getline(char* buffer,streamsize num,char delim=‘\n’);
      • 读取字符(至定界符)到buffer中
      • 一旦读取了num个字符还未读取定界符,第num个字符设置为’\0’,返回(输入流对象状态为4)
      • 如果因为遇到定界符(缺省为’\0’)则返回定界符会被读取并丢弃,并在末尾追加结尾空字符’\0’,而此时输入流对象状态为0,,读指针停在该定界符的下一个位置。
      • 遇到文件尾,直接返回,此时的输入流对象状态为6
    • 注意:使用getline函数的时候需要注意,在读取定界符是会将定界符替换为’\0’,或者是将第num个字符替换为’\0’,这么操作修改了源数据
int main(){
			ifstream ifs1("./getline", ios::in);
			if (!ifs1){ cerr << "ifs流对象状态错误--打开文件失败" << endl; }
			while (1){
				char buff[256];
				ifs1.getline(buff, 256, '\n');// 如果因为遇到定界符(缺省为‘\n’)返回(输入流对象状态为0)定界符被读取并丢弃,追加结尾空字符‘\0’,读指针停在该定界符的下一个位置。
				if (ifs1){
					cout << ifs1.rdstate() << "数据:" << buff;// 因为缺省值为‘\n’,所以最终输出出来的数据没有换行,这是因为定界符被读取并丢弃l,追加结尾空字符‘\0’导致的
				}
				else{
					break;
				}
			}
			ifs1.close();
			return 0;
		}

5、二进制I/O

  • 读取二进制数据
    • 函数:istream& istream::read (char* buffer,streamsize num);
      • 从输入流中读取num个字节到缓冲区buffer中
      • 返回流对象本身,其在布尔上下文中的值,成功(读满)为true,失败(没读满)为false
      • 如果没读满num个字节,函数就返回了,比如遇到文件尾,最后一次读到缓冲区buffer中的字节数,可以通过istream::gcount()函数获得
  • 获取读长度
    • 函数:streamsize istream::gcount (void);
      • 返回最后一次从输入流中读取的字节数
  • 写入二进制数据
    • 函数ostream& ostream::write (const char* buffer,streamsize num);
      • 将缓冲区buffer中的num个字节写入到输出流中
      • 返回流本身,其在布尔上下文中的值,成功(写满)为true,失败(没写满)为false
int main(){
	ifstream ifs("./getline", ios::in);
	ofstream ofs("./outwrite", ios::out);
	if (!ifs){ cerr << "ifs流对象状态错误--打开文件失败" << endl; }
	if (!ofs){ cerr << "ofs流对象状态错误--打开文件失败" << endl; }
	char buff[3];
	while (1){
		ifs.read(buff, 3);//从输入流中读取num个字节到缓冲区buffer中,返回流对象本身,其在布尔上下文中的值,成功(读满)为true,失败(没读满)为false
		if (ifs){// 意味着读满了3个字节
			ofs.write(buff, 3);
		}
		else{ // 没读满3字节
				int count = ifs.gcount();// 获取最后一次读取到的字节数
				ofs.write(buff, count);
				break;
			}
		}
		ifs.close();
		ofs.close();
		return 0;
	}

6、读写指针与随机访问

  • 设置读/写指针位置
    • 函数:istream& istream::seekg (off_type offset,ios::seekdir origin);// 设置读指针
    • ostream& ostream::seekp (off_type offset,ios::seekdir origin);// 设置写指针
      • origin表示偏移量offset的起点有下面三种表示方式
        • ios::beg:从文件的第一个字节
        • ios::cur:从文件的当前位置
        • ios::end:从文件最后一个字节的下一个位置
      • offset表示距起点位置的偏移量,为负/正表示向文件头/尾的方向偏移
      • 当读/写指针被移到文件头之前或文件尾之后,则失败
  • 获取读/写指针位置
    • 函数:pos_type istream::tellg (void);// 返回读指针的位置
    • pos_type ostream::tellp (void);// 返回写写指针的位置
      • 返回读/写指针当前位置相对于文件头的字节偏移量
int main(){
	ifstream ifs("./getline", ios::ate);// 将文件读指针设置到文件尾
	ofstream ofs("./zpyl", ios::out);
	if (!ifs){ cerr << "ifs流对象状态错误--打开文件失败" << endl; }
	if (!ofs){ cerr << "ofs流对象状态错误--打开文件失败" << endl; }
	int size = ifs.tellg();// 文件大小
	char buff[size];
	ifs.clear();
	ifs.seekg(0, ios::beg);
	ifs.read(buff, size);
	ofs.write(buff, size);
	ifs.close();
	ofs.close();
	return 0;
}

\\\\\\\\\ 一下内容只做了解 \\\\\\\\\\\\\\\\\

7、字符串流

  • 输出字符串流
    • 将不同类型的数据组合成一个字符串
# include <sstream>
int main(){
	ostringstream oss;
	oss << 1234 << " " << 56.78 << " " << "ABCD";
	string str = oss.str();
	cout << str <<endl;
	return 0;
}
  • 输入字符串流
    • 将一段字符串中的数据,分别取出来
#include <sstream>
int main(){
	string is("1234 56.78 ABCD");
	istringstream iss(is);
	int i;
	double b;
	string c;
	iss >> i >> b >> c;
	cout << i << c << b << endl;
	return 0;
}

8、格式化I/O

8.1 简介

  • 流函数(一组成员函数)
    • I/O流类(ios)定义了一组用于控制输入输出格式的公有成员函数,调用这些函数可以改变I/O流对象内部的格式状态,进而影响后续输入输出的格式化方式
  • 流控制符(一组全局函数)
    • 标准库提供了一组特殊的全局函数,它们有的带有参数(在iomanip头文件中声明),有的不带参数(在iostream头文件中声明)
    • 因其可被直接嵌入到输入输出表达式中,影响后续输入输出格式,故形象称之为流控制符

8.2 I/O流格式化函数

在这里插入图片描述

  • 一般而言,对I/O流格式的改变都是持久的,即只要不再设置新格式,当前格式将始终保持下去
  • 显示域宽是个例外,通过ios::width(int)所设置的显示域宽,只影响紧随其后的第一次输出,再往后的输出又恢复到默认状态

8.3 I/O流格式标志

在这里插入图片描述

  • 例子
    在这里插入图片描述

8.4 I/O流格式化控制符

在这里插入图片描述

  • 例子
    在这里插入图片描述

网站公告

今日签到

点亮在社区的每一天
去签到