C++_chapter10_C++IO流类库
本章记录IO流类库
第 10 章 IO流类库
10.1 IO流基本认识
10.1.1 什么是I/O:标准IO 文件IO 串IO
数据的输入和输出(input/output简写为I/O);
对标准输入设备和标准输出设备的输入输出简称为标准I/O;
对在外存磁盘上文件的输入输出简称为文件I/O;
对内存中指定的字符串存储空间的输入输出简称为串I/O;
10.1.2 什么是流
数据输入输出的过程,可以形象地看成流。
从流中获取数据的操作称为“提取”(输入)操作,cin >> n; 箭头指向n,所以为输入 。
向流中添加数据的操作称为“插入”(输出)操作 ,cout << n, 箭头指向输出,所以为输出。
- 标准输入输出流;
- 文件流
- 字符串流
10.1.3 流类库继承体系

- 流库具有两个平行的基类:streambuf 和 ios 类,所有流类均以两者之一作为基类。
- streambuf 类提供对缓冲区的低级操作:设置缓冲区、对缓冲区指针操作、向缓冲区存/取字符;
- ios_base、ios 类记录流状态,支持对streambuf 的缓冲区输入/输出的格式化或非格式化转换;
- strstreambuf:使用串保存字符序列。扩展 streambuf 在缓冲区提取和插入的管理;
- filebuf:使用文件保存字符序列。包括打开文件;读/写、查找字符;
可以重载ostream,cout, 以 友元方式重载。
10.1.4 标准输入/输出流
C++为用户进行标准I/O操作定义了四个类对象: cin,cout,cerr和clog
cin为istream流类的对象,代表标准输入设备键盘,后三个为ostream流类的对象;
cout 为 ostream 类对象。
cout代表标准输出设备显示器
cerr和clog含义相同,均代表错误信息输出设备显示器
10.1.4.1 ostream 流操作
主要重载了cout;
- operator << , 可以连续的输出,本质就是运算符重载。默认重载了编译器内置类型的操作。
- put( ),输出单个字符,返回一个ostream对象的引用
cout.put(‘H’).put(‘i’);
int main()
{
// cout.put();
cout.put('a').put('\t').put('b');
cout.put('a').put('n');
return 1;
}

3. write( )
输出缓冲区的操作,输出缓冲区长度。
write(buf, len);
write( )返回一个ostream对象的引用;
cout.write (buf, len); //char buf[len];
要用输出缓冲区,可以连续输出。
void test_write()
{
char p[] = "test write()";
cout << sizeof(p) << endl; // 13
// cout.write(p,sizeof(p)-1).put('c'); // test write()c
cout.write(p, sizeof(p)).put('c'); // test write()c
}
10.1.4.2 istream流的操作
opeartor>>操作
get( )
getline( )
read( )
peek( )
putback( )
1 >>操作
>>的连续使用;连续时候时候,用空格分割。
为什么可以连续使用>>操作?
>>的返回值
操作返回一个istream对象的引用
操作的重载功能
void test_cin()
{
int n;
char c;
cin >> n >> c;
cout << "n = " << n << " c= " << c << endl;
}

2 cin.get() 操作
- 读取单个字符, 返回一个整数;

- get(char&)操作
void test_get()
{
char ch1;
char ch2;
cin.get(ch1).get(ch2);
cout << ch1 << "" << ch2 << endl;
}

- get对回车换行的处理


void test_get()
{
int n = cin.get();
cout << n << endl;
cin.get(); // 读取缓冲区中的回车。
char ch1;
char ch2;
cin.get(ch1).get(ch2);
cout << ch1 << "" << ch2 << endl;
}
3 getline操作
读取一行,遇到回车键结束,返回istream对象的引用
getline会将字符串结束符算符getline总长度中;所以长度为6的buf,最多接受5个字符,最后再加上字符串结束标志。


getline()操作与>>的区别:
cin >> 遇到空格会结束。
cin.getline(buf,len),直到遇到回车才结束。
4 cin.read()操作
read(buf, len)
返回一个istream对象的引用,对空白字符照读不误

