文件(c语言)

文章目录

  • 前言
  • 一、文件以及与文件有关的基本概念
    • 1.文件
    • 2.文本文件操作
    • 3.FILE
    • 4.文件流
    • 5.数据流
  • 二、文件的一些基本操作
    • 1.fopen函数
    • 2.fgetc函数
    • 3.fgets函数
    • 4.fputc函数
    • 5.fputs函数
    • 6.fread函数
    • 7.fwrite函数
    • 8.fprintf函数与fscanf函数
    • 9.fseek函数
    • 10.利用文件的基本操作读取文件的最后一行
    • 11.ftell函数
    • 12.fclose函数

         三、总结


前言

最近笔者对文件进行了粗略的学习,以此文检验学习的成果,如有错误,请不吝指出。

 

一、文件以及与文件有关的基本概念

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的使用有许多细节,笔者也并未全部明了,以下附上参考的博客供自己以后学习

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和依次为012.

返回值:如果成功,则 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.每个函数使用都有其局限性,要挑选最合适的函数使用,并将加强对文件请求的访问类型的理解

(笔者本文用记录自己学习过程与分享学习经验,如有不足请不吝指出)

你可能感兴趣的:(c语言)