目录
1.使用文件的原因
我们都知道,当我们结束一个程序时,程序里的数据会随之在内存上消失。
使用文件的好处在于我们可以把数据保存到文件中,或者需要从文件加载配置信息。
下面是内存和硬盘的关系,可以帮助我们理解文件的作用:
下面就简单介绍文件的使用。
2.预备知识
a._iobuf结构体
当我们打开一个文件的时候,就会将创建一个结构体,将一些信息保存到这里面。
这个结构体了解一下就好,我们不需要仔细研究。
struct _iobuf
{
char *_ptr; ------当前缓冲区内容指针
int _cnt; ------缓冲区还有多少字符
char *_base; ------缓冲区的起始地址
int _flag; ------文件流的状态,是否错误或结束
int _file; -----文件描述符
int _charbuf; -----双字节缓冲,缓冲2个字节
int _bufsiz; -----缓冲区大小
char *_tmpfname; -----临时文件名
};
typedef struct _iobuf FILE;
b.文件指针
文件指针比较简单,FILE是文件结构体类型,声明文件指针如下:
FILE* p; 声明一个文件指针
后面会经常用到。
c.文件的打开方式
操作方式 | 解释 | 如果文件不存在 |
---|---|---|
“r” | 只读,为了读取文件,打开一个已存在的文本文件 | 出错 |
“w” | 只写,为了写入文件,打开一个文本文件 | 自动创建一个文件 |
“a” | 追加,在文本文件末尾追加数据 | 自动创建一个文件 |
rb | 只读,为了读取文件,打开一个已存在的二进制文件 | 出错 |
rw | 只写,为了写入文件,打开一个二进制文件 | 自动创建一个文件 |
ab | 追加,在二进制文件末尾追加数据 | 自动创建一个文件 |
r+ | 读写,为了读取和写入文件,打开一个已存在的文本文件 | 出错 |
w+ | 读写,为了读取和写入文件,打开一个文本文件 | 自动创建一个文件 |
a+ | 读写,在文本文件末尾进行读取和写入 | 自动创建一个文件 |
rb+ | 读写,为了读取和写入文件,打开一个已存在的二进制文件 | 出错 |
wb+ | 读写,为了读取和写入文件,打开一个二进制文件 | 自动创建一个文件 |
ab+ | 读写,在二进制文件末尾进行读取和写入 | 自动创建一个文件 |
d.文件的打开和关闭
打开文件函数:fopen
FILE * fopen ( const char * filename, const char * mode );
形参: const char* filename 文件名
const char* mode 打开文件的方式
返回值: 文件(FILE)指针
打开文件示例:
#include<stdio.h>
#include<stdlib.h>
int main()
{
//以只读的方式打开test.txt文件
FILE* p = fopen("test.txt", "r");
return 0;
}
关闭文件函数:fclose
int fclose ( FILE * stream );
形参: FILE* stream 文件指针
返回值: 如果关闭文件成功,返回0
如果关闭文件失败,返回EOF
关闭文件示例:
#include<stdio.h>
#include<stdlib.h>
int main()
{
//以只读的方式打开test.txt文件
FILE* p = fopen("test.txt", "r");
//关闭文件,以免浪费资源
fclose(p);
return 0;
}
3.文件的顺序读写
a.字符输入输出函数
I.fgetc
int fgetc ( FILE * stream );
形参: FILE* stream 指向文件的指针
返回值: 如果读取成功,返回读取到字符的ASCII码值
如果到文件末尾或者读取失败,都会返回EOF
例子:
先创建一个test.txt文本文件,再从文件里读取字符
#include<stdio.h>
#include<stdlib.h>
int main()
{
//打开文件
FILE* p = fopen("test.txt", "r");
//检查是否打开成功
if (p == NULL)
{
perror("opening file fail!\n");
}
//读入一个字符
int c = fgetc(p);
//输出
printf("%c\n", c);
//关闭文件
fclose(p);
p = NULL;
return 0;
}
输出:
a
II.fputc
int fputc ( int character, FILE * stream );
形参: int character 写入的字符
FILE* stream 文件指针
返回值: 如果写入成功,返回写入的字符的ASCII码值
如果写入失败或,返回EOF
示例:
#include<stdio.h>
#include<stdlib.h>
int main()
{
//以只写的方式打开文件
FILE* p = fopen("test.txt", "w");
if (p == NULL)
{
perror("fail to open file\n");
}
//写入字符a
fputc('a', p);
fputc('a', p);
fputc('a', p);
fputc('a', p);
fputc('a', p);
fclose(p);
p = NULL;
}
写入前:
写入后:
b.字符串输入输出函数
I.fgets
char * fgets ( char * str, int n, FILE * stream );
形参: char* str 指向字符数组的指针,读取的数组会保存到字符数组里
int n 读取不超过n的字符,最多读取n-1个
FILE* stream 文件指针
返回值: 如果读取成功,返回指向读取的字符串的指针
如果没有读取到字符串或者读取失败的时候,都会返回NULL指针
示例:
读取这个文件里的前两个字符:
#include<stdio.h>
#include<stdlib.h>
int main()
{
char str[10];
FILE* p = fopen("test.txt", "r");
if (p == NULL)
{
perror("file to open file\n");
}
//读取前两个字符,并将它们存到str字符数组里
fgets(str, 3, p);
printf(str);
fclose(p);
p = NULL;
return 0;
}
输出:
aa
II.fputs
int fputs ( const char * str, FILE * stream );
形参: const char* str 指向要写入文件的字符串的指针
FILE* stream 文件指针
返回值: 写入文件成功,返回非负数
失败返回EOF
示例:
#include<stdio.h>
#include<stdlib.h>
int main()
{
char str[20] = "China";
//以只写的方式打开文件
FILE* p = fopen("test.txt", "w");
if (p == NULL)
{
perror("file to open file\n");
}
//将字符串"China"写入到文件中
fputs(str, p);
fclose(p);
p = NULL;
return 0;
}
写入前:
写入后:
c.格式化输入输出函数
I.fscanf
int fscanf ( FILE * stream, const char * format, ... );
形参: FILE * stream 文件指针
const char * format 输入格式
... 可变形参,指向保存数据空间的指针
返回值: 读取成功,则返回读取到的项(格式中指定的项)的个数
读取失败或者到文件末尾,则返回EOF
读取文件中的字符串:
示例:
#include<stdio.h>
#include<stdlib.h>
int main()
{
char buffer[20];
//打开文件
FILE* p = fopen("test.txt", "r");
if (p == NULL)
{
perror("open file fail\n");
}
//从文件中读取字符串到buffer中
fscanf(p, "%s", buffer);
//打印
printf(buffer);
fclose(p);
p = NULL;
return 0;
}
输出:
China
I.fprintf
int fprintf ( FILE * stream, const char * format, ... );
形参: FILE * stream 文件指针
const char * format 输出格式
... 可变形参,各项数据
返回值: 写入成功,返回写入字符的个数
写入失败,则返回EOF
示例:
#include<stdio.h>
#include<stdlib.h>
int main()
{
//以只写的方式打开文件
FILE* p = fopen("test.txt", "w");
if (p == NULL)
{
perror("open file fail\n");
}
//从把数据写入到文件中
fprintf(p, "%s", "今天吃火锅");
fclose(p);
p = NULL;
return 0;
}
写入前:
写入后:
d.二进制输入输出函数
II.fwrite
size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );
形参: void* ptr 指向一块内存空间
size_t size 元素的大小
size_t count 写入的元素个数
FILE* stream 文件指针
返回值: 返回写入的元素个数
如果元素的大小为0,或者元素的个数为0,返回0
示例:
#include<stdio.h>
#include<stdlib.h>
int main()
{
int arr[10];
for (int i = 0; i < 10; i++)
{
arr[i] = i + 1;
}
//以二进制只写的方式打开文件
FILE* p = fopen("test.txt", "wb");
if (p == NULL)
{
perror("open file fail\n");
}
//写入文件中
int ret = fwrite(arr, sizeof(int), 10, p);
printf("%d\n", ret);
fclose(p);
p = NULL;
return 0;
}
输出:
10
文件的变化:
乱码了,这是正常的,因为是以二进制的方式写入文件中
I.fread
size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
形参: void* ptr 指向一块内存空间
size_t size 元素的大小
size_t count 写入的元素个数
FILE* stream 文件指针
返回值: 返回读取到的元素个数
如果元素的大小为0,或者元素的个数为0,返回0
示例:
介绍fwrite函数时已经把1-10写入文件了,这里使用fread把数据读出来,并打印
#include<stdio.h>
#include<stdlib.h>
int main()
{
int arr[10];
//以二进制只读的方式打开文件
FILE* p = fopen("test.txt", "rb");
if (p == NULL)
{
perror("open file fail\n");
}
//读文件
int ret = fread(arr, sizeof(int), 10, p);
//打印
for (int i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
fclose(p);
p = NULL;
return 0;
}
输出:
1 2 3 4 5 6 7 8 9 10
4.文件的随机读写
前面介绍的几个函数都是顺序读写的,
要是我们想要做到从指定位置开始读或者写,就要用到下面几个函数。
(1).fseek
如果要指定写入或者读取的位置,就要设置文件指针的位置
这时候就要用到fseek函数,帮我们设置文件指针到指定位置
int fseek ( FILE * stream, long int offset, int origin );
形参: FILE* stream 文件指针
long int offset 偏移量
int origin 开始的位置
origin的几个参数:
常量 | 位置 |
---|---|
SEEK_SET | 文件的开始位置 |
SEEK_CUR | 文件指针当前的位置 |
SEEK_END | 文件的结束的位置 |
返回值: 设置成功,返回0
设置失败,返回非0数
注意:
指针的步长是一个字节
设置文件指针位置示例:
数据如下,我准备跳过两个字节,让文件指针指向C,然后读取
#include<stdio.h>
#include<stdlib.h>
int main()
{
char buffer;
//打开文件
FILE* p = fopen("test.txt", "r");
if (p == NULL)
{
printf("打开文件失败\n");
}
// 设置文件指针的位置
fseek(p, 2, SEEK_SET);
//读数据
fscanf(p, "%c", &buffer);
printf("%c", buffer);
//关闭文件
fclose(p);
p = NULL;
return 0;
}
输出:
C
(2).ftell
当你一顿操作,然后不知道文件指针指向哪里的时候,
可以使用ftell函数告诉你指针的位置。
long int ftell ( FILE * stream );
形参: FILE* stream
返回值: 相对文件起始位置的偏移量。
获取文件指针位置示例:
#include<stdio.h>
#include<stdlib.h>
int main()
{
int ret = 0;
//打开文件
FILE* p = fopen("test.txt", "r");
if (p == NULL)
{
printf("打开文件失败\n");
}
//获取文件指针位置, 此时获取的就是起始位置
ret = ftell(p);
printf("%d\n", ret);
//设置完文件指针,此时获取的就是相对起始位置的偏移量
fseek(p, 3, SEEK_SET);
ret = ftell(p);
printf("%d\n", ret);
//关闭文件
fclose(p);
p = NULL;
return 0;
}
0
3
(3).rewind
这个函数可以让文件指针快速回到起始位置,使用起来也很简单
void rewind ( FILE * stream );
形参: FILE* stream 文件指针
返回值: 没有返回值
示例:
#include<stdio.h>
#include<stdlib.h>
int main()
{
int ret = 0;
//打开文件
FILE* p = fopen("test.txt", "r");
if (p == NULL)
{
printf("打开文件失败\n");
}
//设置完文件指针,打印当前文件指针
fseek(p, 3, SEEK_SET);
ret = ftell(p);
printf("%d\n", ret);
//使用rewind函数将文件指针设置到起始位置
rewind(p);
ret = ftell(p);
printf("%d\n", ret);
//关闭文件
fclose(p);
p = NULL;
return 0;
}
输出:
3
0