C++的IO流

IO是 Input与Output

输入输出:针对终端而言的

键盘,鼠标用来输入数据的

显示数据时,在屏幕(显示器)

磁盘 存储数据

unix哲学:一切皆文件 抽象成文件描述符

IO流

输入输出流针对于程序而言,针对于内存

数据从其他地方保存到内存中,称为输入流

数据从内存保存到其他地方,成为输出流

流的状态

流都有四种状态:

1.goodbit 有效状态

2 .badbit 系统级别的错误,不可恢复

3.failbit 可以恢复的错误

4.eofbit 到了流的末尾

当流处于有效状态时,才可以正常使用

bool good() const;

bool bad()const;

bool fail()const;

bool eof()const;

标准输入输出流:

Code:

cpp 复制代码
#include<iostream>
#include<string>
using std::cout;
using std::cin;
using std::endl;
using std::string;
using std::istream;//通用输入流

void printStreamStatus(istream &is)
{
	cout << "it's goodbit: " << is.good() << endl;
	cout << "it's badbit: " << is.bad() << endl;
	cout << "it's eofbit: " << is.eof() << endl;
	cout << "it's failbit: " << is.fail() << endl;
}

void test0()
{
	int number = 0;
	printStreamStatus(cin);
	cin >> number;
	cout << "number: " << number << endl;
	printStreamStatus(cin);
	//流的状态异常,会导致无法输入

	cin.clear();//恢复流的状态
	//清空缓冲区
	cin.ignore(1024, '\n');
	string line;
	cin >> line;
	cout << "line: " << line << endl;

}
int main()
{
	test0();
	return 0;
}

对于cin这个istrem对象,在输入时,我们可以检测cin对象是否异常来判断输入是否正确,然后恢复状态,清空缓冲区。

关于ignore函数的声明:

basic_istream& ignore(std::streamsize count=1,int_type delim=Traits::eof());

返回值时一个istream的引用,第一个参数是要舍弃的字节数,第二个是分隔符,默认值是流的末尾

作用就是:作用:丢弃掉count个字节的数据,直到遇到分隔符为止

示例:健壮的读取一个整形

Code:

cpp 复制代码
void ReadInteger(istream &is,int &number)
{
	cout << "Please input a interger: " << endl;
	while (is >> number, !is.eof())
	{
		if (is.bad())
		{
			cout << "istream has broken!" << endl;
			return;
		}
		else if (is.fail())
		{
			is.clear();
			is.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
			cout << "Please input a interger: " << endl;
		}
		else
		{
			cout << "number: " << number << endl;
			break;
		}
	}
}

缓冲区是什么?

缓冲区(Buffer)是内存中的一块临时存储区域,用于暂存数据,减少频繁的I/O操作。

复制代码
// 比喻:缓冲区就像快递的中转站
// 发送方(程序) -> 中转站(缓冲区) -> 接收方(屏幕/键盘)

2. cout的缓冲区 vs cin的缓冲区

cout的缓冲区(输出缓冲区)

cpp

复制代码
// 程序输出数据时:
程序数据 → cout缓冲区 → 操作系统 → 屏幕控制台

// 示例:
cout << "Hello";  // 数据先进入输出缓冲区
// 此时屏幕上可能还看不到"Hello"

cin的缓冲区(输入缓冲区)

复制代码
// 用户输入数据时:
键盘输入 → 操作系统 → cin缓冲区 → 程序读取

// 示例:
int num;
cin >> num;  // 从输入缓冲区读取数据

Code:

cpp 复制代码
#include<iostream>
#include<string>
#include<Windows.h>
using std::cout;
using std::cin;
using std::endl;
using std::string;
using std::istream;//通用输入流

void test0()
{
	for (int i = 0; i < 4097; i++)
	{
		cout << 'a';
	}
    
	Sleep(3000);
}
int main()
{
	test0();
	return 0;
}

成因介绍:

详细的执行过程分析

第1-4096个字符:

  1. 循环输出第1到第4096个字符'a'

  2. 每个字符占用1字节,共4096字节

  3. 当输出第4096个字符时,缓冲区正好填满

  4. 缓冲区满触发自动刷新

  5. 结果:您看到屏幕上立即显示出4096个'a'

