目录
1.对比一组函数
1.1scanf
从标准输入(键盘)读取格式化的数据。
int main()
{
int x = 0;
scanf("%d", &x);
return 0;
}
1.2fscanf
而fscanf就比scanf高级了一点,可以说是fscanf包含了scanf的功能,对于这个函数的定义是这样子的:
从所有的输入流读取格式化的数据。
int fscanf ( FILE * stream, const char * format, ... );
实际上fscanf就多了一个FILE * 的参数,也就是打开的文件的地址参数,所有当我们进行使用的时候,我们可以先像scanf一样写出来,然后再在前面加上一个FILE * 的参数就好了。
//fscanf
struct S
{
int n;
double d;
};
int main()
{
struct S s = {0};
FILE* pf = fopen("data.txt", "r");//文本文件data.txt
if (NULL == pf)
{
perror("fopen");
return -1;
}
//读文件
fscanf(pf, "%d %lf", &(s.n), &(s.d));
printf("%d %lf\n", s.n, s.d);
//关闭文件
fclose(pf);
pf = NULL;
}
1.3sscanf
这里的意思就是从字符串中读取到格式化的数据,也就是说我们输入进去的数据,sscanf会转化成字符串的形式。也就是:
从字符串中读取一个格式化的数据。
int sscanf ( const char * s, const char * format, ...);
而对于参数我们可以再对比一下,前面的fscanf就是多了一个FILE *的指针,而现在这里就是改成了字符指针,也就是从字符指针中读一个数据,到后面的格式化数据里面去。
struct S
{
int n;
double d;
char name[10];
};
int main()
{
char arr[100] = { 0 };
struct S tmp = { 0 };
struct S s = { 100, 3.14, "zhangsan" };
//把一个格式化的数据转换成字符串
sprintf(arr, "%d %lf %s", s.n, s.d, s.name);
//打印
printf("%s\n", arr);
//从arr中的字符串中提取出一个格式化的数据
sscanf(arr, "%d %lf %s", &(tmp.n), &(tmp.d), tmp.name);
//打印
printf("%d %lf %s\n", tmp.n, tmp.d, tmp.name);
return 0;
}
1.4printf
printf就是一个打印函数,从键盘上输出,对于它来说就是:
把格式化的数据到标准输出(屏幕)上
int main()
{
int x = 0;
printf("%d", x);
return 0;
}
1.5fprintf
fprintf同样是比printf高级一点,它可以输出到更多的地方,也就是包括着printf的功能,他的定义是:
把格式化的数据输出到所有输出流(屏幕/文件)上
释义:将按格式指向的C字符串写入流。如果格式包括格式说明符(从%开始的子序列),则格式化后的附加参数将被格式化并插入到结果字符串中,替换各自的说明符。
int fprintf ( FILE * stream, const char * format, ... );
同样经过对比之后,我们发现fprintf和printf也就是差了一个FILE *的参数,所以我们同样可以先按照printf的格式写,然后再加上参数,就可以很好的使用fprintf了。
//fprintf(按某一种格式写入)
struct S
{
int n;
double d;
};
int main()
{
struct S s = { 100, 3.14 };
FILE* pf = fopen("data.txt", "w");//写入文本文件data.txt
if (NULL == pf)
{
perror("fopen");
return -1;
}
//写文件
fprintf(pf, "%d %lf", s.n, s.d);
//关闭文件
fclose(pf);
pf = NULL;
}
1.6sprintf
释义:在printf上使用格式时,使用相同的文本组成字符串,但内容不是打印出来,而是存储在str指向的缓冲区中的C字符串中。
实际上这里的意思就是:
把格式化的数据输出转换成字符串。
int sprintf ( char * str, const char * format, ... );
而对于这个函数参数,我们参照printf转换发现,这里就是多了一个字符指针,也就是从后面的输出的格式化中读数据,到前面的字符指针里面去。然后输出得到的就是字符串。
struct S
{
int n;
double d;
char name[10];
};
int main()
{
char arr[100] = { 0 };
struct S tmp = { 0 };
struct S s = { 100, 3.14, "zhangsan" };
//把一个格式化的数据转换成字符串
sprintf(arr, "%d %lf %s", s.n, s.d, s.name);
//打印
printf("%s\n", arr);
return 0;
}
2. 文件的随机读写
2.1 fseek
根据文件指针的位置和偏移量来定位文件指针
int fseek ( FILE * stream, long int offset, int origin );
2.2 ftell
返回文件指针相对于起始位置的偏移量
long int ftell ( FILE * stream );
2.3 rewind
让文件指针的位置回到文件的起始位置
void rewind ( FILE * stream );
2.4例子
int main()
{
FILE* pf = fopen("test.txt", "r");
if (NULL == pf)
{
perror("fopen()");
return 1;
}
//读文件
int ch = fgetc(pf);
printf("%c\n", ch);//a
fseek(pf, 2, SEEK_SET);//从开头的偏移量
ch = fgetc(pf);
printf("%c\n", ch);//c
fseek(pf, -4, SEEK_END);//从结尾的偏移量
ch = fgetc(pf);
printf("%c\n", ch);//d
fseek(pf, 2, SEEK_CUR);//文件指针的当前位置
ch = fgetc(pf);
printf("%c\n", ch);//f
int pos = ftell(pf);//返回文件指针相对于起始位置的偏移量
printf("%d\n", pos);//6
rewind(pf);//让文件指针的位置回到文件的起始位置
ch = fgetc(pf);
printf("%c\n", ch);//a
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
3. 文本文件和二进制文件
根据数据的组织形式,数据文件被称为文本文件或者二进制文件。
数据在内存中以二进制的形式存储,如果不加转换的输出到外存,就是二进制文件。
如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件。
一个数据在内存中是怎么存储的呢?
字符一律以ASCII形式存储,数值型数据既可以用ASCII形式存储,也可以使用二进制形式存储。
如有整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占用5个字节(每个字符一个字节),而二进制形式输出,则在磁盘上只占4个字节(VS2013测试)。
int main()
{
int a = 10000;
FILE* pf = fopen("test.txt", "wb");
fwrite(&a, 4, 1, pf);//二进制的形式写到文件中
fclose(pf);
pf = NULL;
return 0;
}
4. 文件读取结束的判定
4.1 被错误使用的feof
牢记:在文件读取过程中,不能用feof函数的返回值直接用来判断文件的是否结束。
而是应用于当文件读取结束的时候,判断是读取失败结束(返回0),还是遇到文件尾结束(返回非0)。
1. 文本文件读取是否结束,判断返回值是否为 EOF ( fgetc ),或者 NULL ( fgets )
例如:
- fgetc 判断是否为 EOF(为0) .
- fgets 判断返回值是否为 NULL(为0) .
2. 二进制文件的读取结束判断,判断返回值是否小于实际要读的个数。
例如:
- fread判断返回值是否小于实际要读的个数。
正确的使用:
文本文件的例子:
#include<stdlib.h>
int main()
{
int c;
//打开文件
FILE* pf = fopen("test.txt", "r");;
//判空
if (pf == NULL)
{
perror(" File opening failed\n");
return 1;
}
//fgetc 当读取失败的时候或者遇到文件结束的时候,都会返回EOF
// 标准C I/O读取文件循环
while ((c=fgetc(pf)) != EOF)
{
putchar(c);
}
//判断是什么原因结束的
if (ferror(pf))
puts("I/O error when reading");
else if (feof(pf))
puts("End of file reached successfully");
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
二进制文件的例子:
enum { SIZE = 5 };
int main(void)
{
double a[SIZE] = {1.,2.,3.,4.,5.};
FILE *fp = fopen("test.bin", "wb"); // 必须用二进制模式
fwrite(a, sizeof *a, SIZE, fp); // 写 double 的数组
fclose(fp);
double b[SIZE];
fp = fopen("test.bin","rb");
size_t ret_code = fread(b, sizeof *b, SIZE, fp); // 读 double 的数组
if(ret_code == SIZE) {
puts("Array read successfully, contents: ");
for(int n = 0; n < SIZE; ++n) printf("%f ", b[n]);
putchar('\n');
} else { // error handling
if (feof(fp))
printf("Error reading test.bin: unexpected end of file\n");
else if (ferror(fp)) {
perror("Error reading test.bin");
}
}
fclose(fp);
}
5. 文件缓冲区
ANSIC 标准采用“缓冲文件系统”处理的数据文件的,所谓缓冲文件系统是指系统自动地在内存中为程序中每一个正在使用的文件开辟一块“文件缓冲区”。从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上。如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的大小据C编译系统决定的。
#include<windows.h>
int main()
{
//打开文件
FILE* pf = fopen("test.txt", "w");
//将代码放入缓冲区
fputs("abcdef", pf);
printf("睡眠10秒-已经写数据了,打开test.txt文件,发现文件没有内容\n");
Sleep(10000);
printf("刷新缓冲区\n");
//刷新缓冲区时,才将输出缓冲区的数据写到文件(磁盘)
fflush(pf);
printf("再睡眠10秒-此时,再次打开test.txt文件,文件有内容了\n");
Sleep(10000);
fclose(pf);
//注:fclose在关闭文件的时候,也会刷新缓冲区
pf = NULL;
return 0;
}