数据要进行持久化存储就需要用到文件。C/C++ 将文件看作有序的字节流,每个文件都是以 EOF(文件结束标志)结束。
按数据的组织形式,文件可分为文本文件和二进制文件。
数据在内存中以二进制形式存储的,如果不加转换地输出到外存,就是二进制文件。如果要求在外存上以 ASCII 码形式存储,需要在存储前进行转换。以ASCII 字符的形式存储的文件是文本文件。
- 字符一律以 ASCII 形式存储
- 数据型数据既可以用 ASCII 形式存储,也可以使用二进制形式存储。
例如:整数 12345,如果以 ASCII 码的形式存储输出到磁盘,则占用5个字节,而以二进制形式输出,则在磁盘上只占4个字节。
流
在 C 语言中,流(stream)可以被视为一种特殊的数据结构,它负责在程序和外部设备(如键盘,显示器,文件等)之间进行数据传输。
C语言提供了标准流方便进行输入输出操作。C/C++ 程序在启动时,默认打开3个流:
stdin
: 标准输入流,一般从键盘输入stdout
: 标准输出流,一般输出至控制台stderr
: 标准错误流,一般输出到控制台
这三个流的类型都是FILE*
,通常称为文件指针。
C/C++中就是通过文件指针来维护流的各种操作的。
文件指针
每个打开的文件都在内存中开辟一块区域,用来存放文件的相关信息(如文件名称,文件状态,文件当前位置等),这些信息是保存在一个 FILE 类型的结构体变量中的。
struct _iobuf {
char *_ptr;
int _cnt;
char *_base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char *_tmpfname;
};
typedef struct _iobuf FILE;
一般通过 FILE*
指针来维护该结构体变量,通过文件指针能间接找到它关联的文件。
文件操作函数
文件的操作步骤:
- 打开文件
- 对文件进行读、写操作
- 使用完文件后要关闭文件
打开文件 fopen() 和 关闭文件 fclose()
打开文件,fopen() 函数的声明:
FILE* fopen(const char* filename, const char* mode);
参数:
filename
: 文件名。默认为当前工作目录,可以指定路径,如:"D:\file\data.txt"mode
: 文件打开模式,即文件访问的权限
文本文件打开模式:
- "r" : 只读,文件必须存在
- "w" : 只写,会覆盖原内容,文件不存在会新建文件
- "a" : 只写,写入数据会被附加到文件末尾,文件不存在会新建文件
- "r+" : 读/写,文件必须存在
- "w+" : 读/写,写会覆盖原内容,文件不存在将新建文件
- "a+" : 读/写,写入数据会被附加到文件末尾,文件不存在会新建文件
二进制文件打开模式:"rb"、"wb"、"ab"、"rb+"、"wb+"、"ab+"。
关闭文件,fclose() 的声明:
int fclose(FILE* stream);
使用方式:
FILE *in, *out;
in = fopen("data.in", "r");
out = fopen("data.out", "w");
//读、写操作
fclose(in);
fclose(out);
对文件进行读/写操作
文件光标操作
fseek()
移动光标。
int fseek(FILE* stream, long offset, int origin);
参数:
stream
: 指明要操作的文件offset
: 偏移量,以字节为单位origin
: 偏移的起始位置SEEK_SET
:文件开始位置SEEK_CUR
:光标当前位置SEEK_END
: 文件末尾
rewind()
光标偏移到文件头。
void rewind(FILE* fp);
feof()
判断光标是否在文件末尾。
int feof(FILE* stream);
返回值: 当光标在文件尾时返回非零值,不是返回0。
ftell()
获取光标位置
long ftell(FILE* fp);
例如,测算文件大小
fseek(fp, 0L, SEEK_END);
int size = ftell(fp);
字符输入 fgetc() 和输出 fputc()
fgetc()从指定的流获取一个字符,并将位置标识符往前移动,声明:
int fgetc(FILE* stream);
参数:
stream
: 文件,要被读取的流
返回值:以无符号 char 强制转换为 int 的形式返回读取的字符,如果到达文件末尾或发生错误,则返回 EOF。
fputc()把参数 char 指定的字符写入到指定的流中,并将位置标识符往前移动,声明:
int fputc(int char, FILE* stream);
参数:
char
: 要写入的字符,以对应的 int 值进行传递stream
: 文件指针,要被写入字符的流
举例:
#include <cstdio>
int main()
{
FILE* pfread = fopen("data1.txt", "r");
if (pfread == nullptr)
{
perror("fopen->data1.txt");//打开data1.txt失败
return 1;
}
FILE* pfwrite = fopen("data2.txt", "w");
if (pfwrite == nullptr)
{
fclose(pfread);//关闭data1.txt
pfread = nullptr;//释放pread的空间
perror("fopen->data2.txt");//打开data2.txt失败
return 1;
}
//数据的读写(拷贝)
int ch = 0;
while ((ch = fgetc(pfread)) != EOF)
{
fputc(ch, pfwrite);
}
fclose(pfread);
fclose(pfwrite);
return 0;
}
文本行输入 fgets() 和输出 fputs()
fgets() 函数声明:
char* fgets(cahr* buf, int max_count, FILE* stream);
参数:
buf
: 字符数组指针,该数组存储要读取的字符串max_count
: 要读取的最大字符数。stream
: 要被读取的流
返回值:读取到的字符串,读完后再读返回值是 NULL。
fgets() 从 stream 中读取字符串,并将字符串存入 buf 中。当读取到 max_count-1 个字符时,或者读取到换行符时,或者到达文件末尾时,它会停止。
fputs() 声明:
int fputs(const char* str, FILE* steam);
参数:
str
: 要写入的字符串stream
: 要被写入的流
返回值:成功写入返回非负值,失败返回EOF。
举例:
#include <cstdio>
#include <iostream>
int main() {
FILE *pf = fopen("data.txt", "w+");
if(pf == nullptr) {
return 1;
}
//写文件。fputs不会自动换行
fputs("abcd\n", pf);
fputs("efgh\n", pf);
rewind(pf);
//读取
char buf[20];
fgets(buf, 20, pf);
std::cout << buf;
fclose(pf);
pf = nullptr;
return 0;
}
格式化输入 fscanf() 和输出 fprintf()
使用方法类似 scanf()
和 printf()
。格式化输入 fscanf() 和输出 fprintf()声明:
int fscanf(FILE* stream, const char* format, ...);
int fprintf(FILE* stream, const char* format, ...);
举例:
#include <iostream>
struct Stu
{
int num;
char name[20];
char sex;
int age;
};
int main()
{
Stu s{0};
FILE* pf = fopen("data.txt", "r");
if (pf == nullptr) {
return 1;
}
//写读文件
fscanf(pf, "%d%s %c%d", &s.num, s.name, &s.sex, &s.age);
fprintf(stdout, "%d %s %c %d\n", s.num, s.name, s.sex, s.age);
fclose(pf);
pf = nullptr;
return 0;
}
二进制输入 fread() 和输出 fwrite()
fread() 从给定流 stream 读取数据到 _DstBuf 所指向的空间,声明:
size_t fread(void* _DstBuf,size_t _ElementSize,size_t _Count,FILE* stream);
参数:
_DstBuf
:指向一块内存空间的指针,其大小至少为_ElementSize*_Count 字节_ElementSize
: 要读取的每个元素的大小,以字节为单位_Count
: 要读取的元素个数stream
: 输入流
fwrite() 将 _Str 所指向的数据写入到给定流 stream 中,声明:
size_t fwrite(const void* _Str,size_t _Size,size_t _Count,FILE* stream);
参数:
_Str
: 指向被写入的数据的指针_Size
: 要被写入的每个元素的大小,以字节为单位_Count
: 要读取的元素个数stream
: 输出流
举例
#include <iostream>
struct Stu
{
int num;
char name[20];
char sex;
int age;
};
int main()
{
Stu s1[3] = {1, "HH", 'M', 20,
2, "ZZ", 'M', 21,
3, "YY", 'M', 19,};
Stu *s2 = (Stu*)malloc(sizeof(s1[0]) * 3);
FILE* pf = fopen("data.txt", "wb+");
if (pf == nullptr)
{
return 1;
}
//二进制的形式读写文件
fwrite(s1, sizeof(s1[0]), 3, pf);
rewind(pf);
fread(s2, sizeof(s1[0]), 3, pf);
for (int i = 0; i < 3; i++) {
printf("%d %s %c %d\n", (s2+i)->num, (s2+i)->name,
(s2+i)->sex, (s2+i)->age);
}
fclose(pf);
pf = nullptr;
return 0;
}
freopen 函数
freopen 函数将 filename 所指文件与给定的 stream 流关联起来,主要用来重定向标准流。freopen() 函数的声明:
FILE* freopen(const char* filename, const char* mode, FILE* stream);
参数:
- filename: 要打开文件的名称
- mode: 文件打开模式,即文件访问的权限
- stream: 文件要关联的流,通常是标准文件流(stdin/stdout)或标准错误输出流(stderr)
返回值:成功则返回关联的 stream 指针
使用方式:
#include <cstdio> //freopen 所在头文件
#include <iostream>
using namespace std;
int main() {
int x, y;
freopen("data.in", "r", stdin); //以只读模式打开文件,并重定向stdin
freopen("data.out", "w", stdout);//以只写模式打开文件,并重定向stdout
//使用 printf/scanf/cin/cout
cin >> x >> y; //从 data.in 读取数据到 x 和 y
cout << x +y; //把 x+y 的结果写入 data.out
fclose(stdin); //关闭输入流
fclose(stdout); //关闭输出流
return 0;
}
printf/scanf/cin/cout
等函数默认使用 stdin/stdout
,将 stdin/stdout
重定向后,这些函数将输入/输出到被定向的文件。
ifstream/ofstream 文件输入输出流
C++ 提供了 ifstream 和 ofstream 来两个文件流用于进行文件输入输出操作
#include <fstream>
using namespace std; // 两个类型都在 std 命名空间里
int main() {
char data[100];
//以读模式打开文件
ifstream fin("in.txt");
//以写模式打开文件
ofstream fout("out.txt");
//读取,写入操作类似cin/cout
fin.getline(data, 100);
fout << data;
//关闭流
fin.close();
fout.close();
return 0;
}
标签:文件,stream,int,char,pf,FILE,Cpp,操作
From: https://www.cnblogs.com/hzyuan/p/17968344