第4097个字符:

  1. 当程序试图输出第4097个字符时,发现缓冲区已满

  2. 系统自动执行:

    • 刷新当前缓冲区(4096个'a'已显示)

    • 清空缓冲区

    • 将第4097个字符'a'放入现在空了的缓冲区

  3. 结果:第4097个字符进入缓冲区,但未显示

Sleep(3000):

  1. 程序暂停3秒

  2. 此时缓冲区中只有1个字符'a'

  3. 缓冲区未满(只有1/4096),不会触发自动刷新

  4. 结果:屏幕上没有新输出,保持4096个'a'的显示状态

程序结束(return 0):

  1. main()函数返回

  2. 程序正常退出

  3. 退出前自动刷新所有标准流的缓冲区

  4. 结果:缓冲区中仅存的1个字符'a'被刷新显示到屏幕

可用cerr输出用于调试,输出内容不会到缓冲区,而是直接输出。

文件IO

文件输入流 ifstream

std::basic_ifstream<CharT,Traits>::basic_ifstream

几种构造函数:

|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----|-----------|
| basic_ifstream(); | (1) | |
| explicit basic_ifstream( const char* filename, std::ios_base::openmode mode = std::ios_base::in ); | (2) | |
| explicit basic_ifstream( const std::filesystem::path::value_type* filename, std::ios_base::openmode mode = std::ios_base::in ); | (3) | (C++17 起) |
| explicit basic_ifstream( const std::string& filename, std::ios_base::openmode mode = std::ios_base::in ); | (4) | (C++11 起) |
| template< class FsPath > explicit basic_ifstream( const FsPath& filename, std::ios_base::openmode mode = std::ios_base::in ); | (5) | (C++17 起) |
| basic_ifstream( basic_ifstream&& other ); | (6) | (C++11 起) |
| basic_ifstream( const basic_ifstream& rhs ) = delete; |

explicit basic_ifstream( const char* filename, std::ios_base::openmode mode= std::ios_base::in );

第一个参数是文件名,第二个操作是文件的打开模式

指定可用的文件打开标志。它是一个 BitmaskType,定义了以下常量

|-----------------------|-------------------------------------------------------------------------------------|
| 常量 | 解释 |
| app | 每次写入前定位到流的末尾 |
| 二进制 | 以 二进制模式 打开 |
| in | 为读取而打开 |
| out | 为写入而打开 |
| trunc | 打开时丢弃流的内容 |
| ate | 打开后立即定位到流的末尾 |
| noreplace (C++23) | 以独占模式打开 |

app针对文件输出流,ate针对输入流

basic_ifstream( const basic_ifstream& rhs ) = delete;

表明拷贝构造被删除,文件输入流的拷贝构造被删除

几种成员函数:

成员函数

|--------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------|
| (构造函数) | 构造文件流 (public member function) |
| (析构函数) [virtual](隐式声明) | 销毁 basic_ifstream 和关联的缓冲区,关闭文件 (virtual public member function) |
| operator= (C++11) | 移动文件流 (public member function) |
| swap (C++11) | 交换两个文件流 (public member function) |
| rdbuf | 返回底层原始文件设备对象 (public member function) |
| native_handle (C++26) | 返回底层实现定义的句柄 (public member function) |
| ###### 文件操作 ||
| is_open | 检查流是否关联了文件 (public member function) |
| open | 打开文件并将其与流关联 (public member function) |
| close | 关闭关联的文件 (public member function) |

读取一行

|----------------------------------------------------------------------------------------------------------------------------------------------------|-----|---|
| basic_istream& getline( char_type* s, std::streamsize count ); | (1) | |
| basic_istream& getline( char_type* s, std::streamsize count, char_type delim ); | (2) |

标准库里的getline

template< class CharT, class Traits, class Allocator >

std::basic_istream<CharT, Traits>&

getline( std::basic_istream<CharT, Traits>& input,

std::basic_string<CharT, Traits, Allocator>& str, CharT delim );

第一个参数是流对象,第二个是字符串,第三个是分隔符

Code:

cpp 复制代码
#include<iostream>
#include<fstream>
#include<string>
#include<Windows.h>
#include<vector>
using std::cout;
using std::cin;
using std::endl;
using std::cerr;
using std::string;
using std::istream;//通用输入流
using std::ifstream;

