C中文件输入输出
文件的缓冲区:
每一个被使用的文件都在内存中用一个FILE结构体来存储相应的文件信息(如文件的名字、文件的状态、文件当前位置等)。FILE的定义在stdio.h中
typedef struct { short level; //缓冲区“满”的程度 unsigned flags; //文件状态标志 char fd;//文件描述符 unsigned char hold;//如缓冲区无内容不读取字符 short bsize;//缓冲区的大小 unsigned char* buffer;//缓冲区位置 unsigned char* curp;//指针当前的指向 unsigned istemp; //临时文件指示器 short token; //用于有效性检查 }FILE; 注:不同的编译器的FILE类型略有不同。例如,VC6.0中 struct _iobuf { char *_ptr; int _cnt; char *_base; int _flag; int _file; int _charbuf; int _bufsiz; char *_tmpfname; }; typedef struct _iobuf FILE;
一般使用文件类型指针,FILE* fp(注:它指向内存中的文件信息区(即FILE)的开头,而不是指向外部介质上的数据文件的开头)。
FILE* fp;
fp = fopen("test.txt","w");//以只写的方式打开文件
...
fclose(fp);//关闭文件
文件打开方式 | 若指定文件不存在 |
"r"(只读) | 出错 |
"w"(只写) | 建立新文件,若文件存在,内容被销毁 |
"a"(追加) | 出错(自测VC6可以打开,新建一个空文件,应该编译器默认添加了w) |
"rb"(二进制文件的只读) | 出错 |
"wb"(二进制文件的只写) | 建立新文件,若文件存在,内容被销毁 |
"ab"(二进制文件的追加) | 出错 |
"r+"(读写) | 出错 |
"w+"(读写) | 建立新文件,若文件存在,内容被销毁 |
"a+"(读写) | 出错(自测VC6可以打开,新建一个空文件,应该编译器默认添加了w) |
"rb+"(二进制文件的读写) | 出错 |
"wb+"(二进制文件的读写) | 建立新文件,若文件存在,内容被销毁 |
"ab+"(二进制文件的读写) | 出错(自测VC6可以打开,新建一个空文件,应该编译器默认添加了w) |
注:加b的表示对二进制文件操作,默认(不加b)对文本文件操作。a(append)表示在文件末尾写入。
常用下面的方法打开一个文件
if((fp=fopen("filename","r"))==NULL) { printf("打开文件失败!"); exit(0); }
向文件读写的函数
fgetc(fp) 或 getc(fp) | 从fp指向的文件读入一个字符 | 成功,返回读到的字符,失败返回文件结束标志EOF(即-1) |
fput(ch, fp) 或 putc(ch, fp) | 把字符ch写到文件中去 | 成功,返回写入的字符,失败返回EOF |
fgets(char* str, int n, FILE* fp) | 从文件读入一个长度为n-1的字符串,存到字符数组str中 | 成功,返回str的地址,失败返回NULL |
fput(char* str, FILE* fp) | 把字符串str写入文件中 | 成功返回0,失败返回非0 |
fprintf(文件指针, 格式字符串, 输出列表) | 以格式化的方式写文件 | |
fscanf(文件指针, 格式字符串, 输入列表) | 以格式化的方式读文件 | |
fwrite(const void *, size_t, size_t, FILE *) | 向文件中写一个数据块 | |
fread(void *, size_t, size_t, FILE *) | 从文件中读一个数据块 |
最最常用的是 fread 和 fwrite 函数,它们在读写时是以二进制的方式进行的,速度快。
fread(void *buffer, size_t size, size_t count, FILE * fp) fwrite(const void *buffer, size_t size, size_t count, FILE *fp) buffer 是一个地址 size:要读写的字节数 count:要读写多少个数据项(每个数据项的长度为size) fp:文件指针 返回值:成功返回count,失败返回小于 count 的整数
返回值
注意:用”w+"打开时,用fwrite写入后就用fread读有点问题。不知为何?建议“w“和”r”分开读写。知道了原因:写入后文件位置标记(或称文件位置指针)是在文件的末尾,应该把标记定位到文件头再读文件。
- 文件位置标记(文件位置指针)
文件位置标记是指“接下来要读写的下一个字符的位置”,它随文件的读或写向后移动。
改变文件标记的位置:
void rewind(FILE* fp):使文件标记指向文件开头。
int fseek(FILE* fp, long offset, int origin); origin表示起始点,offset表示相对起始点的偏移量(正数向后偏移,负值向前偏移),成功返回0,失败返回非0值。
origin可以取值0,1,2;0表示文件头位置,1表示当前位置,2表示文件末尾位置。
#define SEEK_SET 0 //文件的开头位置
#define SEEK_CUR 1
#define SEEK_END 2
long ftell(FILE* fp); 获取文件文件位置标记的当前位置。是用相对于文件头的位移量来表示。成功返回位置,失败返回-1L。
feof(fp) 若标记到文件末尾,再读文件,该函数就返回1。否则返回0
还有一些文件读写的函数:
ferror(fp) 若文件的输入输出出错了,该函数返回非0值,未出错返回0。
clearerr(fp) 清空错误,使文件错误标志和文件结束标志置为0。当ferror函数为非0值后,应该调用clearerr函数,使ferror(fp)的值为0,以便下次检查。
C++的文件流
ifstream / ofstream / fstream
输入 / 输出 / 输入和输出
关于流的继承关系:Cpp中流继承关系
写入文件时用 std::ofstream
读取文件时用 std::ifstream
打开模式:(所在类 std::ios_base::openmode 或 std::ios::openmode)可以按位或“|”组合下面模式
openmode | effect |
in | 打开文件读,若文件不存在会打开失败。 |
out | 打开文件写,若文件存在,覆盖原来内容;若不存在,则新建。 |
ate | 打开文件,并把文件流位置移动到结尾。 |
app | 打开文件,在文件结尾追加 |
trunc | 在写之前删除文件内容 |
binary | 以二进制模式打开(不指定binary,默认是text模式) |
建议用binary方式打开,因为对于非文本数据的文件(如图形文件)用文本模式打开,将是一场灾难。(例如:写 --> 读 --> )
打开文件的两种方式:1. 在构造时直接传递文件名,2. 用 open()
#include <fstream> ... // 1. std::ofstream ofile("filename");//ofstream 默认的打开模式为 std::ios::out // 2. std::ofstream ofile; ofile.open("filename"); /// 检查文件是否打开 if(!ofile.is_open()) 或者 if(!ofile) return; 或者 if(ofile.fail()) return; { return;//... }
ifstream
std::ifstream ifile("test.txt");//默认的模式是 std::ios_base::in if (!ifile) return -1; const int size = 256; char buffer[size]; do { //ifile.getline(buffer, size); //std::cout << buffer << ' '; std::string str; std::getline(ifile, str); } while (!ifile.eof());
文件位置标记
对于输入操作 ifstream ,获取标记(get marker)判断下一个读取的位置。用 tellg() 返回获取标记的位置,用 seekg(..) 来设置 get marker 的位置。
对于输出操作 ofstream,放置标记(put marker)判断下一个写入的位置。用 tellp() 返回放置标记的位置,用 seekp(..) 来设置 put marker 的位置。
istream& seekg (streamoff off, ios_base::seekdir way); ostream& seekp (streamoff off, ios_base::seekdir way); //@Param off : 相对 way 的偏移字节数 // way : std::ios_base::beg 文件的开始 0 // std::ios_base::cur 文件的当前位置 1 // std::ios_base::end 文件的结尾 2
二进制模式
在C++程序和文件之间总是存在另一个程序,就是操作系统(OS)。your program <-->operating system <--> file
//未格式化的输出 (以二进制形式或字节形式写入,buf为起始地址,bufsize为字节数) std::ofstream& std::ofstream::write(const char* buf, std::streamsize bufsize); //未格式化的输入 std::ifstream& std::ifstream::read(char* buf, std::streamsize bufsize);
以二进制读写文件:
std::ofstream ofile("test.dat", std::ios_base::binary);//默认的模式是 std::ios_base::out if (!ofile.is_open()) return -1; const char szBuf[] = "I love you. 12x00k k3 123"; ofile.write(szBuf, sizeof(szBuf)); ofile.close(); std::ifstream ifile("test.dat", std::ios_base::binary);//默认的模式是 std::ios_base::in if (!ifile) return -1; std::string str; const int size = 7;//一般指定 1024 2048 4096 char buffer[size]; while (!ifile.eof()) { ifile.read(buffer, size); std::streamsize n = ifile.gcount();//获得实际读到的字节数 std::string s(buffer, n); str += s; } ifile.close(); //或者用下面 读取整个文件的内容 的方法 BYTE* buf = (BYTE*)str.c_str(); int nSize = str.size();
读取整个文件的内容 (重点)
// 读取整个文件内容的方法 // std::string s; ifile >> s;//不要用这种,因为到空白符会截止。例如:pretty girl 读到s就是 pretty //1. ifile.seekg(0, std::ios::end); int nLen = ifile.tellg(); ifile.seekg(0, std::ios::beg); char* buffer = new char[nLen]; ifile.read(buffer, nLen); std::string s(buffer, buffer + nLen); delete[] buffer; //2. #include <streambuf> std::istreambuf_iterator<char> eos; std::string sText(std::istreambuf_iterator<char>(ifile), eos); //3. 注意括号 std::string sText((std::istreambuf_iterator<char>(ifile)), std::istreambuf_iterator<char>()); //4. 用 stringstream (#include <sstream>) std::stringstream buffer; buffer << ifile.rdbuf(); std::string s(buffer.str()); //5. getline 有时会出错,不知为何 std::string s; getline(ifile, s, (char)ifile.eof()); //6. 用Poco库中的 StreamCopier Header: Poco/StreamCopier.h std::string s; Poco::StreamCopier::copyToString(ifile, s);
注意:文件的大小是否大于string的最大存储空间
std::string s; std::ifstream ifile("F:/兄弟连.rmvb"); ifile.seekg(0, std::ios::end); std::istream::pos_type pos = ifile.tellg(); if (pos > s.max_size())//max_size : 4294967294 0xfffffffe 4G-2 2^32-2 { std::cout << "文件大小超过string的最大空间,需要切割。"; }
MFC中的 CFile
CFile ofile; if (!ofile.Open(_T("test.dat"), CFile::modeCreate | CFile::modeWrite)) return FALSE; const char szBuf[] = "I love you. 12x00k k3 123"; ofile.Write(szBuf, sizeof(szBuf)); ofile.Close(); CFile ifile; if (!ifile.Open(_T("test.dat"), CFile::modeRead)) return FALSE; /* std::string str; char buf[7]; //一般 1024 2048 4096 int nLen = 0; while (nLen = ifile.Read(buf, sizeof(buf))) { std::string s(buf, nLen); str += s; } */ //or UINT nSize = ifile.GetLength(); //ifile.SeekToBegin(); //int n = ifile.SeekToEnd(); char* buf = new char[nSize]; ifile.Read(buf, nSize); //do something ... std::string s(buf, nSize); delete[] buf;
MS的 API 函数
//写文件 HANDLE hFile = ::CreateFile(_T("test.dat"), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); if (INVALID_HANDLE_VALUE == hFile) return FALSE; const char szBuf[] = "I love you. 12x00k k3 123"; DWORD dwRet = 0; ::WriteFile(hFile, szBuf, sizeof(szBuf), &dwRet, NULL); CloseHandle(hFile); //读文件 HANDLE hFile2 = ::CreateFile(_T("test.dat"), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); if (INVALID_HANDLE_VALUE == hFile2) return FALSE; DWORD dwLen = ::GetFileSize(hFile2, NULL); char* buf = new char[dwLen]; DWORD dwRet2; //实际读到的字节数 ::ReadFile(hFile2, buf, dwLen, &dwRet2, NULL); CloseHandle(hFile2); std::string s(buf, dwRet2); delete[] buf;
FILE的知识:www.cnblogs.com/lxy2015/p/5302365.html
2020/12/19
Cpp读文件
std::ifstream ifile("test.dat", std::ios_base::binary);//默认的模式是 std::ios_base::in , 并会 |std::ios_base::in if (!ifile) return -1; std::string str; const int size = 1024; char buffer[size]; while (!ifile.eof()) { ifile.read(buffer, size); std::streamsize n = ifile.gcount();//获得实际读到的字节数 std::string s(buffer, n); //... do something // str += s; } ifile.close();
复制文件
#include<fstream> bool Copyfile(char* FileSource, char* FileDest) { fstream fsCopee(FileSource, ios::binary | ios::in); if (!fsCopee) return false; fstream fsCoper(FileDest, ios::binary | ios::out); if (!fsCoper) return false; fsCoper << fsCopee.rdbuf(); return true }