【读取指定数量的字符,最后不算入字符串结束符的数量】
read与getline区别是,read读len个字符;getline读len-1个字符。
void test_read()
{
char buf[6] {0};
cin.read(buf, 6);
cout << buf << endl;
}
5 peek与putback
peek:查看而不读取
putback:将一个字符添加到流
void test_putback()
{
char c[10], c2, c3;
c2 = cin.get();
c3 = cin.get();
cin.putback(c2); // 放入缓冲区的第一个位置;
cin.getline(c,10);
cout << c << endl;
}

10.2 文件IO
10.2.1 文件流
- ofstream,由ostream派生而来,用于写文件;
- ifstream,由istream派生而来, 用于读文件;
- fstream,由iostream派生而来,用于读写文件;
10.2.2 打开文件

可使用函数open()打开文件。文件的打开即是在流与文件之间建立一个连接。
函数原型:
void open(const char * filename, int mode = ios::out,int prot = _SH_DENYNO);
fstream第二个默认参数是out。
filename:文件的名称,可以包含(绝对和相对)路径
mode:文件打开模式
prot:保护模式
文件打开 方式:
ios_out 和 out 相同,是一个枚举类型。
10.2.2.1保护模式
这里保护的是当前进程打开的文件,拒绝其他进程。
#define _SH_DENYRW 0x10 /* deny read/write mode */拒绝其他进程对文件进行读写
#define _SH_DENYWR 0x20 /* deny write mode */拒绝写入文件
#define _SH_DENYRD 0x30 /* deny read mode */拒绝文件的读取权限
#define _SH_DENYNO 0x40 /* deny none mode */读取和写入许可
#define _SH_SECURE 0x80 /* secure mode */共享读取,独占
10.2.2.2 文件打开的五几种方式
推荐使用第五种断言方式打开文件。
void test_open()
{
ofstream fout("test.txt", ios::out | ios::app);
// 第一种:
if (fout.is_open())
{
cout << "succ" << endl;
}
// 第二种
if (fout.good())
{
cout << "succ" << endl;
}
// 第三种
if (fout)
{
cout << "succ" << endl;
}
// 第四种
if (!fout)
{
cout << "failed " << endl;
}
// 第五种:断言方式
assert(fout);
}
10.2.2.3 打开文件几点说明
文件打开也可以通过构造函数打开,例如:
ofstream fout(“out.txt“,ios::out);
- 文件的打开方式可以为上述的一个枚举常量,也可以为多个枚举常量构成的按位或表达式。
- 使用ofstream的open成员函数打开一个文件时,若由字符指针参数所指定的文件不存在,则建立该文件。 但是如果是ifstream打开文件,如果文件不存在,则打开文件失败,出现断言。
- 当打开方式中不含有ios::ate或ios::app选项时,则文件指针被自动移到文件的开始位置,即字节地址为0的位置。
- 从效果上看ofstream指定out模式等同于指定了out和trunc模式;
ofstream fout("test.txt", ios::out );
直接指定out模式,文件会被清空。但是加上 app后,不会被清空。
ofstream fout("test.txt", ios::out | ios::app);
默认情况下,fstream对象以in和out模式同时打开。
当文件同时以in和out打开时不会清空;
如果只使用out模式,而不指定in模式,则文件会清空现有数据。
如果同时指定了out与app,不会清空;
ofstream fout("test.txt", ios::out | ios::app);
如果打开文件时指定了trunc模式,则无论是否同时指定了in模式,文件同样会被清空。
10.2.2.4文件打开模式有效组合

上述所有的打开模式组合还可以添加ate模式。对这些模式添加ate模只会改变文件打开时的初始定位,在第一次读或写之前,将文件定位于文件末尾处。
good()方法,判断流状态是正常的;如果成功是正常的。
//ofstream fout1("test.txt", ios::out |ios::in | ios::ate); // 会覆盖掉之前的内容。
//ofstream fout2("test.txt", ios::out | ios::in | ios::ate);
ofstream fout1("test.txt", ios::out | ios::in | ios::app); // 会覆盖掉之前的内容。
ofstream fout2("test.txt", ios::out | ios::in | ios::app);
所以,之后使用时候,使用app方式,以追加的方式进行读写,并且之前内容不会被覆盖。多个进程或线程读写文件时候,使用app方式打开文件,这样可以同时向文件中写入,之前的内容不会被覆盖。
10.2.3 读写文件
void test_open()
{
//ofstream fout1("test.txt", ios::out |ios::in | ios::ate); // 会覆盖掉之前的内容。
//ofstream fout2("test.txt", ios::out | ios::in | ios::ate);
ofstream fout1("test.txt", ios::out | ios::in | ios::app); // 会覆盖掉之前的内容。
ofstream fout2("test.txt", ios::out | ios::in | ios::app);
// 第一种:
if (fout1.is_open())
{
cout << "succ" << endl;
}
// 第二种
if (fout1.good())
{
cout << "succ" << endl;
}
// 第三种
if (fout2)
{
cout << "succ" << endl;
}
// 第四种
if (!fout1)
{
cout << "failed " << endl;
}
// 第五种:断言方式
assert(fout2);
fout1 << "X";
fout2 << "Y";
}
10.2.4 流状态