void test0()
{
	//文件输入流要求文件必须存在
	ifstream ifs;
	ifs.open("ifstream.txt");
	if (!ifs.good())
	{
		cout << "iftream open file error!" << endl;
		return;
	}
	//string word;
	////>>输入流运算符以空格\t \n作为分格符
	//while (ifs >> word)
	//{
	//	cout << word;
	//}

	//每次读取一行数据
	//成员函数方式
	//char buff[1024] = { 0 };
	//while (ifs.getline(buff, 1024))
	//{
	//	cout << buff << endl;
	//}

	//std标准库方式
	std::vector<string>file;
	file.reserve(20);
	string line;
	while (std::getline(ifs, line))
	{
		cout << line << endl;
		file.push_back(line);
	}
	
	//for (auto& line : file)cout << line << endl;

	ifs.close();//切记关闭流对象
	
}
int main()
{
	test0();
	return 0;
}

读取整个文件

std::basic_istream<CharT,Traits>::read

std::basic_istream

|--------------------------------------------------------------------------------------------------------------------------------|---|---|
| basic_istream& read( char_type* s, std::streamsize count ); | | |
| | | |

从流中提取字符。

行为类似于 UnformattedInputFunction。构造并检查哨兵对象后,提取字符并将其存储到字符数组的连续位置,该字符数组的第一个元素由 s 指向。字符的提取和存储会一直持续到满足以下任一条件:

  • count 个字符已被提取和存储。
  • 输入序列上发生文件尾条件(在这种情况下,调用 setstate(failbit|eofbit))。可以使用 gcount() 查询成功提取的字符数。

std::basic_istream<CharT,Traits>::seekg

std::basic_istream

|--------------------------------------------------------------------------------------------------------------------------------------------------|-----|---|
| basic_istream& seekg( pos_type pos ); | (1) | |
| basic_istream& seekg( off_type off, std::ios_base::seekdir dir ); | (2) | |
| | | |

设置当前关联的 streambuf 对象的输入位置指示器。

|-----------------------------------|-----------|
| 在执行任何其他操作之前,seekg 会清除 eofbit。 | (C++11 起) |

seekg 的行为类似于 非格式化输入函数,除了 gcount() 不受影响。在构造并检查哨兵对象后,

  1. 如果 fail() != true,将输入位置指示器设置为绝对值(相对于文件开头)pos。具体来说,执行 rdbuf()->pubseekpos(pos, std::ios_base::in)(pubseekpos 又会调用特定缓冲区的 seekpos,例如 basic_filebuf::seekposbasic_stringbuf::seekposstrstreambuf::seekpos)。如果失败,则调用 setstate(std::ios_base::failbit)。

  2. 如果 fail() != true,将输入位置指示器设置为相对于 dir 定义的位置的 off 位置。具体来说,执行 rdbuf()->pubseekoff(off, dir, std::ios_base::in)。如果失败,则调用 setstate(std::ios_base::failbit)。

参数

|-----|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| pos | - | 要设置输入位置指示器的绝对位置 |
| off | - | 要设置输入位置指示器的相对位置(正或负) |
| dir | - | 定义应用相对偏移量的基本位置。它可以是以下常量之一 |----------------------------------------------------------------|-------------| | 常量 | 解释 | | beg | 流的开始 | | end | 流的结束 | | cur | 流位置指示器的当前位置 | |

std::basic_istream<CharT,Traits>::tellg

std::basic_istream

|-------------------|---|---|
| pos_type tellg(); | | |
| | | |

返回当前关联的 streambuf 对象的输入位置指示符。

行为类似于 UnformattedInputFunction,除了 gcount() 不受影响。构造并检查守卫对象后,如果 fail() == true,则返回 pos_type(-1)。否则,返回 rdbuf()->pubseekoff(0, std::ios_base::cur, std::ios_base::in)。

Code:

cpp 复制代码
#include<iostream>
#include<fstream>
#include<string>
#include<Windows.h>
#include<vector>
using std::cout;
using std::cin;
using std::endl;
using std::cerr;
using std::string;
using std::istream;//通用输入流
using std::ifstream;

void test0()
{
	//文件输入流要求文件必须存在
	ifstream ifs("ifstream.txt",std::ios::binary);
	ifs.seekg(0, std::ios::end);
	long len = ifs.tellg();
	cout << "length: " << len << endl;
	char* pbuff = new char[len + 1];
	//读取整个文件的内容
	ifs.seekg(0);
	ifs.read(pbuff, len);
	pbuff[len] = '\0';
	cout << pbuff << endl;
	delete[] pbuff;
	ifs.close();//切记关闭流对象
}
int main()
{
	test0();
	return 0;
}

