赞
踩
一、基本概念:
1、文件流:
C 语言把文件看作是一个字符的序列,即文件是由一个一个字符组成的字符流,因此 c 语言将文件也称之为文件流。
即当读写一个文件时,可以不必关心文件的格式或结构。
2、文本文件和二进制文件:
计算机文件的存储,物理上都是二进制,所以文本文件与二进制文件的区别并不是物理上的, 而是逻辑上的。这两者只是在编码层次上有差异。简单来说,文本文件是基于字符编码的文件,常见的编码有 ASCII 编码,二进制文件是基于值编码的文件。
3、文件缓冲:
文件缓冲区(buffer) 存在的好处:首先,缓冲区在内存中,从内存中读取数据比从文件(硬盘)中读取数据要快得多。
其次,对文件的读写需要用到 fopen、fread、fwrite 等系统底层函数,而用户进程每调用一次系统函数都要从用户态切换到内核态,等执行完毕后再返回用户态,这种切换要花费一定时间成本(对于高并发程序而言,这种状态的切换会影响到程序性能)。
刷新缓存区:可以使用 fflush()。
二、文件操作
1、文件的打开和关闭:
1)打开fopen() 声明 FILE * fopen ( const char * filename, const char * mode );
mode详解:
2)关闭fclose() 声明 int fclose ( FILE * stream );
成功返回0,失败返回EOF(-1)。
2、文件的读取和写入(文本操作)
1)一次读写一个字符
写入 fputc() 声明 int fputc (int ch, FILE * stream ); 写入成功返回写入的字符,失败返回EOF。
读取 fgetc() 声明 int fgetc ( FILE * stream ); 正常,返回读取的字符;读到文件尾或出错时,为 EOF。
读取字符,重点是判断结束条件是什么?通常的做法是依据返回值判断。
2)一次读写一行字符
什么是行:
行是文本编辑器中的概念,文件流中就是一个字符,不同的平台有差异。
换行符在window 平台是'\r\n',在linux 平台是'\n'。
平台差异:
①linux 读 windows 中的换行,会多读一个字符,windows 读 linux 中的换行,则没有问题。
解决:dos2unix 命令
②Linux 中无论使用 gedit 还是 vim ,系统都会自动在末行添加\n 标志。Windows 当中系统不会自动添加\n。
写入 fputcs() 声明 int fputs(char *str,FILE *fp); 正常,返 0;出错返 EOF。
读取 fgetcs() 声明 char *fgets(char *str,int length,FILE *fp);
正常,返 str 指针;出错或遇到文件结尾返空指针 NULL。
3、详解 fgetcs() :
从 fp 所指向的文件中,至多读 length-1 个字符,送入字符数组 str中, 如果在读入 length-1 个字符结束前遇\n 或 EOF,读入即结束,字符串读入后在最后加一个‘\0’字符。
fgets 函数返回有三个条件:
1)读 length-1 个字符前遇到\n,读取结束(\n 被读取) + \0。
2)读 length-1 个字符前遇到 EOF,读取结束 +\0。
3)读到length-1 个符 +\0。
4、文件的读写(二进制操作)----一次读写一块数据
C 语言所有的文件接口函数,要么以 '\0',表示输入结束,要么以 '\n', EOF(0xFF)表示读取结束,这些都是文本文件的重要标识。而二进制文件,则往往以块的形式,写入或读出。所有的二进制接口对于这些标识,是不敏感的,把它们当做普通字符处理。
写入 fwrite 声明 int fwrite(void *buffer, int num_bytes, int count, FILE *fp);
读取 fread 声明int fread(void *buffer, int num_bytes, int count, FILE *fp);
返回值: 成功,返回读/写的字段数;出错或文件结束,返回 0。
注意 fread返回值陷阱:
- #define _CRT_SECURE_NO_WARNINGS
- #include <stdio.h>
- #include <string.h>
- int main(void)
- {
- FILE *fpw = fopen("bin.txt", "wb+");
- if (fpw == NULL)
- {
- return -1;
- }
- char *p = "123456789";
- fwrite(p, 1, strlen(p), fpw);
- rewind(fpw);
- char buf[1024];
- int n;
- //错误读法 4+4+0
- n =fread((void*)buf,4,1,fpw);
- printf("n = %d\n",n);
- n =fread((void*)buf,4,1,fpw);
- printf("n = %d\n",n);
- n =fread((void*)buf,4,1,fpw); //按块读取,不够4个导致出错
- printf("n = %d\n",n);
-
- //正确读法 4+4+1
- /*n = fread((void*)buf, 1, 4, fpw);
- printf("n = %d\n", n);
- n = fread((void*)buf, 1, 4, fpw);
- printf("n = %d\n", n);
- n = fread((void*)buf, 1, 4, fpw);
- printf("n = %d\n", n);*/
- return 0;
- }

分析:fread 依靠读出块的多少来标识读结果和文件结束标志。因此读取时最好以最小的单元格式进行读,或是以写入的最小单元进行读。
5、文件指针偏移函数:
1、rewind() 声明 void rewind ( FILE * stream ); 将文件指针重新指向一个流的开头。
用途:如果一个文件具有读写属性,则我们写完文件时,文件指针指向结尾,当需要读取时就需要 rewind函数。
2、ftell() 声明 long ftell ( FILE * stream );
成功,返回当前读写位置偏离文件头部的字节数。失败, 返回-1。
3、fseek() 声明 int fseek ( FILE * stream, long offset, int where); 偏移文件指针
成功返回 0 ,失败返回-1。
常见起始位置宏定义:
- #define SEEK_SET 0 //文件开头
- #define SEEK_CUR 1 //当前位置
- #define SEEK_END 2 //文件结尾
-
- /*简单用法*/
- fseek(fp,100L,0); //把 fp 指针移动到离文件开头 100 字节处;
- fseek(fp,100L,1); //把 fp 指针移动到离文件当前位置 100 字节处;
- fseek(fp,-100L,2); //把 fp 指针退回到离文件结尾 100 字节处。
三、总结:
前面简单介绍了文本读写和二进制读写,但归根到底二进制读写才是本质。
1、使用二进制读取功能更加强大,比如读取结构体就非常适合,结构体包含各种类型,类型大小不一,使用二进制就不需要考虑这些问题。
2、文件读写特别注意如何判断是否读取成功,是否写入完成,一般使用返回值判断。
3、读以下文件,请问 fgets 共执行了多少次?
fgets( buf, 10, fp) ;
1234567890abcdefg(换行)
1234567890(换行)
abcdefg(EOF)
分析:fgets 共执行了5次,length为10,所以每次最多读10-1 = 9个字符。
第一次,读到1~9 +\0 第二次,读到0~g \n +\0 第三次,读到1~9 +\0
第四次,读到0 \n +\0 第五次,a~g +\0
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。