对应于这个标志字各状态位,ios类还提供了以下成员函数来检测或设置流的状态:
bool rdstate(); //返回流的当前状态标志字
bool eof(); //返回非0值表示到达文件尾
bool fail(); //返回非0值表示操作失败
bool bad(); //返回非0值表示出现错误
bool good(); //返回非0值表示流操作正常
bool clear(int flag=0); //将流的状态设置为flag
为提高程序的可靠性,应在程序中检测I/O流的操作是否正常。当检测到流操作出现错误时,可以通过异常处理来解决问题。
void test_streamstat()
{
ofstream fout1("test.txt", ios::out | ios::trunc | ios::app);
if (fout1.fail())
{
cout << "fail" << endl; // 出现fail
}
fout1.clear();
fout1.open("test.txt");
if (fout1.good())
{
cout << "good" << endl; // 重新打开后,出现了good
}
}
上边代码,trunc 和 app 同时使用会出现错误。然后使用clear() 清空流状态,再次打开就可以正常使用了。
10.2.5 文件关闭
每个文件流类中都提供有一个关闭文件的成员函数close()
功能:当打开的文件操作结束后,就需要关闭它,使文件流与对应的物理文件断开联系,并能够保证最后输出到文件缓冲区中的内容,无论是否已满,都将立即写入到对应的物理文件中。
函数原型:void close();
文件流对应的文件被关闭后,还可以利用该文件流调用open成员函数打开其他的文件。
10.3 文件读写
文件读写
二进制文件的读写
文件随机读写tellp、tellg、seekp、seekg
10.3.1 文件读写方式
<<
get
put
read
write
文本模式打开与二进式模式打开区别
测试<< >>
按照先后顺序,先写入aaaa空格, 再写入255;读时也按照写的顺序进行读,不会出现问题,结果如下:
void test_readwrite()
{
ofstream fout("test2.txt", ios::app);
fout << "aaaa " << 2585;
fout.close();
ifstream fin("test2.txt");
string str;
fin >> str;
cout << str << endl;
int n;
fin >> n;
cout << n << endl;
}

可以看到,上边的读写操作,会自动跳过空格。将255前边的空格跳过,然后将255读入到n中。
但是如果先读fin>>n,再读fin>>str;就会报错。
put() get()读写测试
put get() 这两个函数都是单字符输入输出。
void test_putget()
{
ofstream fout("test2.txt");
assert(fout);
char ch;
for (int i = 0; i < 26; ++i)
{
ch = 'A' + i;
fout.put(ch);
}
fout.close();
ifstream fin("test2.txt");
while (fin.get(ch))
{
cout << ch;
}
cout << endl;
}

10.3.2 二进制文件读写
二进制文件不同于文本文件,它可用于任何类型的文件(包括文本文件)。
对二进制文件的读写可采从istream类继承下来的成员函数read()和从ostream类继承下来的成员函数write()。
文件默认是以文本方式打开。文件打开操作时使用枚举常量ios::binary,例如:
ofstream fout(“binary.dat”,ios::out | ios::binary)。
如果以文本方式打开文件,当进行文件写入时候,遇到\n会进行转换,在不同平台下转换方式如下:
Windows下转为\r\n; Linux下保持不变;mac下\n转为\r;
如果打开文件时候,不指定为ios::binary方式,则会以文本方式打开,在进行文件写入时候,遇到\n会被替换为\r\n;
文本方式打开
可以看到文本方式打开,文件会被替换为\n会被替换为:\r\n