输出流:

std::basic_ofstream

std::basic_ofstream

|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---|---|
| 定义于头文件 <fstream> | | |
| template< class CharT, class Traits = std::char_traits<CharT> > class basic_ofstream : public std::basic_ostream<CharT, Traits> | |

std::basic_ofstream<CharT,Traits>::basic_ofstream

std::basic_ofstream

|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----|-----------|
| basic_ofstream(); | (1) | |
| explicit basic_ofstream( const char* filename, std::ios_base::openmode mode = std::ios_base::out ); | (2) | |
| explicit basic_ofstream( const std::filesystem::path::value_type* filename, std::ios_base::openmode mode = std::ios_base::out ); | (3) | (C++17 起) |
| explicit basic_ofstream( const std::string& filename, std::ios_base::openmode mode = std::ios_base::out ); | (4) | (C++11 起) |
| template< class FsPath > explicit basic_ofstream( const FsPath& filename, std::ios_base::openmode mode = std::ios_base::out ); | (5) | (C++17 起) |
| basic_ofstream( basic_ofstream&& other ); | (6) | (C++11 起) |
| basic_ofstream( const basic_ofstream& rhs ) = delete; | (7) | (C++11 起) |
| | | |

构造新的文件流。

  1. 默认构造函数:构造一个未关联文件的流:默认构造 std::basic_filebuf,并使用指向此默认构造的 std::basic_filebuf 成员的指针构造基类。

2,3) 首先,执行与默认构造函数相同的步骤,然后通过调用 rdbuf()->open(filename, mode | std::ios_base::out) 将流与文件关联(有关该调用的效果的详细信息,请参阅 std::basic_filebuf::open)。如果 open() 调用返回空指针,则设置 setstate(failbit)。重载 (3) 仅在 std::filesystem::path::value_type 不是 char 时提供。(C++17 起)

4,5) 与 basic_ofstream(filename.c_str(), mode) 相同。(5) 仅当 FsPathstd::filesystem::path 时才参与重载决议。(C++17 起) 注意,尽管默认模式是 out,但效果与 std::filebuf::open 中描述的 out | trunc 的效果相同。

  1. 移动构造函数。首先,从 other 移动构造基类(不影响 rdbuf() 指针),然后移动构造 std::basic_filebuf 成员,然后调用 this->set_rdbuf() 以将新的 basic_filebuf 安装为基类中的 rdbuf() 指针。

  2. 复制构造函数被删除:此类不可复制

Code:

cpp 复制代码
#include<iostream>
#include<fstream>
#include<string>
#include<Windows.h>
#include<vector>
using std::cout;
using std::cin;
using std::endl;
using std::cerr;
using std::string;
using std::istream;//通用输入流
using std::ifstream;
using std::ofstream;

string file;
void test0()
{
	//文件输入流要求文件必须存在
	ifstream ifs("ifstream.txt",std::ios::binary);
	ifs.seekg(0, std::ios::end);
	long len = ifs.tellg();
	cout << "length: " << len << endl;
	char* pbuff = new char[len + 1];
	//读取整个文件的内容
	ifs.seekg(0);
	ifs.read(pbuff, len);
	pbuff[len] = '\0';
	file = pbuff;
	delete[] pbuff;
	ifs.close();//切记关闭流对象
}

void test1()
{
	///string filename("ofstream.txt");
	//文件输出流没有文件直接创建一个新文件
	//ofstream ofs(filename);
	////默认out模式,当文件存在时直接清空内容
	//if (!ofs)
	//{
	//	cout << "ofstream open file error" << endl;
	//	return;
	//}
	////ofs << file;

	//在文件末尾追加
	ofstream ofs("ifstream.txt", std::ios::app);
	string text = "\n你好\n";
	ofs << text;
	ofs.close();
}
int main()
{
	test0();
	test1();
	return 0;
}

fstream

cpp 复制代码
#include<iostream>
#include<string>
#include<Windows.h>
#include<fstream>
using std::cout;
using std::cin;
using std::endl;
using std::string;
using std::istream;//通用输入流
using std::fstream;

