文章目录
- 1.文件读写
- 2.二进制文件的读写
- 3.文件随机读写tellp、tellg、seekp、seekg
1.文件读写
- 文本读写方式1:<<, >>,
- 文本读写方式2:get, put,
- read,
- write
- 文本模式打开与二进制模式打开的区别
- eg:P60\01.cpp
#include <cassert>
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main(void)
{
ofstream fout( "test.txt");
fout << "abc" << " " << 200;//先把abc 200写入到文件中
fout.close();
ifstream fin( "test.txt");
string s;
int n;
//fin>>n>>s;不按照顺序读取的话,会输出异常状态
fin >> s >> n;//用文件输入流打开文件,并将其输出
cout << s << " " << n << endl;
return 0;
}
- 测试:
- eg:P60\02.cpp
#include <cassert>
#include <cassert>
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main(void)
{
ofstream fout1( "test2.txt");
assert(fout1);
char ch;
//往一个文件中写入26个字母
for ( int i = 0; i < 26; i++)
{
ch = 'A' + i;
fout1.put(ch);
}
fout1.close();
ifstream fin1( "test2.txt");
//读取文件到结束时,文件流处于EOF状态,类型转换指针void*指针会返回空指针,就跳出循环了
while (fin1.get(ch))
{
cout << ch;
}
cout << endl;
return 0;
}
- 测试:写入文件的结果
26个字节 - eg:P60\03.cpp
#include <cassert>
#include <cassert>
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main(void)
{
/*
如果以文本方式打开文件,写入字符的时候,遇到\n会做转换,写入\r不做转换
如果以二进制方式打开文件写入字符的时候,遇到\n不会做转换
windows平台\n会转换为 \r\n
linux平台保留不变
mac系统\n转换为\r
*/
//默认以文本方式打开文件
ofstream fout1( "test3.txt");
fout1<<"ABC\r\n";
fout1.close();
return 0;
}
- 测试:是6个字符,A,B,C,\r,\r,\n总共6个字符
- eg:P60\04.cpp
#include <cassert>
#include <cassert>
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main(void)
{
/*
(1)如果以文本方式打开文件,写入字符的时候,遇到\n会做转换,写入\r不做转换
windows平台\n会转换为 \r\n
linux平台保留不变
mac系统\n转换为\r
(2)如果以二进制方式打开文件写入字符的时候,遇到\n不会做转换
(3)以文本方式打开文件,也可以写入二进制数据
以二进制方式打开文件,也可以写入文本
写入的数据是二进制还是文本,与打开方式无关,与写入使用的函数有关
要写入二进制数据,应该用write,相应的读要用read
*/
//以二进制方式打开
ofstream fout1( "test3.txt", ios::out| ios::binary );
fout1<<"ABC\r\n";
fout1.close();
return 0;
}
- 测试:不做任何转换,输出5个字节
2.二进制文件的读写
- 二进制文件不同于文本文件,它可用于任何类型的文件(包括文本文件)
- 对二进制文件的读写可采取从istream类继承下来的成员函数read()和从ostream类继承下来的成员函数write()
- 文件打开操作时使用枚举常量ios::binary,eg:ofstream fout(“binary.dat”, ios::out|ios::binary);
- eg:P60\05.cpp
#include <cassert>
#include <cassert>
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
struct Test
{
int a;
int b;
};
//测试:以文本方式打开文件,也可以写入二进制数据
//写入的数据是二进制还是文本,与打开方式无关,与写入使用的函数有关
int main(void)
{
Test test = {100, 200};
//以文本方式打开
ofstream fout("test4.txt");
//以二进制方式写入8个字节(将100在机器中的内存表示写入文件中),转化成char*
fout.write(reinterpret_cast<char*>(&test), sizeof(Test));
fout.close();
//如何知道写入成功?可以读取出来
Test test2;
ifstream fin("test4.txt");
fin.read(reinterpret_cast<char*>(&test2), sizeof(Test));
cout<<test2.a<<" "<<test2.b<<endl;
return 0;
}
- 测试
因为以文本方式打开是看不到这些字符的,因为它超过了ASCII码字符所表示的范围 - eg:P60\07.cpp
#include <cassert>
#include <cassert>
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
struct Test
{
int a;
int b;
};
//测试:以二进制方式打开文件,也可以写入文本
int main(void)
{
Test test = {100, 200};
//以二进制方式打开
ofstream fout("test4.txt", ios::out|ios::binary);
//c插入运算符<<是以文本方式写入数据的
//虽然以二进制方式打开,但是写入的方式是文本的方式
fout<<"abc"<<200;
return 0;
}
- 测试:
- P60\08.cpp
#include <cassert>
#include <cassert>
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
struct Test
{
int a;
string b;//string对象
string c;
};
//MyString类大小总是4字节,与字符串str无关
//同样string只与其成员有关,他里面的成员就是一些指针,不同平台不一样,当前平台是32字节,其他平台可能是16字节,因为持有的数据成员不同
class MyString
{
char* str;
}
int main(void)
{
Test t1;
t1.a = 100;
t1.b = "xxxabcdddddddddddddddddddddddddddddddddddddddddddddddffffffffffffffffffffffffff";
t1.c = "yyyffffffffffffffffffffffffffffffffddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd";
//下面的写入方式是不行的
//因为每次写入的时候,并不是将字符串b和c的所有内容写入到文件中,总是写入了68字节而已(struct Test:4+32+32)
//只是写将string中的成员指针写进去了,而没有将指针所指向的内存写入进去
ofstream fout("test6.txt", ios::out|ios::binary);
fout.write((char*)&t1, sizeof(t1));
fout.close();
ifstream fin("test6.txt", ios::out|ios:binary);
Test t2;
fin.read((char*)&t2, sizeof(Test));
cout<<t2.a<<" "<<t2.b<<" "<<t2.c<<endl;
fin.close();
//string类型的大小总是32字节
//计算一个类型或者说一个类型所对应对象的大小,与字符串内容无关
cout<<sizeof(Test)<<endl;
string a = "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd";
cout<<sizeof(string)<<endl;
cout<<sizeof(a)<<endl;
return 0;
}
- 测试:
字符串超过32字节
写入的文件总是68字节 - eg:P60\09.cpp
#include <cassert>
#include <cassert>
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
struct Test
{
int a;
string b;//string对象
string c;
};
//MyString类大小总是4字节,与字符串str无关
//同样string只与其成员有关,他里面的成员就是一些指针,不同平台不一样,当前平台是32字节,其他平台可能是16字节,因为持有的数据成员不同
class MyString
{
char* str;
}
int main(void)
{
Test t1;
t1.a = 100;
t1.b = "xxxabcdddddddddddddddddddddddddddddddddddddddddddddddffffffffffffffffffffffffff";
t1.c = "yyyffffffffffffffffffffffffffffffffddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd";
// //下面的写入方式是不行的
// //因为每次写入的时候,并不是将字符串b和c的所有内容写入到文件中,总是写入了68字节而已(struct Test:4+32+32)
// ofstream fout("test6.txt", ios::out|ios::binary);
// fout.write((char*)&t1, sizeof(t1));
// fout.close();
// ifstream fin("test6.txt", ios::out|ios:binary);
// Test t2;
// fin.read((char*)&t2, sizeof(Test));
// cout<<t2.a<<" "<<t2.b<<" "<<t2.c<<endl;
// fin.close();
// //string类型的大小总是32字节
// //计算一个类型或者说一个类型所对应对象的大小,与字符串内容无关
// cout<<sizeof(Test)<<endl;
// string a = "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd";
// cout<<sizeof(string)<<endl;
// cout<<sizeof(a)<<endl;
//正确的写入方式:一个一个写入
ofstream fout("test6.txt", ios::out|ios::binary);
//写入字符串的时候,最好先写入字符串的长度,然后写入字符串实际数据
fout.write((char*)&t1.a, sizeof(int));
int len;
len = t1.b.length();
fout.write((char*)&len, sizeof(int));
fout.write((char*)&t1.b.data(), t1.b.length());
len = t1.c.length();
fout.write((char*)&len, sizeof(int));
fout.write((char*)&t1.c.data(), t1.c.length());//data()实际上调用的是c_str()方法
fout.close();
ifstream fin("test6.txt", ios::out|ios:binary);
Test t2;
fin.read((char*)&t2.a, sizeof(int));
fin.read((char*)&len, sizeof(int)); //读第二个字符串的长度
t2.b.resize(len);
fin.read((char*)&t2.b[0], len);
fin.read((char*)&len, sizeof(int));
t2.b.resize(len);
fin.read((char*)&t2.c[0], len);
cout<<t2.a<<" "<<t2.b<<" "<<t2.c<<endl;
fin.close();
return 0;
}
- 测试:
看乱码,首先是4个字节的整数,然后又是4个字节的整数,表示第一个字符串的长度,然后是第二个字符串的长度 - write()成员函数
函数功能:以字节为单位向文件流中写入整块数据,最有价值的应用可以处理结构体变量和类对象
函数原型:
ostream& write( const char* pch, int nCount );
函数参数:
pch 写入的数据的指针
nCount 写入数据的字节大小
- read() 成员函数
函数功能:从文件流中读出整块数据
函数原型:
istream& read( char* pch, int nCount );
函数参数:
pch 用来接收数据的指针
nCount 读取的字节数的大小
3.文件随机读写tellp、tellg、seekp、seekg
- 当前文件流活动指针
(1)文件流指针用以跟踪发生 I/O 操作的位置
(2)每当从流中读取或写入一个字符,当前活动指针就会向前移动
(3)当打开方式中不含有ios::ate或ios::app选项时,则文件指针被自动移到文件的开始位置,即字节地址为0的位置。 - 文件的随机读写 seekp和seekg:定位文件指针
函数功能
seekp:设置输出文件流的文件流指针位置
seekg:设置输入文件流的文件流指针位置
函数原型:
ostream& seekp( streampos pos );定位到某一个位置pos,基准点是文件的开头指定的
ostream& seekp( streamoff off, ios::seek_dir dir );基准点可以是开头,结尾,或者文件当前位置,类似于Linux下的fseek,lseek
istream& seekg( streampos pos );
istream& seekg( streamoff off, ios::seek_dir dir );
函数参数
pos:新的文件流指针位置值
off:需要偏移的值
dir:搜索的起始位置
- 文件的随机读写tellp和tellg:获取当前文件流活动指针
输出流用tellp
输入流用tellg
函数功能
tellp:获得输出的文件流指针的当前位置,以字节为单位
tellg:获得输入的文件流指针的当前位置,以字节为单位
函数原型:
streampos tellp();
streampos tellg();
函数返回值:实际上是一个long类型
- 其他
C库
fseek,ftell
Linux系统调用
lseek
lseek(fd, 0, SEEK_CUR)
seekp,seekg与C库中fseek相互对应(C库不区分输入流和输出流)
tellp,tellg与C库中ftell相互对应
seekp,seekg与Linux系统调用中lseek相互对应
Linux系统调用没有对应的tell函数,而可以通过lseek(fd, 0, SEEK_CUR)实现类似功能,其返回值为当前文件指针的位置
- seek_dir
dir参数用于对文件流指针的定位操作上,代表搜索的起始位置
在ios中定义的枚举类型:
enum seek_dir {beg, cur, end};
每个枚举常量的含义:
ios::beg:文件流的起始位置
ios::cur:文件流的当前位置
ios::end:文件流的结束位置
- eg:P60\10.cpp
#include <cassert>
#include <cassert>
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main(void)
{
ifstream fin("test7.txt");//文件输入流不会创建文件
//如果文件不存在,打开会失败
assert(fin);
fin.seekg(2);//定位到字符c
char ch;
fin.get(ch);
cout<<ch<<endl;
//输出最后一个字符
//定位到文件结尾处,向前-1
//文件流指针指向了末尾的地方
fin.seekg(-1, ios::end);
fin.get(ch);
cout<<ch<<endl;
//用以下方法可以获取文件的大小
//获取文件流指针的位置
//0是结束符的位置
fin.seekg(0, ios::end);
streampos pos = fin.tellg();
cout<<pos<<endl;
return 0;
}
- 测试:
当前文件大小是7个字节