二进制方式打开 ios::binary

10.3.3 write()成员函数
函数功能:以字节位单位向文件流中写入整块数据,最有价值的应用可以处理结构体变量和类对象。
函数原型:
ostream& write( const char* pch, int nCount );
函数参数:
pch 写入的数据的指针
nCount 写入数据的字节大小
10.3.4 read()成员函数
函数功能:从文件流中读出整块数据
函数原型:
istream& read( char* pch, int nCount );
函数参数:
pch 用来接收数据的指针
nCount 读取的字节数的大小
实验:read write读写普通变量
通过下面的实验可以看到,write 和 read都是以单字符读取文件的。一次读入或写一个块的数据。
void test_writeread()
{
ofstream fout("test4.txt", ios::out | ios::binary);
int a = 200;
fout.write(reinterpret_cast<char*>(&a), sizeof(a));
fout.close();
int b;
ifstream fin("test4.txt", ios::in | ios::binary);
fin.read(reinterpret_cast<char*>(&b), sizeof(b));
cout << b << endl;
}


实验:read write读写不填充就对齐的结构体
直接读写不填充就自动对齐的结构体。可以看到能够正常读取结构体。
struct C
{
int a1;
int a2;
};
void test_writeread2()
{
ofstream fout("test5.txt", ios::out | ios::binary);
C c1;
c1.a1 = 200;
c1.a2 = 300;
int size = sizeof(c1);
fout.write(reinterpret_cast<char*>(&c1), sizeof(C));
fout.close();
ifstream fin("test5.txt", ios::in | ios::binary);
C c2;
fin.read(reinterpret_cast<char*>(&c2), sizeof(c2));
cout << c2.a1 << "" << c2.a2 << endl;
}

实验:read write读写自动填充对齐的结构体
对于自动填充的结构体,read和write也能够自动读写正确。
struct C
{
int a1;
char a2;
};
void test_writeread2()
{
ofstream fout("test5.txt", ios::out | ios::binary);
C c1;
c1.a1 = 200;
c1.a2 = 't';
int size = sizeof(c1);
fout.write(reinterpret_cast<char*>(&c1), sizeof(C));
fout.close();
ifstream fin("test5.txt", ios::in | ios::binary);
C c2;
fin.read(reinterpret_cast<char*>(&c2), sizeof(c2));
cout << c2.a1 << "" << c2.a2 << endl;
}

