文章目录
三、总结
前言
最近笔者对文件进行了粗略的学习,以此文检验学习的成果,如有错误,请不吝指出。
一、文件以及与文件有关的基本概念
1.文件
- 文件类型(存储)
文本文件和二进制文件:
文本文件是以字符编码的方式进行保存的。
二进制文件将内存中的数据原封不动的进行保存,适用于非字符为主的数据。其实,所有的 数据都可以算是二进制文件。二进制文件的优点在于存取速度快,占用空间小。
- 文件类型(功能)
数据文件与程序文件:
程序文件包括我们用编译器创造的源文件(xxx.c)等等
文件的内容时程序运行时读取的数据,我们便称其为程序文件
(笔者对于文件类型为功能的文件的理解并不透彻,若在后续学习过程中有了更加深刻的认 识会加以补充)
2.文本文件操作
C语言中主要通过标准I/O函数来对文本文件进行处理。相关的操作包括打开、读写、关闭与设置缓冲区。
相关的存取函数有:fopen(), fclose(), fgetc(), fputc(), fgets(), fputs(), fprintf(), fscanf()等
3.FILE
我们对于FILE的理解并不能单纯以其字面意思理解,FILE的本质本质时一个结构体,该结构中含有文件名、文件状态和文件当前位置等信息。
4.文件流
在后文对文件进行操作时,我们会定义一个文件指针FILE *p 用于打开文件,这个指针便可以成为文件流。
5.数据流
指程序与数据的交互是以流的形式进行的。进行C语言文件的存取时,都会先进行“打开文件”操作,这个操作就是在打开数据流,而“关闭文件”操作就是关闭数据流。
扩展:
缓冲文件系统中,关键的概念是“文件类型指针”,简称**“文件指针”**。
每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是有系统声明的,取名FILE。
例如,VS2013编译环境提供的 stdio.h 头文件中有以下的文件类型申明:
struct _iobuf {
char *_ptr;
int _cnt;
char *_base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char *_tmpfname;
};
typedef struct _iobuf FILE;
FILE* pf;//文件指针变量
补:
除以上内容外,文件还涉及到许多知识如I/0函数与文件缓冲区等等概念,笔者脑子不够用特此粘贴大牛的博客供未来的自己理解
一些其他基本概念:
C语言文件操作(含详细步骤)(尤其可以关注第九点有关文件缓冲区的解释)
对于流的更深刻理解可参考以下博客:
二、文件的一些基本操作
1.fopen函数
声明:
FILE *fopen( const char *filename, const char *mode)
filename表示文件位置
mode表示文件的访问模式
字符串 mode
指定为文件请求的访问类型,如下所示。
mode | Access |
---|---|
"r" | 打开以便读取。 如果文件不存在或找不到,fopen 调用将失败。 |
"w" | 打开用于写入的空文件。 如果给定文件存在,则其内容会被销毁。 |
"a" | 在文件末尾打开以进行写入(追加),在新数据写入到文件之前不移除文件末尾 (EOF) 标记。 如果文件不存在,则创建文件。 |
"r+" | 打开以便读取和写入。 文件必须存在。 |
"w+" | 打开用于读取和写入的空文件。 如果文件存在,则其内容会被销毁。 |
"a+" | 打开以进行读取和追加。 追加操作包括在新数据写入文件之前移除 EOF 标记。 写入完成后,EOF 标记不会还原。 如果文件不存在,则创建文件。 |
返回值:
返回指向打开文件的指针,如果文件不存在则返回空指针。
代码如下(示例):
FILE* fp = fopen("C:\\Users\\x\\Desktop\\test.txt.txt", "r");//fopen返回指向打开文件的指针。 一个 null 指针值指示错误。
if (fp == NULL)
{
printf("打开文件夹失败.\n");
return 0;
}
2.fgetc函数
功能:从流中读取单个字符
声明:
int fgetc( FILE *stream );
stream是指向流的指针,即文件指针
返回值:fgetc
返回作为 int
读取的字符或返回 EOF
以指示错误或文件尾
注意:每读取一个字符,文件指针便会往后偏移一个位置。读到文件结尾时会出现EOF。这种以EOF作为文件结束标志的文件,必须是文本文件。
EOF:是end of file的缩写,通常在文本的最后位置表示资料结束
//注意:fgetc只能读取无符号字符
代码如下(示例):
char ch = fgetc(fp);//fgetc 返回作为 int 读取的字符或返回 EOF 以指示错误或文件尾。
printf("%c\n", ch);
ch = fgetc(fp);
printf("%c\n", ch);
ch = fgetc(fp);
3.fgets函数
功能:将文件中的数据存储到定义的字符串中
声明:char *fgets( char *str, int numChars, FILE *stream );
numchars表示要读取的字符数
返回值:返回 str
。 返回 NULL
指示错误或文件尾条件。
若对于txt文本每次最多读取一行的数据
//读取一行
//fgets fgets这个函数读取完会将文件指针移动到下一个字符
char str[200];
fgets(str, 200, fp);
printf("%s\n", str);
fgets(str, 200, fp);
printf("%s\n", str);
fgets(str, 200, fp);
printf("%s\n", str);*/
//char str[200];
//while (fgets(str, 200, fp))//其中每个函数都会返回 str。 返回 NULL表示错误或已到达文件结尾。
//{
// printf("%s\n", str);
//}
//char str[200];
//char* p = fgets(str, 200, fp);//200表示要读取的最大字符数,一般是字符数组的长度
//printf("%s\n", str);
//printf("%s", p);//输出相同, 因为返回的是str
4.fputc函数
//fputc 函数 写一个字符,清空写
//fputc('a', fp);//将原有文档内容清空,写入新的字符
//其中每个函数都会返回写入的字符。 对于 fputc,返回值 EOF 指示一个错误。
5.fputs函数
功能:写入一个字符串到文档中。也可以描述为将一个字符串写入流中。
声明:int fputs( const char *str, FILE *stream )。
返回值:成功,则返回非负值。 发生错误时,将返回 EOF
。
//写入字符串
//char *str = "sajdawdlwiajdaw";//不会自动写入换行符
//fputs(str, fp); //如果每个函数成功,则返回非负值。 发生错误时,fputs 和 fputws 将返回 EOF。
6.fread函数
功能:从流中读取数据
声明:
size_t fread( void *buffer, size_t size, size_t count, FILE *stream );
buffer表示读取的数据的存储的存储位置
size表示项目的大小(以字节为单位)
count表示要读取的项的数量
返回值:
返回函数读取的完整项数
//fread函数
//返回实际读取的大小
char str[200] = { 0 };//第一种清零方式:加{0}
memset(str, 0, sizeof(char));//记得补上memset的注释
int n = fread(str, 1, 20, fp);
str[n] = '\0';//来一个字符串终止符终止
printf("%s\n", str);//会出现烫烫烫的结果是因为字符串没有清零,清零的操作有两种
printf("%d\n", n);
注意:对字符串进行清零操作有两种方式:
1. 直接对已经定义的字符数组赋值。如 char str[200] = {0}。
2. 使用memset函数对其进行清零
memset函数:
功能:将某一块内存的内容全部设定为指定的值,通常用于为新申请的内存进行初始化工作。
声明:void *memset( void *dest, int c, size_t count );
dest可以表示任意形式的指针,如需要初始化的字符数组
c表示要设置的字符,如果想将字符数组初始化为0,则将其设定为0。
count则表示要设置的字符数,一般用sizeof表示。
7.fwrite函数
功能:将数据写入流。
声明:
size_t fwrite( const void *buffer, size_t size, size_t count, FILE *stream);
buffer
指向要写入的数据的指针。如数组名
size
项大小(以字节为单位)。一般为1
count
要写入的项的最大数量。一般用sizeof
stream
指向 FILE
结构的指针。
返回值:
fwrite
返回函数写入的完整项数
//fwrite函数
int num = 456789;
fwrite(&num, 1, sizeof(num), fp);
补:
还可以定义一个结构体并将结构体中的数据写入文件中。
typedef struct _Person
{
char name[20];
char sex[4];
int age;
}person;
int main()
{
//此处省略打开文件一系列操作
person p1 = { "张三", "男", 20 };
fwrite(&p1, 1, sizeof(p1), fp);//文件指针写完偏移到下一个而不是下一行
return 0;
}
笔者在使用fwrite写入数字时得到了乱码的文本,经查阅博客发现fwrite并不适合写入一切数据。以下是查阅的博客。
C语言文件操作函数fwrite导致写入文件的内容乱码的问题解决方案
8.fprintf与fscanf函数
fprintf
功能:将格式化数据输出到流(文件)。
声明:
int fprintf( FILE *stream, const char *format ,[ argument ]... );
返回值: 返回已写入的字节数。
理解:fprintf()和printf()一样工作.
printf是打印输出到屏幕,fprintf是打印输出到文件。
char s[] = "this is a string";
char c = '\n';
fprintf( fp, "%s%c", s, c );
fscanf
功能:从一个流中执行格式化输入,fscanf遇到空格和换行时结束,注意空格时也结束。
声明:
int fscanf(FILE *stream, char *format,[argument...]);
int fscanf(文件指针,格式字符串,输入列表);
其中的format就是相当于正则表达式中的格式,即用什么样的格式来分隔文件中的信息。
[argument]中输入的应是参数的地址。
long l;
float fp;
char s[81];
char c;
fscanf( stream, "%s", s ); // C4996
fscanf( stream, "%ld", &l ); // C4996
fscanf( stream, "%f", &fp ); // C4996
fscanf( stream, "%c", &c ); // C4996
printf( "%s\n", s );
printf( "%ld\n", l );
printf( "%f\n", fp );
printf( "%c\n", c );
因为fscanf的使用有许多细节,笔者也并未全部明了,以下附上参考的博客供自己以后学习
9.fseek函数
功能:文件指针定位。将文件移动到指定位置。
声明:
int fseek( FILE *stream, long offset, int origin );
offset表示设定的偏移的字节数,也就是偏移量。若其为负值则负向偏移,正值正向偏移。
origin表示设定位置从哪里开始偏移,可能的取值有:
SEEK_SET: 文件开头
SEEK_CUR: 当前位置
SEEK_END: 文件结尾
其中SEEK_SET, SEEK_CUR和SEEK_END和依次为0,1和2.
返回值:如果成功,则 fseek
和 _fseeki64
返回 0。 否则,返回一个非零值。
fseek(fp, 100L, 0);
fseek(fp, 100L, 1);
fseek(fp, -100L, 2);
/*
SEEK_SET: 文件开头
SEEK_CUR: 当前位置
SEEK_END: 文件结尾
其中SEEK_SET, SEEK_CUR和SEEK_END和依次为0,1和2.
*/
10.利用文件的基本操作读取文件的最后一行
//读取文件的最后一行
fseek(fp, -1, 2);
char ch = 0;
int length = 0;
while(fread(&ch,1,1,fp))//每正向读一个就会文件指针就会到读取的位置
{
if(ch=='\n')
break;
fseek(fp, -2, 1);//然后再使指针负向偏移两个字节,直至读到换行符
length++;
}
printf("%d\n", length);//读取最后一行的长度
fseek(fp, -length, 2);
/*
初始化字符串的另一种方法
char *str = (char*)malloc(sizeof(char)*length);
memset(fp, 0, length);
*/
char str[200] = {0};
fread(str, 1, length, fp);
printf("%s", str);
11.ftell函数
功能:获取文件指针的当前位置。
声明:
long ftell( FILE *stream );
返回值:返回当前的文件位置。
12.fclose函数
功能:关闭文件(流)
声明:
int fclose( FILE *stream );
返回值:若成功关闭流则返回0
三、总结
通过对文件的简单学习,学到了几点技能,特此记录自己的学习过程。
1.对于网络上参差不齐的对同一知识的解释,最好的方法便是去查阅官方的资料或者使用书籍查阅资料(例如在vs上使用F11对函数进行解读)。
2.认识到了返回值的重要性,之前一直认为了解返回值对于使用函数并无多大作用,学习文件之后认识到返回值可以帮助自己更好地理解与调用函数。
3.每个函数使用都有其局限性,要挑选最合适的函数使用,并将加强对文件请求的访问类型的理解
(笔者本文用记录自己学习过程与分享学习经验,如有不足请不吝指出)