文章目录
前言
1.对于“流”的基本理解
2.文件的顺序读写
2.1 适用于所有“流”的函数
2.1.1 字符操作函数fputc和fgets
2.1.2 字符串操作函数fgets和fputs
2.1.3 格式化数据函数fprintf和fscanf
2.2 只适用于文件的函数
2.2.1 二进制数据函数fwrite和fread
2.3 *格式化数据与字符串之间的转换存储
2.3.1 sprintf函数与sscanf函数
3.文件的随机读写
3.1 fseet函数
3.2 ftell函数
3.3 rewind函数
前言
我们已经知道可以将数据存放到文件中进行储存,但是数据的类型却又很多种,我们针对这些不同类型的数据有着不同的操作函数,这些函数以特定的方式将数据写入文件、读取到内存。
对于流的基本理解
流是磁盘或其它外围设备中存储的数据的源点或终点。有输入流和输出流。在我们输入数据和输出数据的时候,是不用考虑输入的内容最终回到哪,如果每个数据均考虑其来龙去脉,这样将会增加程序员的负担,有了“流”的概念,我们只需要考虑输入数据,具体数据会以什么方式呈现到什么地方则由“流”来决定。
C程序运行默认打开三个流
FIRE* stdin 标准输入流(键盘)
FIRE* stdout 标准输出流(屏幕)
FIRE* stderr 标准错误流
文件的顺序读写 —— 适用于所有流的函数
一、字符操作函数 - fputc和fgets
1.fputc函数
int fputc( int c, FILE *stream );
int c:为写入的字符,为int类型是因为放入的是ASCII码值
FILE *stream:文件指针
该函数针对单个字符进行操作,若写入成功则其返回值为所写入字符的ASCII码值,若写入失败或写入结束均返回EOF。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
//打开文件
FILE* pf = fopen("d:\\users\\rich\\桌面\\test.txt", "w");
//判断是否打开成功
if (pf == NULL)
{
perror(NULL); //判断错误
return 1;
}
char i = 0;
for (i = 'a'; i <= 'z'; i++)
{
fputc(i, pf);
}
fclose(pf); //关闭文件
pf = NULL; //及时置空,防止其变成野指针
return 0;
}
成功写入文件
2.fgetc函数
int fgetc( FILE *stream );
FILE *stream:文件指针
该函数可将写入文件的数据读取出来。若读取成功,其返回值为读取到数据的ASCII码值,若读取失败或结束则返回EOF。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
//打开文件
FILE* pf =fopen("d:\\users\\rich\\桌面\\test.txt", "r");
//判断是否打开成功
if (pf == NULL)
{
perror(NULL); //判断错误
return 1;
}
int cmp = 0;
while ((cmp = fgetc(pf)) != EOF)
{
printf("%c ", cmp);
}
fclose(pf);
pf = NULL;
return 0;
}
读取并打印
二、字符串操作函数 - fgets和fputs
1.fputs函数
int fputs( const char *string, FILE *stream );
const char *string:为写入的字符串
FILE *stream :是文件指针变量
该函数可将字符串写入文件当中,若写入成功则返回一个非负的值,若失败时则返回EOF。如下代码,我们用变量、n来承担函数的返回值,通过调试发现n的值为0。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
//打开文件
FILE* pf = fopen("d:\\users\\rich\\桌面\\test.txt", "w");
//判断是否打开成功
if (pf == NULL)
{
perror(fopen); //判断错误
return 1;
}
int n = fputs("hello world", pf);
fclose(pf);
pf = NULL;
return 0;
}
成功写入
2.fgets函数
char *fgets( char *string, int n, FILE *stream );
char *string:读取的字符串放入的地方
int n:读取的个数,实际读取的比自己希望的个数小1,因为要默认放一个 \0
FILE *stream :是文件指针变量
该函数可以读取写入文件的字符串,若读取成功则返回存放读取后存放字符串的地址,若读取失败则返回NULL。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
//打开文件
FILE* pf = (FILE*)fopen("d:\\users\\rich\\桌面\\test.txt", "r");
//判断是否打开成功
if (pf == NULL)
{
perror(fopen); //判断错误
return 1;
}
char arr[30];
char* p = fgets(arr, 5, pf);
printf("%s\n", arr);
fclose(pf);
pf = NULL;
return 0;
}
读取结果:代码中需要读取5个字符,实际读取到4个字符和一个 ‘\0’
三、格式化数据函数 - fprintf和fscanf
1.fprintf函数
int fprintf(FILE* stream, const char* format[, argument]...);
FILE *stream :文件指针变量
const char* format[, argument]...:与printf的参数类似,需要写入的类型,需要写入数据的名称
该函数可以存储格式化数据,各种类型均可存储,由于类型比较多,所以传参的时候需要传数据的类型、数据名。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
struct S
{
char name[10];
int age;
float score;
};
int main()
{
struct S s = { "zhangsan",18,149.5f };
//打开文件
FILE* pf = fopen("d:\\users\\rich\\桌面\\test.txt", "w");
//判断是否打开成功
if (pf == NULL)
{
perror(fopen); //判断错误
return 1;
}
int n = fprintf(pf, "%s %d %f", s.name, s.age, s.score);
fclose(pf);
pf = NULL;
return 0;
}
成功写入
2.fscanf函数
int fscanf( FILE *stream, const char *format [, argument ]... );
FILE *stream:文件指针
const char* format[, argument]...:与scanf类似,需要取得数据的类型,需要取类型的位置
读取写入的数据,本函数需要对写入的数据的类型要有了解。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
struct S
{
char name[10];
int age;
float score;
};
int main()
{
struct S s = { 0 };
//打开文件
FILE* pf = fopen("d:\\users\\rich\\桌面\\test.txt", "r");
//判断是否打开成功
if (pf == NULL)
{
perror(fopen); //判断错误
return 1;
}
fscanf(pf, "%s %d %f", s.name, &(s.age), &(s.score));
printf("%s %d %f\n", s.name, s.age, s.score);
//屏幕
fprintf(stdout, "%s %d %f", s.name, s.age, s.score);
//将读取后放入结构体中的数据,修改流的方向到屏幕上,则相当于打印了
fclose(pf);
pf = NULL;
return 0;
}
代码中在fscanf函数读取数据存入结构体变量中后,用fprintf函数,将其流的指向修改为“屏幕”,这样就将数据直接打印在屏幕中,和前面“流”的介绍相对应。
*格式化数据与字符串之间的转换存储——不针对文件操作
1.sprintf函数
int sprintf ( char * str, const char * format, ... );
char * str:需要存放数据的地址
const char * format, ... :rintf内容一样,数据类型,数据名
该函数将格式化数据转化成字符串进行存储。
2.sscanf函数
int sscanf ( const char * s, const char * format, ...);
const char * s:取数据的地址
const char * format, ...:与scanf函数一样,数据类型,数据地址
该函数将sprintf函数以字符串形式存储的数据以格式化的形式读取出来。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
struct S
{
char name[10];
int age;
float score;
};
int main()
{
struct S s = { "zhangsan",18,149.5f };
char arr[100];
struct S cmp = { 0 };
sprintf(arr, "%s %d %f", s.name, s.age, s.score);
printf("字符串:%s\n", arr);
sscanf(arr, "%s %d %f", cmp.name, &(cmp.age), &(cmp.score));
printf("格式化:%s %d %f", cmp.name, cmp.age, cmp.score);
return 0;
}
文件的顺序读写 —— 只适用于文件的函数
1.fwrite函数
size_t fwrite( const void *buffer, size_t size, size_t count, FILE *stream );
const void *buffer::需要写入数据的位置
size_t size:需要写入数据的大小
size_t count:需要写入数据的数量
该函数可将数据以二进制的形式写入文件当中。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
struct S
{
char name[10];
int age;
float score;
};
int main()
{
struct S s = { "zhangsan",18,149.5f};
//打开文件
FILE* pf = (FILE*)fopen("d:\\users\\rich\\桌面\\test.txt", "w");
//判断是否打开成功
if (pf == NULL)
{
perror(fopen); //判断错误
return 1;
}
unsigned int n = fwrite(&s, sizeof(struct S), 1, pf);
fclose(pf);
pf = NULL;
return 0;
}
成功写入
2.fread函数
size_t fread(void* buffer, size_t size, size_t count, file* stream);
void* buffer:读取后放的位置
size_t size:需要写入数据的大小
size_t count:需要写入数据的数量
该函数可以读取文件中以二进制存储的数据。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
struct S
{
char name[10];
int age;
float score;
};
int main()
{
struct S s = { 0 };
//打开文件
FILE* pf = fopen("d:\\users\\rich\\桌面\\test.txt", "r");
//判断是否打开成功
if (pf == NULL)
{
perror(fopen); //判断错误
return 1;
}
fread(&s, sizeof(struct S), 1, pf);
printf("%s %d %f\n", s.name, s.age, s.score);
fclose(pf);
pf = NULL;
return 0;
}
读取到内存后打印
文件的随机读写
在上述的一系列函数中,函数都是按照从头到尾的顺序一次性进行下去,若我们想对数据中间有什么操作,用上述函数则无法实现,为了能更加灵活的操作文件中的数据,我们引入了文件的随机读写函数,即按照操作者的意愿进行读写操作。
假设文件中存放的有字符‘a’、‘b’、‘c’、‘d’、‘e’、‘f’、‘g’、‘h’,我们以函数fgetc想读取到字符‘c’。在读取字符。刚开始时文件指针默认指向起始位置。
成功读取完字符‘a’后将会,文件指针指向将会下移到下一个字符的位置,即指向‘b’
成功读取完字符‘b’后将会,文件指针指向将会下移到下一个字符的位置,即指向‘c’,则可以读取到字符‘c’。
上述顺序读法很显然无法直接一次就读取到字符‘c’。下面就介绍一下随机读取。
1.fseek函数
该函数按照某个偏移量来读取字符。
int fseek(FILE* stream, long offset, int origin);
FILE *stream :是文件指针变量
long offset:偏移的字节数
int origin:从哪个位置开始算偏移量
以哪个位置为起点读取,需要设置相应的位置,有以下三种形式:
SEEK_SET | 起始位置 |
SEEK_END | 末尾位置(相对于起始位置偏移量为负数,最后一个元素之后,并不是指向的最后一个元素) |
SEEK_CUR | 当前文件指针的位置 |
1.相对于起始地址向右偏移2个字节后读取字符‘c’.(偏移量:向某个方向数的第几个元素)
fseek(p, 2, SEEK_SET);
char ch = fgetc(p);
printf("%c\n", ch);
2.相对于当前位置(字符‘c’的位置)向右偏移3个字节读取字符‘g’。
fseek(p, 3, SEEK_CUR);
char ch = fgetc(p);
printf("%c\n", ch);
3.以末尾位置为起点向左偏移8个字节读取字符‘a’
fseek(p, -8, SEEK_END);
char ch = fgetc(p);
printf("%c\n", ch);
2. ftell函数
用于返回文件指针相对于起始位置的偏移量(偏移量:简单理解为从左到右为第几个元素)
long ftell( FILE *stream );
参数只需要传一个文件指针
例如计算字符‘c’的偏移量
fseek(p, 2, SEEK_SET);
char ch = fgetc(p);
printf("%c\n", ch);
printf("%d\n", ftell(p));
字符‘c’为第三个元素,所以计算出其偏移量为3。
3.rewind函数
void rewind ( FILE * stream );
参数只需上传文件指针
在进行上述随机读写时容易遗忘现在文件指针指向的位置,该函数的作用是让文件指针指向起始位置。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
FILE* p = fopen("d:\\users\\rich\\桌面\\test.txt", "r");
if (p == NULL)
{
perror(main);
return 1;
}
fseek(p, 2, SEEK_SET);
char ch = fgetc(p);
printf("%c\n", ch); //c
printf("%d\n", ftell(p)); //3
rewind(p); //让指针指向起始位置
ch = fgetc(p);
printf("%c\n", ch); //a
fclose(p);
p = NULL;
return 0;
}
输出结果:
彩蛋
我们已经知道可以将文件指针移动到末尾位置,又知道如何求偏移量,我们就可以通过这两个函数配合计算出放入文件的数据的大小。
fseek(p, 0, SEEK_END); //让文件指针指向末尾
printf("%d\n", ftell(p)); //计算起始位置到末尾位置的偏移量(元素个数)