实验:read write读写包含string类型的结构体
当结构体或者类中包含了string类型的机构体时候,使用如下方式进行读写:首先写入字符串长度,然后再写入字符串;读的是时候,先读长度,然后开辟内存,再读入字符串。
void test_writereadString()
{
Test t1;
int size = sizeof(Test);
int size2 = sizeof(string); // 28 string 大小是28
t1.str1 = "aaaaaaaaaaaaabbbbbbbbbbbbbbbbbbcccccccccccccccddddddd";
t1.str2 = "dddddddaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbcccccccccccccccddddddd";
ofstream fout("test6.txt", ios::out | ios::binary);
int len;
len = t1.str1.length();
fout.write((char*)&len, sizeof(int));
fout.write(t1.str1.data(), t1.str1.length());
len = t1.str2.length();
fout.write((char*)&len, sizeof(int));
fout.write(t1.str2.data(), t1.str2.length());
fout.close();
ifstream fin("test6.txt", ios::in | ios::binary);
Test t2;
fin.read((char*)&t2.a,sizeof(int));
t2.str1.resize(t2.a);
fin.read(&t2.str1[0], t2.a);
fin.read((char*)&t2.a, sizeof(int));
t2.str2.resize(t2.a);
fin.read(&t2.str2[0], t2.a);
cout << t2.a << " " << t2.str1 << "" << t2.str2 << endl;
}
10.3.5当前文件流活动指针
文件流指针用以跟踪发生 I/O 操作的位置。每当从流中读取或写入一个字符,当前活动指针就会向前移动;
当打开方式中不含有ios::ate或ios::app选项时,则文件指针被自动移到文件的开始位置,即字节地址为0的位置。
10.3.6 文件的随机读写-seekp和seekg
函数功能
seekp:设置输出文件流的文件流指针位置
seekg:设置输入文件流的文件流指针位置
函数原型:
ostream& seekp( streampos pos );
ostream& seekp( streamoff off, ios::seek_dir dir );
istream& seekg( streampos pos );
istream& seekg( streamoff off, ios::seek_dir dir );
函数参数
pos:新的文件流指针位置值
off:需要偏移的值
dir:搜索的起始位置
实验:seekg
void test_seekg()
{
ifstream fin("test7.txt"); // 如果文件不存在,会失败
assert(fin);
fin.seekg(2); // 从索引为2的位置开始读;
char ch;
fin.get(ch);
cout << ch << endl;
fin.seekg(-2, ios::end); // -1 表示最后一个;-2 表示倒数第二个。
fin.get(ch);
cout << ch << endl;
// 获取一个文件大小
fin.seekg(0, ios::end);
streampos pos = fin.tellg();
cout << pos << endl;
}
10.3.7 文件的随机读写-tellp和tellg
函数功能
tellp:获得输出的文件流指针的当前位置,以字节为单位
tellg:获得输入的文件流指针的当前位置,以字节为单位
函数原型:
streampos tellp();
streampos tellg();
函数返回值:实际上是一个long类型。
实验:获取文件大小
tellp 实际上是从文件的开始计算,到文件的当前位置,一共多少个字节。
// 获取一个文件大小
fin.seekg(0, ios::end);
streampos pos = fin.tellg();
cout << pos << endl;
10.4 字符串流
10.4.1 字符串流
istringstream,由istream派生而来, 提供读string的功能
ostringstream,由ostream派生而来,提供写string的功能
stringstream,由iostream派生而来,提供读写string的功能
10.4.2 istringstream
istringstream 提供了字符串流的输入输出功能。下面的程序,getline输出一行字符串,然后放入字符串流中,然后从流中逐行读取字符串。
istringstream默认以一个或者多个空格为间隔符号。
void test_string()
{
string line;
string word;
while (getline(cin, line))
{
istringstream iss(line); // 字符串流
while (iss >> word)
{
cout << word << "#";
}
cout << endl;
}
}

