由于并未在前5天学习过程中记录,所以题目开始于第6天,后续会抽空填补上前5天的内容。第6天主要是文件读写的训练。
今天上午的流程如下:
一.文件读写api
Fgetc fputc 按照字符读写文件
Fputs fgets 按照行读写文件(读写配置文件)
Fread fwrite 按照块读写文件(大数据块迁移)
按照格式化进行读写文件
二.文件控制api
文件是否结束
文件指针的定位和跳转
三.Api做项目
********************************************************************************************************************************
一 文件读写api
文件打开
分类:文件打开 文件关闭
fopen
函数举例:fopen("txt", "r")
功能:打开指定文件,并将fp指向此文件
返回值:NULL或非NULL
说明:
1.用“r“方式打开的文件只能用于向计算机输入而不能用作向该文件输出数据,而且该文件应用已经存在,不能用”r“方式打开一个并不存在的文件(即输入文件),否则出错;
2.用”w“方式打开的文件只能用于向该文件写数据(即输出文件),而不能用来向计算机输入。如果原来不存在该文件,则在打开时新建立一个以指定的名字命名的文件。如果原来已经存在一个该文件名命名的文件,则在打开时将该文件删除,然后重新建立一个新的文件;
3.如果希望向文件末尾添加新的数据(不希望删除原有数据),则用”a“方式打开,但是此时该文件必须存在,否则将得到错误的信息,打开时,位置指针指向文件的末尾;
4.用”r+“,”w+", "a+“方式打开的文件既可以用来输入数据,也能用来输出数据。用”r“方式时,该文件应该已经存在,以便能向计算机输入数据。用”w+"方式则新建立一个文件,先向此文件些数据,然后可以读此文件中的数据。用“a”方式打开的文件,原来的文件不能删除,位置指针移到文件末尾,可以添加,可以读。
5.如果不能实现“打开”的任务,fopen函数将会带回一个错误的信息。出错的原因可能用“r”方式打开一个并不存在的文件,磁盘出故障;磁盘已满无法建立新文件等。此时fopen函数将带回一个空指针值NULL(NULL在stdio.h文件中已被定为0)。
常用下面的方法打开一个文件:
if((fp =fopen("file","r") == NULL)
{
printf("cannot open this file\n");
exit(0);
}
即先检查打开的操作是否有错误,如果有错误就显示不能打开此文件。exit函数的作用是关闭所有的文件,终止正在执行的程序,等待用户检查出错误,修改后再运行。
6.在向计算机输入文本文件时,将回车换行符转换为一个换行符,在输出时把换行符转换成为回车和换行两个字符。在用二进制文件时,不进行这种转换,在内存中的数据的形式与输出到外部文件中的数据形式完全一致,一一对应。
函数原型:int fclose(FILE *fp) -- fclose(fp)
功能:关闭fp指向的文件
返回值:正常关闭为0,出错为非0;
文件读写
分类:字符读写函数,字符串读写函数,块读写函数,格式化文件输入输出函数
fputc
函数原型:int fputc(int c, FILE *fp)
功能:把一字节代码c写入fp指向的文件中
返回值:正常返回c,出错返回EOF(-1)
fgetc
函数原型:int fgetc(FILE *fp)
功能:从fp指向的文件中读取一字节代码
返回值:正常返回到读到的代码值,读到文件尾或出错则为EOF
feof
函数原型:int feof(FILE *fp)
功能:判断文件是否结束
返回值:文件结束
定义函数int fputs(const char * s, FILE * stream);
函数说明:fputs()用来将参数s所指向的字符串写入到参数stream所指向的文件内
返回值:若成功返回写出的字符个数,出错返回EOF
表示从文件按中读出的字符数不超过n-1,存储到字符数组str中,并在末尾加上结束表示‘\0',换言之,n代表了字符数组的长度,即sizeof(str)。如果读取过程遇到换行符或文件结束标志,读取操作结束。若正常读取,返回指向str代表字符串的指针,否则,返回NULL(空指针)。
直接给出代码吧,用于备忘。需要说明的是,由于注释在VS2013里是对齐的,但是复制过来就对不齐了,所以这里全部将注释放在被解释语句的上一行。
#define _CRT_SECURE_NO_WARNINGS
#include "stdlib.h"
#include "stdio.h"
#include "string.h"
//按字符串写文件
void main01()
{
int i = 0;
//创建文件FILE指针类型指针fp
FILE *fp = NULL;
//初始化一字符串,用于写入文件
char a[] = "abcdefgdddd";
//文件路径的书写,使用如下格式在linux和win下通用
char *filename = "C:/Users/Administrator/Desktop/11.txt";
//打开文件,r+表示文件开头,a+表示文件结尾
fp = fopen(filename, "a+");
//标准流程,判断是否打开
if (fp == NULL)
{
printf("func fopen() err\n");
return;
}
//按字符串写入文件
for (i = 0; i < strlen(a); i++)
{
fputc(a[i], fp);
}
//关闭文件
fclose(fp);
system("pause");
}
//按字符串读文件
void main02()
{
int i = 0;
//创建文件FILE指针类型指针fp
FILE *fp = NULL;
FILE *filename = "C:/Users/Administrator/Desktop/11.txt";
//打开文件,r+表示文件开头,a+表示文件结尾
fp = fopen(filename,"r");
if (fp == NULL)
{
printf("func fopen() err\n");
return;
}
//判断是否到文件的末尾
while (!feof(fp))
{
//按字符串读文件
char tmpc = fgetc(fp);
printf("%c", tmpc);
}
printf("\n");
fclose(fp);
system("pause");
}
//按照行读写文件
void main()
{
FILE *fp = NULL;
char str[100];
char *filename = "C:/Users/Administrator/Desktop/11.txt";
fp = fopen(filename, "r");
if (fp == NULL)
{
printf("func fopen() err\n");
return;
}
//此处添加断点,查看是否按行打印
while (!feof(fp))
{
char *tmp = fgets(str, 100, fp);
if (tmp != NULL)
{
printf("%s", str);
}
}
fclose(fp);
system("pause");
}
以上代码是学习过程中编写,可以直接执行,需要注意的是,上面有多个main主函数入口,需要执行哪个,就把其他main函数取个别名即可。
关于字符串读写函数fgets和fputs函数的说明
按块读写文件-----fread函数和fwrite函数
用getc和putc函数可以用来读写文件中的一个字符。但是常常要求一次读入一组数据(例如,一个实数或一个结构体变量的值),ANSI C标准提出设置两个函数(fread和fwrite),用来读写一个数据块。他们的一般调用函数为:
fread(buffer,size,count,fp)
fwrite(buffer,size,count,fp)
其中:
buffer:是一个指针,对fread来说,它是读入数据的存放地址。对于fwirete来说,是要输出数据的地址(以上指的是起始地址)。
size:要读写的字节数。
count:要进行读写多少个size字节的数据项。
fp:文件型指针。
如果文件以二进制形式打开,用fread和fwrite函数就可以读写任何类型的信息。
例如:
fread(f,4,2,fp);
其中,f是一个实型数组名。一个实型变量占4个字节。这个函数从fp所指向的文件读入2个4字节的数据,存储到数组f中。
如果有一个如下结构体类型:
struct student_type
{char name[10];
int num;
int age;
char addr[30];
}stud[40];
结构体数组stud有40个元素,每个元素用来存放一个学生的数据(包括姓名,学号,年龄,地址)。假设学生的数据已存放在磁盘文件中,可以用下面的for语句和fread函数读入40个学生的数据:
for(i=0;i<40;i++)
fread(&stud[i], sizeof(struct studeng_type), 1, fp);
同样,以下for语句和fwrite函数可以将内存中的学生数据输出到磁盘文件中去:
for(i=0;i<40;i++)
fwrite(&stud[i], sizeof(struct student_type) ,1 fp);
如果函数fread和fwrite调用成功,则函数返回值为count的值,即输入或者输出数据项的完整个数。
例1:从键盘输入 4个学生的有关数据,然后把它们转存到磁盘文件上去。
#define _CRT_SECURE_NO_WARNINGS
#include "stdlib.h"
#include "stdio.h"
#define SIZE 4
struct student_type
{
char name[10]; //姓名
int num; //学号
int age; //年龄
char addr[15]; //地址
}stud[SIZE];
void save()
{
FILE *fp;
int i;
//打开文件,wb是如果没有就创建,rb是如果没有就出错
if ((fp = fopen("stu_list", "wb")) == NULL)
{
printf("cannot open file\n");
return;
}
for (i = 0; i < SIZE; i++)
{
//写入数据,并判断是否写入正确
if (fwrite(&stud[i], sizeof(struct student_type), 1, fp) != 1)
printf("file write error\n");
}
//关闭
fclose(fp);
}
void main()
{
int i;
for (i = 0; i < SIZE; i++)
{
scanf("%s%d%d%s", stud[i].name, &stud[i].num, &stud[i].age, stud[i].addr);
save();
}
}
请注意输入输出数据的状况。从键盘输入4个学生的数据是ASCII码,也就是文本文件。在送到计算机内存时,回车和换行符转换成一个换行符。再从内存以“wb”方式(二进制写)输出到“stu_list”文件,此时不发生字符转换,按内存中存储形式原样输出到磁盘文件上。在上面验证程序中,又用fread函数从“stu_list”文件向内存读入数据,注意此时用的是“rb”方式,即二进制方式,数据按原样输入,也不发生字符转换。也就是这时候内存中的数据恢复到第一个程序向“stu_list”输出以前的情况。最后在验证程序中,用printf函数输出到屏幕,pirntf是格式输出函数,输出ASCII码,在屏幕上显示字符。换行符又转换为回车加换行符。
如果企图从“stu_list”文件中以“r”方式读入数据就会出错。
fread和fwrite函数一般用于二进制文件的输入输出。因为他们是按照数据块的长度来处理输入输出的,在字符发生转换的情况下很可能出现与原设想的情况不同。
关于具体的函数说明可以参考谭浩强老师的C语言书,下面是我保存的《C程序设计》的电子版下载地址:
http://pan.baidu.com/s/1c02fMpi
关于二三后续给出。
给出一段备份代码,学习以下:
//cfg_op.c
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
int GetCfgItem(char *pFileName /*in*/, char *pKey /*in*/, char * pValue/*in out*/, int * pValueLen /*out*/);
int WriteCfgItem(char *pFileName /*in*/, char *pItemName /*in*/, char *pItemValue/*in*/, int itemValueLen /*in*/);
//实现流程
//打开文件
//按照行的方式 循环读文件
//解析每一行,若匹配key关键字,在进行value值的提取
//提取value值需要 1去除前后空格 1级指针典型应用
#define LineMaxLen 2048
#define KeyMaxLen 64
int GetCfgItem(char *pFileName /*in*/, char *pKey /*in*/, char * pValue/*in out*/, int * pValueLen /*out*/)
{
int rv = 0;
FILE *fp = NULL;
char lineBuf[LineMaxLen];
char *pTmp = NULL, *pBegin = NULL, *pEnd = NULL;
if (pFileName==NULL || pKey==NULL || pValue==NULL || pValueLen==NULL)
{
rv = -1;
printf("GetCfgItem() err. param err \n");
goto End;
}
fp = fopen(pFileName, "r");
if (fp == NULL)
{
rv = -2;
printf("fopen() err. \n");
goto End;
}
while (!feof(fp))
{
//读每一行
memset(lineBuf, 0, sizeof(lineBuf));
pTmp = fgets(lineBuf, LineMaxLen, fp);
if (pTmp == NULL)
{
break;
}
//不含=, 非配置项
pTmp = strchr(lineBuf, '=');
if (pTmp == NULL)
{
continue;
}
//key是否在本行
pTmp = strstr(lineBuf, pKey);
if (pTmp == NULL)
{
continue;
}
//调整到=右边,取value准备
pTmp = strchr(lineBuf, '=');
if (pTmp == NULL)
{
continue;
}
pTmp = pTmp + 1;
//获取value 起点
while (1)
{
if (*pTmp == ' ')
{
pTmp ++ ;
}
else
{
pBegin = pTmp;
if (*pBegin == '\n')
{
//没有配置value
printf("配置项:%s 没有配置value \n", pKey);
goto End;
}
break;
}
}
//获取valude结束点
while (1)
{
if ((*pTmp == ' ' || *pTmp == '\n'))
{
break;
}
else
{
pTmp ++;
}
}
pEnd = pTmp;
//赋值
*pValueLen = pEnd-pBegin;
memcpy(pValue, pBegin, pEnd-pBegin);
break;
}
End:
if (fp != NULL)
{
fclose(fp);
}
return rv;
}
//实现流程
//循环读每一行,检查key配置项是否存在 若存在修改对应value值
//若不存在,在文件末尾 添加 "key = value"
//难点:如何修改文件流中的值
int SetCfgItem(char *pFileName /*in*/, char *pKey /*in*/, char * pValue/*in*/, int ValueLen /*in*/)
{
int rv = 0, iTag = 0, length = 0;
FILE *fp = NULL;
char lineBuf[LineMaxLen];
char *pTmp = NULL, *pBegin = NULL, *pEnd = NULL;
char filebuf[1024*8] = {0};
if (pFileName==NULL || pKey==NULL || pValue==NULL)
{
rv = -1;
printf("SetCfgItem() err. param err \n");
goto End;
}
fp = fopen(pFileName, "r+");
if (fp == NULL)
{
rv = -2;
printf("fopen() err. \n");
//goto End;
}
if (fp == NULL)
{
fp = fopen(pFileName, "w+t");
if (fp == NULL)
{
rv = -3;
printf("fopen() err. \n");
goto End;
}
}
fseek(fp, 0L, SEEK_END);
//获取文件长度;
length = ftell(fp);
fseek(fp, 0L, SEEK_SET);
if (length > 1024*8)
{
rv = -3;
printf("文件超过1024*8, nunsupport");
goto End;
}
while (!feof(fp))
{
//读每一行
memset(lineBuf, 0, sizeof(lineBuf));
pTmp = fgets(lineBuf, LineMaxLen, fp);
if (pTmp == NULL)
{
break;
}
//key关键字是否在本行
pTmp = strstr(lineBuf, pKey);
if (pTmp == NULL)
{
strcat(filebuf, lineBuf);
continue;
}
else
{
sprintf(lineBuf, "%s = %s\n", pKey, pValue);
strcat(filebuf, lineBuf);
//若存在key
iTag = 1;
}
}
//若不存在 追加
if (iTag == 0)
{
fprintf(fp, "%s = %s\n", pKey, pValue);
}
else //若存在
{
if (fp != NULL)
{
fclose(fp);
fp = NULL; //避免野指针
}
fp = fopen(pFileName, "w+t");
if (fp == NULL)
{
rv = -4;
printf("fopen() err. \n");
goto End;
}
fputs(filebuf, fp);
//fwrite(filebuf, sizeof(char), strlen(filebuf), fp);
}
End:
if (fp != NULL)
{
fclose(fp);
}
return rv;
}
// cfg_op.h
#ifndef _INC_CFG_OP_H
#define _INC_CFG_OP_H
#ifdef __cplusplus
extern "C" {
#endif
int GetCfgItem(char *pFileName /*in*/, char *pKey /*in*/, char * pValue/*in out*/, int * pValueLen /*out*/);
int WriteCfgItem(char *pFileName /*in*/, char *pItemName /*in*/, char *pItemValue/*in*/, int itemValueLen /*in*/);
//int CfgItem_Init(void *pHandle, int iType);
//int GetCfgItem(void *pHandle /*in*/, char *pKey /*in*/, char * pValue/*in out*/, int * pValueLen /*out*/);
//int WriteCfgItem(void *pFileName /*in*/, char *pItemName /*in*/, char *pItemValue/*in*/, int itemValueLen /*in*/);
//int CfgItem_Destory(void *pHandle);
#ifdef __cplusplus
}
#endif
#endif