文件分类:操作中用到的二进制和ASCII方式。
二进制文件
形式:把内存中的数据按其在内存中的存储形式原样输出到磁盘上存放。
优点:可以节省外存空间和转换时间。
缺点:一个字节并不对应一个字符,不能直接输出字符形式。
一般中间结果数据需要暂时保存在外存上,以后又需要输入内存的,常用二进制文件保存。
ASCII文件形式:
每一个字节放一个ASCII代码。
优点:便于对字符进行逐个处理,也便于输出字符。
缺点:一般占存储空间较多,而且要花费转换时间。
C语言文件指针定义
文件的读写(头文件均为stdio.h)
fopen函数
头文件:stdio.h
功 能:以type方式打开filename文件并返回该文件的指针
用 法:FILE *fopen(char *filename,char *type);
返回值:filename的文件指针;如果打开失败fopen 函数返回一个NULL 指针;
特点就是单个字母的是 "文件文本" 的操作,双字母的是二进制的”文件操作“。
常用的文件操作是 "r" "w" "a",追加最是常用。
fclose函数
头文件:stdio.h
功 能:关闭一个流
用 法:int fclose(FILE *stream);
返回值:成功返回0,不成功返回EOF(-1)
字符读写函数
fgetc函数
功 能:从fp所指向的文件中读取字符
用 法:int fgetc(FILE *fp);
返回值:返回文件fp所指向的文件中的字符值(EOF为文件尾)
补 充:
1.调用该函数时,文件使用方式必须是以读或读写方式打开的。
2.在文件内部有一个位置指针,用来指向文件的当前读写
fputc函数
功能:将字符(ch的值)输出到fp所指向的文件中去。
用法:int futc(int ch,FILE *fp);
返回值:写入成功返回写入字符ch
不成功返回EOF
int main()
{
FILE *fp = fopen("text.txt", "a+");
if (NULL != fp)
{
char str[] = "this text is success !";
int len = strlen(str);
int i = 0;
while (len--)
{
fputc(str[i] , fp);
i++;
}
fputc(EOF, fp);
fseek(fp ,0L ,SEEK_SET);
char ch = fgetc(fp);
while (ch != EOF)
{
printf("%c", ch);
ch = fgetc(fp);
}
}
return 0;
}
字符串读写函数
fgets函数
功 能:从fp所指向的文件(stdin特殊文件)中读取长度为n的字符串保存到string中
用 法:char *fgets(char *str, int n, FILE *fp);
返回值:成功,返回str
1. 当n<=0 时返回NULL,即空指针。
2. 当n=1 时,返回空串"".
3. 如果读入成功,则返回缓冲区的地址。
4. 如果读入错误或遇到文件结尾(EOF),则返回NULL.
char *fgets(char *str, int n, FILE *stream) { register int c; register char *cstr; cstr=str; while(--n>0 &&(c = getc(stream))!=EOF) if ((*cstr++= c) =='\n') break; *cstr ='\0'; return (c == EOF && cstr == s) ?NULL :str ; } //可以看出读的是n-1的数据流;
fgets(...)读入文本行时的两种情况。
1.如果n大于一行的字符串长度,那么当读到字符串末尾的换行符时,fgets(..)会返回。并且在s的最后插入字符串结束标志'\0'。 而s缓冲区剩余的位置不会再填充。
example:
123abc
fgets(s,10,fp);
此时,读入七个字符,123abc\n,实际上还有最后的'\0',所以,strlen(s)=7; 如果要去除末尾的\n,s[strlen(s)-1]='\0';便可。
2.如果n小于等于一行的字符串的长度,那么读入n-1个字符,此时并没有读入\n因为并没有到行尾 ,同样在最后会插入'\0'.
example:
123abc
char s[5];
fgets(s,5,fp);
这时读入4个字符,123a,并没有换行符,所以strlen(s)=4.
注意:
fgets( ) 读入n-1个字符串,后加上 ‘ \n ’ 和 ‘ \0 ’ 字符,所以它的实际字符长度是 n ,最后一位是 ‘ \n ’
int main() { char str1[200]; fgets(str1, 200, stdin); char *pStr = str1; int a = strlen(pStr); //for (pStr; *pStr != '\n';pStr++); while (*pStr++ != '\n'); printf("%d ,%s\n", a , pStr-2); return 0; }
输入1+‘ \n ’ 查看;存在回车
下
fputs函数
功 能:将字符串string写入fp所指向的文件中。
用 法:int fputs(char *string, FILE *fp);
返回值:输入成功,返回值0
输入失败,返回EOF
数据块读写函数
fread函数
功 能:从fp指向的文件中读取n个size大小的数据写入ptr指向的地方
用 法:int fread(void *ptr, int size, int n, FILE *fp);
返回值:成功,返回读取元素个数 也就是 n,所以它的二三参数注意位置
不成功,返回的是一个小于n 的数所以,要注意条件的判断。
参数说明:
ptr:读入数据的存放地址(首地址)
size:要读写的字节数
n:要进行读写多少个size字节的数据项
1.在使用fread函数的时候,最好使用下面的形式:fread(buf, sizeof(char), sizeof(buf), p);
单个字节读取,每次读取缓冲区的长度,这样就不会出现有些字节被舍弃的情况了。
2.fread可以读二进制文件,有时用字符方式去读文件不能读完整个文件,但是二进制方式就可以 。
这就是因为字符方式用特定的标记结尾的,读取时只要碰到该标记就自动结束。
这就要求用fread() 函数文件打开方式是 "rb" "rb+"这中双字母的读/写方式,例如:open("text.txt" , "rb")
3.在这里, 再次强调一遍: fread, read, recv, fwrite, write, send这类函数针对的是字符(无边界), 而不是字符串(以'\0'作为边界), 与'\0'没有半毛钱的关系。
4.注意到的问题是fread函数不能区分文件是否结尾和出错两种情况。所以必须使用ferror()和feof()函数来确定到底是哪种情况,
fwrite函数
功 能: 从ptr指向的地方读取n个size大小的数据写入fp指向的文件中
用 法:int fwrite(void *ptr, int size, int n, FILE *fp);
返回值:返回写入文件的实际个数
参数说明:ptr:输出数据的地址(首地址)其余同上
注意:这个函数以二进制形式对文件进行操作,不局限于文本文件(最好是二进制 的)
下面是例子:
//我们定义一个结构体,fread() 和 fwrite()多数用与结构体的操作 #define SIZE 5 typedef struct { char name[20]; char num[15]; char age; }student;
//向file_name 的文件中写入,以 bool fwrite() { FILE *fp; int i; char file_name[50]; printf("Please input your filename:"); scanf("%s", file_name); if (!(fp = fopen(file_name, "w+"))) { printf("Can not open %s\n", file_name); return false; } for (i = 0; i < SIZE; i++) { if (fwrite(&stu[i], sizeof(student), 1, fp) != 1) { printf("D'ont write file!\n"); } } fclose(fp); return true; }
//读文件函数 bool fread() { FILE *fp; int i; char file_name[50]; printf("Please input the filename you want read:"); scanf("%s", file_name); if (!(fp = fopen(file_name, "rb"))) { printf("Can not open %s\n", file_name); return false; } fseek(fp ,0L , SEEK_SET); //位置偏移 for (i = 0; i < SIZE; i++) { //返回的是count = 1 if (int a = fread(&buf[i], sizeof(student), 1, fp) != 1) { printf("文件读取失败!%d\n",a); return false; } } fclose(fp); printf("%s\n", "读取结束"); for (i = 0; i < SIZE; i++) { printf("name:%s num:%s age:%c\n", buf[i].name, buf[i].num, buf[i].age); } return true; }
//定义文件缓冲区buf[SIZE] student stu[SIZE], buf[SIZE];; //声明函数 bool fwrite(); bool fread(); void freadS(int size); int main() { int i; printf("%d\n", sizeof(student)); //freadS(sizeof(student)); if (!fread()) { memset(buf , 0 , sizeof(buf)); } for (i = 0; i < SIZE; i++) { printf("Please intput ” str str str“\n:"); scanf("%s %s %s", &stu[i].name, &stu[i].num, &stu[i].age); } if (fwrite()) { fread(); } return 0; }
运行结果:
查看文件中数据存储的格式:
//可以另一种形式看出文件存储的方式。 void freadS(int size) { FILE *fp = NULL; int len = 0, tmp = 1; char buffer[1000]; /*memset(buffer, 1, 100); */ if ((fp = fopen("text.txt", "r")) == 0) { printf("open failed!"); return; } fseek(fp, 0L, SEEK_END); len = ftell(fp);//是四个结构体的字节数 fseek(fp, 0L, SEEK_SET); if(fread(buffer, 1, len, fp) != len) { return ; } for (int i = 0; i < len; i++) { printf("%c", buffer[i]); if (size == tmp) { printf("\n"); tmp = 1; } else { tmp++; } } fclose(fp); printf("\n"); getchar(); }
freads()运行结果是:
能够很清晰看出存储方式。
函数fseek()
用 法:int fseek(FILE *stream, long offset, int fromwhere)
两个参数的意义分别为:
1、long offset 为偏移量,正数表示正向偏移(向尾部偏移),负数表示负向偏移(向首部偏移);
2、int fromwhere 为偏移的起始点,对于fromwhere ,函数fseek定义了三个位置,对应如下:
SEEK_SET(对应 0):文件开头;
SEEK_CUR(对应 1):文件指针所指当前位置;
SEEK_END(对应2):文件结尾;
3、返回值: 如果函数执行成功,FILE *stream将指向以fromwhere 为起始点,偏移offset个字节的位置,返回0。若函数执行失败(比如offset超过了文件自身的大小),则不改变stream指向的位置,返回非0值。
若文件偏移超出了文件末尾位置,还是返回0,;若往回偏移超出了文件首部,返回-1,。