10.4.3 ostringstream
输入输出流用于字符串和double int 类型的转换;
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
void test_string()
{
string line;
string word;
while (getline(cin, line))
{
istringstream iss(line); // 字符串流
while (iss >> word)
{
cout << word << "#";
}
cout << endl;
}
}
string test_doubletostr(double val)
{
ostringstream oss;
oss << val;
return oss.str();
}
double strtodouble(const string& str)
{
istringstream iss(str);
double val;
iss >> val;
return val;
}
int main()
{
double val = 44.44;
string str = test_doubletostr(val);
cout << str << endl;
str = "123.123";
val = strtodouble(str);
cout << val << endl;
return 0;
}
10.4.4 stringstream
将 192,168,1,100 转为 点分十进制方式:192.168.1.100
void test_stringstream()
{
// 将字符串 192,168,1,100 转为192.168.1.100
string buf("192,168,1,100");
stringstream ss(buf);
int v1;
int v2;
int v3;
int v4;
char ch = '.';
ss >> v1 >> ch >> v2 >> ch >> v3 >> ch >> v4;
ch = '.';
stringstream ss2;
ss2 << v1 << ch << v2 << ch << v3 << ch << v4 << endl;
cout << ss2.str() << endl;
}
10.5 输入输出流的格式化
输出流格式化
以操纵子方式格式化
以成员函数方式格式化
宽度控制
对齐控制
填充控制
精度控制
进制输出
10.5.1 输出流格式化
数据输入输出的格式控制使用系统头文件中提供的操纵符。把它们作为插入操作符<<的输出对象即可,如setiosflags、setw、setfill、setprecision、hex、oct等。
通过调用流的成员函数控制格式,如setf、unsetf、width、fill、precision等,优点是在设置格式同时,可以返回以前的设置,便于恢复原来的设置。
// 宽度控制
// 对齐控制
// 填充控制
// 精度控制
// 进制输出
#include <iostream>
#include <iomanip>
using namespace std;
// 通过操纵子方式进行格式化输出
// 宽度控制
// 对齐控制
// 填充控制
// 精度控制
// 进制输出
int main(void)
{
//system("chcp 936");
int n = 64;
double d = 123.45;
double d2 = 0.0187;
cout<<"=================宽度控制====================="<<endl;
cout<<n<<'#'<<endl;
cout<<setw(10)<<n<<'#'<<n<<endl;// 默认是右对齐的,宽度控制不会影响下一个输出
cout<<"=================对齐控制====================="<<endl;
cout<<setw(10)<<setiosflags(ios::left)<<n<<'#'<<endl;
cout<<setw(10)<<n<<'#'<<endl;// 对齐控制会影响下一个输出
//cout<<setw(10)<<setiosflags(ios::right)<<n<<'#'<<endl;
cout<<setw(10)<<resetiosflags(ios::left)<<n<<'#'<<endl;
cout<<"=================填充控制====================="<<endl;
cout<<setw(10)<<setfill('?')<<n<<'#'<<endl;
cout<<setw(10)<<n<<'#'<<endl;// 填充控制会影响下一个输出
cout<<setw(10)<<setfill(' ')<<n<<'#'<<endl;
cout<<"=================精度控制====================="<<endl; // 表示有效数字;
cout<<setprecision(4)<<d<<endl;
cout<<setprecision(2)<<d2<<endl;
cout<<setiosflags(ios::fixed);// 小数点位数;
cout<<setprecision(4)<<d<<endl;
cout<<setprecision(2)<<d2<<endl;
cout<<"=================进制输出====================="<<endl;
//cout<<resetiosflags(ios::dec);
//cout<<setiosflags(ios::oct)<<n<<endl;
////cout<<resetiosflags(ios::oct);
//cout<<n<<endl;
////cout<<setiosflags(ios::hex)<<n<<endl;
//cout<<hex<<n<<endl;
cout<<n<<endl;
cout<<oct<<n<<endl;
cout<<hex<<n<<endl;
cout<<endl;
cout<<setiosflags(ios::showbase);
cout<<dec<<n<<endl;
cout<<oct<<n<<endl;
cout<<hex<<n<<endl;
cout<<endl;
cout<<setbase(10)<<n<<endl; // 第二种进制输出方法
cout<<setbase(8)<<n<<endl;
cout<<setbase(16)<<n<<endl;
return 0;
}
10.5.2 常用的流操纵算子

int main(void)
{
//system("chcp 936");
int n = 64;
double d = 123.45;
double d2 = 0.0187;
cout<<"=================宽度控制====================="<<endl;
cout<<n<<'#'<<endl;
cout.width(10);
cout<<n<<'#'<<n<<endl;// 宽度控制不会影响下一个输出
cout<<"=================对齐控制====================="<<endl;
cout.width(10);
cout.setf(ios::left);
cout<<n<<'#'<<endl;
cout.width(10);
cout<<n<<'#'<<endl;// 对齐控制会影响下一个输出
//cout.width(10);
//cout.setf(ios::right);
//cout<<n<<'#'<<endl;
cout.width(10);
cout.unsetf(ios::left);
cout<<n<<'#'<<endl;
cout<<"=================填充控制====================="<<endl;
cout.width(10);
cout.fill('?');
cout<<n<<'#'<<endl;
cout.width(10);
cout<<n<<'#'<<endl;// 填充控制会影响下一个输出
cout.width(10);
cout.fill(' ');
cout<<n<<'#'<<endl;
cout<<"=================精度控制====================="<<endl;
cout.precision(4);
cout<<d<<endl;
cout.precision(2);
cout<<d2<<endl;
cout.setf(ios::fixed);
cout.precision(4);
cout<<d<<endl;
cout.precision(2);
cout<<d2<<endl;;
cout<<"=================进制输出====================="<<endl;
cout.setf(ios::showbase);
cout<<n<<endl;
cout.unsetf(ios::dec);
cout.setf(ios::oct);
cout<<n<<endl;
cout.unsetf(ios::showbase);
cout<<n<<endl;
return 0;
}
10.5.3 ios类中的枚举常量

10.5.4 ios类的成员函数
ios类提供成员函数对流的状态进行检测和进行输入输出格式控制等操作。


原文地址:https://blog.csdn.net/weixin_43916755/article/details/154475348
免责声明:本站文章内容转载自网络资源,如侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!
