写入文本文件
- 在C++中,文件操作属于技术的应用,不是基本语法。
- 数据持久化的两种方式:文件和数据库。
文本文件一般以行的形式组织数据。
包含头文件:#include <fstream>
类:ofstream (output file stream)
示例:
#include <iostream>
#include <vector>
#include <cmath>
#include <fstream>
using namespace std;
int main() {
ofstream fout;//创建文件输出流文件
fout.open("test.txt");//打开文件,如果不存在就创建他,如果存在,则截断其内容
//向问价中写入数据
fout << "西施|19|及其漂亮\n";
fout << "冰冰|22|漂亮\n";
fout << "幂幂|25|一般\n";
fout.close();//关闭文件,fout对象失效前会自动调用close()。
cout << "操作文件完成.\n";
}
对于open()成员函数的参数,可以用C风格的字符串,也可以用string.
ofstream fout;//创建文件输出流文件
//fout.open("test.txt");//打开文件,如果不存在就创建他,如果存在,则截断其内容
string filename = "test.txt";
//char filename[]="test.txt";
fout.open(filename);
如果要指定目录的话:
string filename="D:\\data\\txt\\test.txt";
一般文件名要全路径,书写的方法如下:
"D:\data\txt\test.txt"
错误。R"(D:\data\txt\test.txt)”
原始字面量,C++11标准。"D:\\data\\txt\\test.txt"
转义字符。"D:/tata/txt/test.txt"
把斜线反着写。"/data/txt/test.txt"
Linux系统采用的方法。
创建文件输出流对象,打开文件,如果文件不存在,则创建它。
ios::out 缺省值:会截断文件内容。
ios::trunc 截断文件内容。
ios::app//不截断文件内容,只在文件未尾追加文件。
ofstream fout(filename);
ofstream fout(filename, ios::out);
ofstream fout(filename, ios:trunc);
ofstream fout(filename, ios:app);
fout.open(filename);
对于,前三个都是清空文件,然后再写,也就是覆盖写。 ios:app
则是继续在他的后面写也就是追加写。
然后这个就是追加写的代码:
#include <iostream>
#include <vector>
#include <cmath>
#include <fstream>
using namespace std;
int main() {
ofstream fout;//创建文件输出流文件
//fout.open("test.txt");//打开文件,如果不存在就创建他,如果存在,则截断其内容
string filename = "test.txt";
//char filename[]="test.txt";
//创建文件输出流对象,打开文件,如果文件不存在,则创建它。
// ios::out 缺省值:会截断文件内容。
// ios::trunc 截断文件内容。
// ios::app//不截断文件内容,只在文件未尾追加文件。
//ofstream fout(filename);
//ofstream fout(filename, ios::out);
//ofstream fout(filename, ios:trunc);
//ofstream fout(filename, ios:app);
//fout.open(filename);
ofstream fout;
fout.open(filename, ios::app);
//判断打开文件是否成功
//失败的原因主要有:1)目录不存在;2)磁盘空间已满;3)没有权限,Linux平台下很常见。
if (fout.is_open() == false) {
cout << "打开文件" << filename << "失败.\n";
return 0;
}
//向文件中写入数据
fout << "西施|19|及其漂亮\n";
fout << "冰冰|22|漂亮\n";
fout << "幂幂|25|一般\n";
fout.close();//关闭文件,fout对象失效前会自动调用close()。
cout << "操作文件完成.\n";
}
读取文件内容
这里有三种方法:
- 第一种
getline(fin, buffer)
//第一种方法
string buffer;//用于存放从文件中读取的内容
//读取文件内容的代码
//文本文件一般以行的方式组织数据
while (getline(fin, buffer)) {
cout << buffer<<endl;
}
- 第二种
fin.getline(buffer, 100)
第二个参数是最多读取内容的字节数
//第二种方法
char buffer[101];
//注意:如果采用ifstream.getline(),一定要保证缓冲区足够大
while (fin.getline(buffer, 100)) {//第二个参数是最多读取内容的字节数
cout << buffer << endl;
}
- 第三个
fin >> buffer
string buffer;
while (fin >> buffer) {
cout << buffer << endl;
}
#include <iostream>
#include <vector>
#include <cmath>
#include <fstream>//ifstream 类需要包含的头文件
#include<string>
using namespace std;
int main() {
string filename = "test.txt";
//char filename[]="test.txt";
//创建文件输入流对象,打开文件,如果文件不存在,则创建它。
// ios::in 缺省值.
//ifstream fin(filename);
//ifstream fin(filename, ios::in);
ifstream fin;
fin.open(filename, ios::in);
//判断打开文件是否成功
//失败的原因主要有:1)目录不存在;2)文件不存在;3)没有权限,Linux平台下很常见。
if (fin.is_open() == false) {
cout << "打开文件" << filename << "失败.\n";
return 0;
}
第一种方法
//string buffer;//用于存放从文件中读取的内容
读取文件内容的代码
文本文件一般以行的方式组织数据
//while (getline(fin, buffer)) {
// cout << buffer<<endl;
//}
第二种方法
//char buffer[101];
注意:如果采用ifstream.getline(),一定要保证缓冲区足够大
//while (fin.getline(buffer, 100)) {//第二个参数是最多读取内容的字节数
// cout << buffer << endl;
//}
//第三种方法
string buffer;
while (fin >> buffer) {
cout << buffer << endl;
}
fin.close();//关闭文件,fout对象失效前会自动调用close()。
cout << "操作文件完成.\n";
}
写入二进制文件
首先我们先看看数据在内存中的分配
对于第一个string类型的。每个字节存放一个字符,字符在内存中存放的是ascii
码,不是符号。
对于第二个int类型的。如果是整数变量存放,那么它就是二进制数据,或者二进制格式。
文件操作
- 写文件:把内存中的数据转移到磁盘文件中。
- 读文件:把磁盘文件中的数据转移到内存中。
- 内存和硬盘都是存储设备,本质上没有区别。
文本文件和二进制文件
- 文本文件:存放的是字符串,以行的方式组织数据,每个字节都是有意义的符号。
- 二进制文件:存放的不一定是字符串,以数据类型组织数据,内容要作为一个整体来考虑,单个字节没有意义。
二进制文件以数据块的形式组织数据,把内存中的数据直接写入文件。
包含头文件:#include <fstream>
类:ofstream (output file stream)
ofstream
打开文件的模式(方式):
对于ofstream
,不管用哪种模式打开文件,如果文件不存在,都会创建文件。
ios:out 缺省值:会截断文件内容。
ios:trunc 截断文件内容。(truncate)
ios:app 不截断文件内容,只在文件未尾追加文件。(append)
ios::binary 以二进制的方式打开文件
文本和二进制方式的区别:
- 在windows平台下,文本文件的换行标志是
\r\n
。 - 在windows平台下,如果以文本方式打开文件,写入数据的时候,系统会将
"\n"
转换成"\r\n"
;读取数据的时候,系统会将"\r\n"
转换成"\n"
。如果以二进制方式打开文件,写和读都不会进行转换。
对于他和其他不一样的就是,第一点就是打开的时候要用二进制fout.open(filename,ios::app | ios::binary);
然后第二点就是写入的时候就是fout.write()
#include <iostream>
#include <vector>
#include <cmath>
#include <fstream>//ifstream 类需要包含的头文件
#include<string>
using namespace std;
int main() {
//文件名一般用全路径,书写的方法如下
//1. "D:\data\txt\test.dat" 错误。
//2. R"(D:\data\txt\test.dat)" 原始字面量,C++11标准。
//3. "D:\\data\\txt\\test.dat" 转义字符。
//4. "D:/tata/txt/test.dat" 把斜线反着写。
//5. "/data/txt/test.dat" Linux系统采用的方法。
string filename = R"(test.dat)";
//char filename[] = R"(test.dat)";
//创建文件输出流对象,打开文件,如果文件不存在,则创建它
//ios::out 缺省值:会截断文件内容
//ios::trunc 截断文件内容(truncate)
//ios::app 不截断文件内容,只要文件末尾追加文件(append)
//ios::binary 以二进制方式打开文件
//ofstream fout(filename,ios::binary);
//ofstream fout(filename,ios::out | ios::binary);
//ofstream fout(filename,ios::trunc | ios::binary);
//ofstream fout(filename,ios::app | ios::binary);
ofstream fout;
fout.open(filename, ios::app | ios::binary);//竖线就是按位或运算
//判断打开文件是否成功
//失败的原因主要有:1)目录不存在;2)磁盘空间已满;3)没有权限,Linux平台下很常见。
if (fout.is_open() == false) {
cout << "打开文件" << filename << "失败.\n";
return 0;
}
//向文件中写入数据
struct st_girl {
char name[301];//姓名
int no;//编号
char memo[301];//备注
double weight;//体重
}girl;
girl = {"西施",3,"中国历史第一美女。",45.8};
fout.write((const char*)&girl, sizeof(st_girl));//写入第一块数据。
girl ={"冰冰",8,"也是个大美女哦。",55.2};
fout.write((const char*)&girl,sizeof(st_girl));//写入第二块数据。
fout.close();
cout << "操作文件完成" << endl;
}
读取二进制文件
二进制文件的格式
- 二进制文件的格式多样,由业务需求决定。
- 程序员自定义的二进制文件格式,只有程序员自己才知道。
- 通用的二进制文件格式: mp3(音乐)、mp4(视频)、bmp(位图)、jpg(图片)、png(图片)
文本文件VS二进制文件
- 文件文件:由可显示的字符组成,方便阅读(解码),占用的空间比较多。
- 二进制文件:由比特0和1组成,组织数据的格式与文件用途有关,不方便阅读(解码)。为了节省存储空间,还可能采用压缩技术。为了保证数据安全,也可能采用加密技术。
包含头文件#include<fstream>
类:ifstream
ifstream 打开文件的模式(方式):
对于ifstream
,如果文件不存在,则打开文件失败
ios::in 缺省值:会截断文件内容
ios::binary 以二进制方式打开文件
ifstream fout(filename,ios::binary);
ifstream fout(filename,ios::in | ios::binary);
ifstream fin;
fin.open(filename, ios::in | ios::binary);//竖线就是按位或运算
#include <iostream>
#include <vector>
#include <cmath>
#include <fstream>//ifstream 类需要包含的头文件
#include<string>
using namespace std;
int main() {
//文件名一般用全路径,书写的方法如下
//1. "D:\data\txt\test.dat" 错误。
//2. R"(D:\data\txt\test.dat)" 原始字面量,C++11标准。
//3. "D:\\data\\txt\\test.dat" 转义字符。
//4. "D:/tata/txt/test.dat" 把斜线反着写。
//5. "/data/txt/test.dat" Linux系统采用的方法。
string filename = R"(test.dat)";
//char filename[] = R"(test.dat)";
//创建文件输出流对象,打开文件,如果文件不存在,则创建它
//ios::in 缺省值:会截断文件内容
//ios::binary 以二进制方式打开文件
//ifstream fout(filename,ios::binary);
//ifstream fout(filename,ios::in | ios::binary);
ifstream fin;
fin.open(filename, ios::in | ios::binary);//竖线就是按位或运算
//判断打开文件是否成功
//失败的原因主要有:1)目录不存在;2)磁盘空间已满;3)没有权限,Linux平台下很常见。
if (fin.is_open() == false) {
cout << "打开文件" << filename << "失败.\n";
return 0;
}
//向文件中写入数据
struct st_girl {
char name[301];//姓名
int no;//编号
char memo[301];//备注
double weight;//体重
}girl;
while (fin.read((char*)&girl, sizeof(girl))) {
cout << "name=" << girl.name << ",no=" << girl.no << ",memo=" << girl.memo << endl;
}
fin.close();
cout << "操作文件完成" << endl;
}
文件操作的一些细节
- 在windows 平台下,文本文件的换行标志是
"\r\n"
. (也就是多两个字节) - 在linux平台下,文本文件的换行标志是
"\n"
。 - 在windows平台下,如果以文本方式打开文件,写入数据的时候,系统会将
"\n"
转换成
"\r\n"
;读取数据的时候,系统会将"\r\n"
转换成"\n"
。如果以二进制方式打开文件,写和读都不会进行转换。 - 在Linux平台下,以文本或二进制方式打开文本文件,系统不会做任何转换。
- 以文本方式读取文件的时候,遇到换行符停止,读入的内容中没有换行符;以二制方式读取文件的时候,遇到换行符不会停止,读入的内容中包括换行符(换行符被视为数据)。
- 在实际开发中,从兼容和语义考虑,一般: a)以文本模式打开文本文件,用行的方法操作它;b)以二进制模式打开二进制文件;用数据块的方法操作它;c)以二进制模式打开文本文件,用数据块的方法操作它(不用行hang的方法),这种情况表示不关心数据的内容;d)不要以文本模式打开二进制文件,也不要用行的方法操作二进制文件,可能会破坏二进制数据文件的格式,也没有必要。(因为二进制文件中的某字节的取值可能是换行符,但它的意义并不是换行,可能是整数n个字节中的某个字节)
fstream类
之前讲了ofsteam
类,还有ifstream
类。
下面讲一下fstream
类。
fstream
类既可以读文本/二进制文件,也可以写文本/二进制文件。
fstream
类的缺省模式是ios:in | ios::out
。
普遍的做法是:
- 如果只想写入数据,用
ofstream
;如果只想读取数据,用ifstream
;如果想写和读数据,用fstream
,这种情况不多见。不同的类体现不同的语义。
也就是对于上面的代码将ifstream
或者ofstream
改成fstream
。这种情况不多见。不同的类体现不同的语义。 - 在Linux平台下,文件的写和读有严格的权限控制。(需要的权限越少越好)
文件操作-随机存取
文件位置指针
对文件进行读/写操作时,文件的位置指针指向当前文件读/写的位置。
很多资料中用“文件读指针的位置”和“文件写指针的位置”,容易误导人。不管用哪个类操作文件,文件的位置指针只有一个。
- 获取文件位置指针
ofstream
类的成员函数是tellp()
;ifstream
类的成员函数是tellg()
,fstream
类两个都有,效果相同
std::streampos tellp();
std::streampos tellg();
例如:
#include <iostream>
#include <vector>
#include <cmath>
#include <fstream>
using namespace std;
int main() {
//fout.open("test.txt");//打开文件,如果不存在就创建他,如果存在,则截断其内容
string filename = "test.txt";
//char filename[]="test.txt";
//创建文件输出流对象,打开文件,如果文件不存在,则创建它。
// ios::out 缺省值:会截断文件内容。
// ios::trunc 截断文件内容。
// ios::app//不截断文件内容,只在文件未尾追加文件。
//ofstream fout(filename);
//ofstream fout(filename, ios::out);
//ofstream fout(filename, ios:trunc);
//ofstream fout(filename, ios:app);
//fout.open(filename);
ofstream fout;
fout.open(filename, ios::out);
//判断打开文件是否成功
//失败的原因主要有:1)目录不存在;2)磁盘空间已满;3)没有权限,Linux平台下很常见。
if (fout.is_open() == false) {
cout << "打开文件" << filename << "失败.\n";
return 0;
}
//向文件中写入数据
cout << "位置:" << fout.tellp()<<endl;
fout << "西施|19|及其漂亮\n";
cout << "位置:" << fout.tellp() << endl;
fout << "冰冰|22|漂亮\n";
cout << "位置:" << fout.tellp() << endl;
fout << "幂幂|25|一般\n";
cout << "位置:" << fout.tellp() << endl;
fout.close();//关闭文件,fout对象失效前会自动调用close()。
cout << "操作文件完成.\n";
}
对于这个结果:
他的位置指针是自己变化的,不需要我们进行调整。
2. 移动文件位置指针
ofstream
类的函数是seekp();
ifstream
类的函数是seekg();
fstream类两个都有,效果相同。
方法一:
std:.istream & seekg(std:streampos_Pos);
fin.seekg(128);//把文件指针移到第128字节。fin.seekp(128);//把文件指针移到第128字节。-fin.seekp(ios:beg)//把文件指针移动文件的开始。fin.seekp(ios:end)// 把文件指针移动文件的结尾。
方法二:
std:istream & seekg(stdstreamoff_Off,stdios:seekdir _Way);
在ios 中定义的枚举类型:
enum seek_dir {beg, cur, end;//beg-文件的起始位置;cur-文件的当前位置;end-文件的结尾位置。
fin.seekg(30,ios:beg);//从文件开始的位置往后移30字节。fin.seekg(-5,ios::cur);//从当前位置往前移5字节。
fin.seekg(8,ios:.cur);//从当前位置往后移8字节。
fin.seekg(-10,ios:end);//从文件结尾的位置往前移10字节。
#include <iostream>
#include <vector>
#include <cmath>
#include <fstream>//ifstream 类需要包含的头文件
#include<string>
using namespace std;
int main() {
string filename = "test.txt";
//char filename[]="test.txt";
//创建文件输入流对象,打开文件,如果文件不存在,则创建它。
// ios::in 缺省值.
//ifstream fin(filename);
//ifstream fin(filename, ios::in);
fstream fs;
fs.open(filename, ios::in | ios::out);
//判断打开文件是否成功
//失败的原因主要有:1)目录不存在;2)文件不存在;3)没有权限,Linux平台下很常见。
if (fs.is_open() == false) {
cout << "打开文件" << filename << "失败.\n";
return 0;
}
fs.seekg(22);//把文件位置移动到第22字节处。
string buffer;
while (fs >> buffer) {
cout << buffer << endl;
}
fs.close();//关闭文件,fout对象失效前会自动调用close()。
cout << "操作文件完成.\n";
}
缓冲区和流状态
写文件的时候,就是先向缓冲区中写数据,如果缓冲区满了,然后在向磁盘中写入。
读数据的时候,就是系统先把更多的数据从磁盘文件中读取出来,放到缓冲区中。
总的来说,缓冲区可以减少磁盘I/O的次数,提高读和写的效率。
文件缓冲区
文件缓冲区(缓存)是系统预留的内存空间,用于存放输入或输出的数据。
根据输出和输入流,分为输出缓冲区和输入缓冲区。
注意,在C++中,每打开一个文件,系统就会为它分配缓冲区。不同的流,缓冲区是独立的。程序员不用关心输入缓冲区,只关心输出缓冲区就行了。
在缺省模式下,输出缓冲区中的数据满了才把数据写入磁盘,但是,这种模式不一定能满足业务的需求。
输出缓冲区的操作:
- flush()成员函数
直接刷新缓冲区。 - endl
换行,然后刷新缓冲区 - unitbuf
fout<<unitbuf;
设置fout输出流,在每次操作之后都进行刷新缓冲区。 - nounitbuf
fout<<nounitbuf;
设置fout输出流,让fout回到缺省的缓冲方式
状态流
流状态有三个:eofbit
、badbit
和failbit
,取值:1-设置;或0-清除。
当三个流状成都为0时,表示一切顺利,good()成员函数返回true。
- eofbit
当输入流操作到达文件末尾时,将设置eofbit。
eof()成员函数检查流是否设置了eofbit。 - badbit
无法诊断的失败破坏流时,将设置badbit。(例如:对输入流进行写入;磁盘没有剩余空间)。bad()成员函数检查流是否设置了badbit。 - failbit
当输入流操作未能读取预期的字符时,将设置failbit(非致命错误,可挽回,一般是软件错误,例如:想读取一个整数,但内容是一个字符串;文件到了未尾)IО失败也可能设置 failbit。
fail()成员函数检查流是否设置了failbit。 - clear()成员函数清理流状态。
- setstate()成员函数重置流状态。