void test0()
{
	//文件输入输出流要求文件必须存在
	fstream fs("fstream.txt");
	if (!fs)
	{
		cout << "fstream open file error!" << endl;
		return;
	}
	int number = 0;
	for (int i = 0; i < 5; i++)
	{
		cin >> number;
		fs << number << " ";
	}
	//要想正确读取,必须偏移位置
	fs.seekg(0);
	for (int i = 0; i < 5; i++)
	{
		fs >> number;
		cout << number << endl;
	}
	cout << endl;
}
int main()
{
	test0();
	return 0;
}

字符串的输入输出流

作用:在字符串与其他类型之间做类型(格式)转换

三种类型

istringstream

ostringstream

stringstream

std::basic_istringstream

|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---|---|
| 定义于头文件 <sstream> | | |
| template< class CharT, class Traits = std::char_traits<CharT>, class Allocator = std::allocator<CharT> > class basic_istringstream : public basic_istream<CharT, Traits>; | | |
| | | |

类模板 std::basic_istringstream 对基于字符串的流实现输入操作。它有效地存储 std::basic_string 的实例并对其执行输入操作。

在底层,该类实际上将 std::basic_stringbuf 的原始字符串设备实现封装到 std::basic_istream 的高级接口中。提供了 std::basic_stringbuf 唯一成员的完整接口。

常见构造:

explicit basic_istringstream

( const std::basic_string<CharT, Traits, Allocator>& str,

std::ios_base::openmode mode =

std::ios_base::in );

std::basic_ostringstream

|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---|---|
| 定义于头文件 <sstream> | | |
| template< class CharT, class Traits = std::char_traits<CharT>, class Allocator = std::allocator<CharT> > class basic_ostringstream : public basic_ostream<CharT, Traits>; | | |
| | | |

类模板 std::basic_ostringstream 实现基于字符串流的输出操作。它有效地存储 std::basic_string 的实例并对其执行输出操作。

在底层,该类主要将 std::basic_stringbuf 的原始字符串设备实现封装到 std::basic_ostream 的高级接口中。提供了指向独有 std::basic_stringbuf 成员的完整接口。

常见构造:

explicit basic_ostringstream

( const std::basic_string<CharT, Traits, Allocator>& str,

std::ios_base::openmode mode =

std::ios_base::out );

cpp 复制代码
#include<iostream>
#include<string>
#include<Windows.h>
#include<fstream>
#include<sstream>
using std::cout;
using std::cin;
using std::endl;
using std::string;
using std::istream;//通用输入流
using std::fstream;
using std::istringstream;
using std::ostringstream;
using std::ifstream;

void test0()
{
	//将其它类型转换为字符串
	int num1 = 512;
	int num2 = 1024;
	ostringstream oss;
	oss << "num1= " << num1 << " num2= " << num2;
	//cout << oss.str() << endl;
	string str = oss.str();
	cout << str << endl;
	
	//将字符串类型转换为其他类型
	string key;
	int number;
	istringstream iss(str);
	//输入流运算符是以\t \n为分隔符
	while (iss >> key >> number)
	{
		cout << key << " " << number << endl;
	}
}

//将整形转换为字符串
string int2str(int number)
{
	ostringstream oss;
	oss << number;
	return oss.str();
}

//读取配置文件
void readConfigruation(const string& filename)
{
	ifstream ifs(filename);
	if (!ifs)
	{
		cout << "ifstream open file " << filename << " error" << endl;
		return;
	}
	string line;
	string key, val;
	while (std::getline(ifs, line))
	{
		istringstream iss(line);
		iss >> key >> val;
		cout << key << " -> " << val << endl;
	}
}

int main()
{
	test0();
	return 0;
}
相关推荐
一碗绿豆汤16 小时前
Java语言概述和开发环境-1
java·开发语言
愈努力俞幸运16 小时前
rust安装
开发语言·后端·rust
天天进步201517 小时前
【Nanobrowser源码分析4】交互篇: 从指令到动作:模拟点击、滚动与输入的底层实现
开发语言·javascript·ecmascript
console.log('npc')17 小时前
vue2中子组件父组件的修改参数
开发语言·前端·javascript
码点17 小时前
【无标题】日文字库Japan.ini
开发语言
IT=>小脑虎17 小时前
2026版 Python零基础小白学习知识点【基础版详解】
开发语言·python·学习
wjs202417 小时前
抽象工厂模式
开发语言
lly20240617 小时前
SVG 模糊效果详解
开发语言
期待のcode17 小时前
Java虚拟机类加载机制
java·开发语言