【Amazing! C】文件操作

 

目录

一、文件的打开和关闭

1.1 文件指针

 1.2 文件的打开和关闭

1.3 文件的使用方式

二、文件的顺序读写

2.1 (数据)流

2.2 文件的读写顺序

2.2.1 fgetc

2.2.2 fputc

2.2.3 fgets

2.2.4 fputs

2.2.5 fscanf

2.2.6 fprintf

2.2.7 fread 二进制方式读取文件

2.2.8 fwrite 二进制方式写进文件

2.3 函数对比

三、文件的随机读写

3.1 fseek

3.2 ftell

3.3 rewind

四、文本文件和二进制文件

五、文件读取结束的判定


         在处理数据的时候,往往通过程序从文件中读取数据或将数据写入文件。本章将对文件的操作过程做一个汇总。

一、文件的打开和关闭

1.1 文件指针

        使用C打开一个文件fopen,就会有一个文件信息区。例如,打开data.txt,就会有一个文件信息区自动创建,这个文件信息区是一个FILE类型的结构体。

        创建一个FILE*的指针变量pf就可以找到对应的文件信息区,从而找到相应的文件。

FILE* fopen(const char* filename, const char* mode);//(文件名,打开方式)

int fclose(FILE* stream);

 1.2 文件的打开和关闭

      操作步骤如下:

  1. 打开文件fopen,创建文件信息区,同时返回文件信息区的地址;
  2. 读、写文件;
  3. 关闭文件fclose。

        代码展示如下:

#include<stdio.h>

int main()
{	
	
    //1. 打开文件
	FILE* pf = fopen("data.txt", "r");
	//FILE* pf = fopen("..\\data.txt", "r");
	//FILE* pf = fopen("..\\..\\data.txt", "r");//相对路径
	//	“\\”转义字符,表示一个斜杠
	//FILE* pf = fopen("C:\\Users\\24_9_7\\24_9_7\\data.txt", "r");//绝对路径


	//打开文件也可能失败
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	//2. 写文件


	//3. 关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

        与动态内存管理流程类似:动态内存开辟是向内存申请内存资源,而打开文件是申请文件资源,都是对资源的申请和管理。

        值得注意的是:打开文件的路径包括相对路径和绝对路径。

        针对相对路径:.表示当前路径,..表示上一级路径。

1.3 文件的使用方式

文件使用方式含义如果指定文件不存在
"r"(只读)为了输入数据,打开一个已经存在的文本文件出错
"w"(只写)为了输出数据,打开一个文本文件建立一个新的文件
"a"(追加)向文本文件尾添加数据建立一个新的文件
"rb"(只读)为了输入数据,打开一个二进制文件出错
"wb"(只写)为了输出数据,打开一个二进制文件建立一个新的文件

二、文件的顺序读写

2.1 (数据)流

        流是高度抽象的概念!以输出流为例,我们知道输出流包括:文件流、标准输出流等。

        在写数据的时候,有很多数据,可能会流向很多地方去,比如:显示到屏幕上、存到硬盘上、传到网络上、放到U盘上...等外部设备。

        不同的外部设备读写方式肯定是不同的。写C语言代码把数据打印到屏幕上,...,需要掌握不同的数据传递方式。为了简化,在外部设备和数据之间再抽象一个“流”,所以有了定义:(数据)流,流淌的是数据。我们更关注数据与流之间的操作。

        但是,在使用scanf从键盘上读取数据,使用printf向屏幕上打印数据,直接就操作了,没有进行打开键盘、打开屏幕的指令。这是因为,C只要运行起来,默认就打开了三个流:

  • 标准输入流        -        stdin
  • 标准输出流        -        stdout
  • 标准错误流        -        stderr

        既然是,那么其类型也必然是FILE*类型

#include<stdio.h>

int main()
{
	char ch = 0;
	for (ch = 'a'; ch <= 'z'; ch++)
	{
		if (ch % 5 == 0)
			fputc('\n', stdout);
		fputc(ch, stdout);//在屏幕上打印信息,没有打印在文件中
	}
	return 0;
}

2.2 文件的读写顺序

功能函数名适用于
字符输入函数fgetc所有输入流
字符输出函数fputc所有输出流
本文行输入函数fgets所有输入流
文本行输出函数fputs所有输出流
格式化输入函数fscanf所有输入流
格式化输出函数fprintf所有输出流
二进制输入fread文件
二进制输出fwrite文件

2.2.1 fgetc

int fgetc(FILE * stream);

        返回指定流的内部文件位置指示器当前指向的字符。然后,内部文件位置指示器前进到下一个字符。

        如果调用时流位于文件末尾,则函数将返回EOF并设置流的文件结束标记(feof)

        如果发生读取错误,该函数将返回EOF并为流设置错误指示符(ferror)

#include<stdio.h>

int main()
{
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读文件
	//int ch = fgetc(pf);//返回的是读到字符的ASCII码,如果读取错误,返回EOF,同时会设置一个标记,可用ferror检测
	//printf("%c ", ch);

	//ch = fgetc(pf);
	//printf("%c ", ch);

	//ch = fgetc(pf);
	//printf("%c ", ch);

	//ch = fgetc(pf);
	//printf("%c ", ch);
	
	int ch = 0;
	while ((ch = fgetc(pf)) != EOF)
	{
		printf("%c ", ch);
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

2.2.2 fputc

int fputc(int character, FILE * stream);

        将字符写入流并前进位置指示器。

        成功后,将返回写入的字符。

        如果发生写入错误,则返回EOF并设置错误指示符(ferror)

#include<stdio.h>

int main()
{
	FILE* pf = fopen("data.txt", "w");//当打开文件的时候,文件中什么都没有,但是却有一个光标(文件状态指针)指向要输入字符的起始位置(指向文件内容的一个标记),每一次操作,光标会发生一次状态更新
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//写文件
	/*fputc('a', pf);
	fputc('b', pf);
	fputc('c', pf);
	fputc('d', pf);*/

	char ch = 0;
	for (ch = 'a'; ch <= 'z'; ch++)
	{
		fputc(ch, pf);
	}

	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

2.2.3 fgets

char * fgets(char * str, int num, FILE * stream);

        从stream(流)中读取字符并将其作为C字符串存储到str中,直到读取(num-1)个字符,或者到达换行符或文件末尾。

        换行符会使fgets停止读取但它被函数视为有效字符并包含在复制到str的字符串中

        终止字符会自动附加到复制到str的字符之后。

        成功后,该函数返回str
        如果在尝试读取字符时遇到文件结尾,则设置文件结束标记 (feof)。如果在读取任何字符之前发生这种情况,则返回的指针为NULL。(并且str的内容保持不变)。
        如果发生读取错误,则设置错误指示符(ferror)并返回NULL指针(但str指向的内容可能已更改)。

#include<stdio.h>

int main()
{
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读文件
	char arr[100] = { 0 };
	fgets(arr, 100, pf);//hello,想读第二行,再来一次就可以了
	printf("%s", arr);

	fgets(arr, 100, pf);
	printf("%s", arr);

	//关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

2.2.4 fputs

int fputs(const char * str, FILE * stream);

        将str指向的C字符串写入流。

        该函数从指定的地址(str)开始复制,直到到达终止字符 ('\0')。此终止字符不会复制到流中。

        成功后,将返回非负值。
        出错时,该函数返回EOF并设置错误指示符 (ferror)。

#include<stdio.h>

int main()
{
	FILE* pf = fopen("data.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//写文件
	fputs("hello\n", pf);
	char arr[] = "world";
	fputs(arr, pf);


	//关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

2.2.5 fscanf

int fscanf(FILE* stream, const char* format, ...);

int scanf(const char* format, ...);

        从流中读取数据,并根据参数格式将其存储到其他参数指向的位置。

        成功后,该函数返回成功填充的参数列表的项目数。此计数可能与预期的项目数匹配,也可能由于匹配失败、读取错误或文件结束而更少(甚至为零)。
        如果在读取时发生读取错误或到达文件末尾,则会设置正确的指示符(feof或ferror)。而且,如果在成功读取任何数据之前发生任何情况,则返回EOF。

#include<stdio.h>

struct S
{
	float f;
	char c;
	int n;
};

int main()
{
	struct S s = { 0 };
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读文件
	fscanf(pf, "%f %c %d", &(s.f), &(s.c), &(s.n));//读取的时候要与写入的格式保持一致
	printf("%f %c %d", s.f, s.c, s.n);

	//关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

2.2.6 fprintf

int fprintf(FILE* stream, const char* format, ...);

int printf(const char* format, ...);

        将format指向的C字符串写入流。

        成功后,将返回写入的字符总数。
        如果发生写入错误,则设置错误指示符(ferror)并返回负数。

#include<stdio.h>

struct S
{
	float f;
	char c;
	int n;
};

int main()
{
	struct S s = { 3.14,'w',100 };
	FILE* pf = fopen("data.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//写文件
	printf("%f %c %d", s.f, s.c, s.n);
	fprintf(pf, "%f %c %d", s.f, s.c, s.n);


	//关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

2.2.7 fread 二进制方式读取文件

size_t fread(void *ptr, size_t size, size_t count, FILE* stream)
             来源于ptr    每个元素大小     个数        写到流中       

        从流中读取count元素数组,每个元素的大小为size字节,并将它们存储在ptr指定的内存块中。 流的位置指示器按读取的总字节数前进。

#include<stdio.h>

int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	//写文件
	FILE* pf = fopen("data.txt", "wb");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	//二进制的写文件
	fwrite(arr, sizeof(arr[0]), sizeof(arr) / sizeof(arr[0]), pf);

	fclose(pf);
	pf = NULL;
	return 0;
}

        fread要求读取count个大小为size字节的数据;
        如果真的读取到count个数据,函数返回count;
        如果没有读取到count个数据,返回的是真实的读取到的完整的数据个数。

2.2.8 fwrite 二进制方式写进文件

size_t fwrite(const void *ptr, size_t size, size_t count, FILE* stream)

        将count元素数组(每个元素的大小为size字节)从ptr指向的内存块写入中的当前位置。 

#include<stdio.h>

int main()
{
	int arr[10] = { 0 };
	//写文件
	FILE* pf = fopen("data.txt", "rb");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	//二进制的写文件
	fread(arr, sizeof(arr[0]), sizeof(arr) / sizeof(arr[0]), pf);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}

	fclose(pf);
	pf = NULL;
	return 0;
}

2.3 函数对比

scanf / fscanf / sscanf
printf / fprintf / sprintf

        scanf是格式化的输入函数,针对的是标准输入流(键盘)
        printf是格式化的输出函数,针对的是标准输出流(屏幕)
        scanf和printf是针对标准输入/输出流的格式化输入/输出函数

        fscanf是针对所有输入流(文件流、标准输入流)的格式化输入函数
        fprintf是针对所有输出流(文件流、标准输入流)的格式化输出函数

        int sscanf(const char* s, const char* format, ...);//将字符串转换成格式化的数据
        int sprintf(char* str, const char* format, ...);//把格式化的数据转换成字符串

#include<stdio.h>

struct S
{
    float f;
    char ch;
    int n;
};

int main()
{
    struct S s = { 3.14, 'w', 100 };
    char arr[10] = { 0 };
    sprintf(arr, "%f-%c-%d", &(s.f), &(s.ch), &(s.n));
    printf("%s\n", arr);

    struct S tmp = { 0 };
    sscanf(arr, "%f-%c-%d", &(tmp.f), &(tmp.ch), &(tmp.n));//从arr中以"%f-%c-%d"的格式提取数据,存放在tmp中
    printf("%f\n", tmp.f);
    printf("%c\n", tmp.ch);
    printf("%d\n", tmp.n);

    return 0;
}

三、文件的随机读写

3.1 fseek

int fseek(FILE* stream, long int offset, int origin);
              流            偏移量         起始位置
  • SEEK_SET 文件指针的初始位置
  • SEEK_CUR 文件指针当前的位置
  • SEEK_END 文件指针的结束位置
#include<stdio.h>

int main()
{
    //打开文件
    FILE* pf = fopen("data.txt", "r");//假设data文件中存放abcdef
    if(pf == NULL)
    {
        ferror(fopen);
        return 1;
    }

    //写文件
    int ch = fgetc(pf);
    printf("%c ", ch);//a
    ch = fgetc(pf);
    printf("%c ", ch);//b
    fseek(pf, -2, SEEK_CUR);
    //fseek(pf, 0, SEEK_SET); 
    //fseek(pf, -6, SEEK_END); 
    ch = fgetc(pf);
    printf("%c ", ch);//a

    //关闭文件
    fclose(pf);
    pf = NULL;

    return 0;
}

3.2 ftell

        返回文件指针相对于起始位置的偏移量,左移为负,右移为正。

#include<stdio.h>

int main()
{
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	//读文件
	int ch = fgetc(pf);
	printf("%c\n", ch);//a
	ch = fgetc(pf);
	printf("%c\n", ch);//b
	ch = fgetc(pf);
	printf("%c\n", ch);//c
	ch = fgetc(pf);
	printf("%c\n", ch);//d
	int pos = ftell(pf);
	printf("pos = %d\n", pos);
	fseek(pf, -pos, SEEK_CUR);
	ch = fgetc(pf);
	printf("%c\n", ch);//a

	//关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

3.3 rewind

        让文件指针的位置回到起始位置。

#include<stdio.h>

int main()
{
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	//读文件
	int ch = fgetc(pf);
	printf("%c\n", ch);//a
	ch = fgetc(pf);
	printf("%c\n", ch);//b
	ch = fgetc(pf);
	printf("%c\n", ch);//c
	ch = fgetc(pf);
	printf("%c\n", ch);//d
	rewind(pf);
	ch = fgetc(pf);
	printf("%c\n", ch);//a

	//关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

四、文本文件和二进制文件

        以ASCII码字符存储的文件就是文本文件;不加转换的输出到外存,就是二进制文件。所以数据文件被分为文本文件和二进制文件。

        字符一律以ASCII码形式储存;

        数据可以ASCII码形式储存,也可以二进制形式储存。

        如:10000

//如果以ASCII码形式输出到磁盘,则占5个字节
0011 0001 0011 0000 0011 0000 0011 0000 0011 0000
    1         0         0         0         0

//而以二进制形式输出,则占4个字节
0000 0000 0000 0000 0010 0111 0001 0000

        通过代码展示如下:

#include<stdio.h>

int main()
{
    int a = 10000;
    FILE* pf = fopen("data.txt", "wb")
    if(pf == NULL)
    {
        ferror("fopen");
        return 1;
    }

    fwrite(a, 4, 1, pf);//二进制的形式写到文件中

    fclose(pf);
    pf = NULL;

    return 0;
}

        通过记事本打开相应文件为乱码,在VS上用二进制编辑器打开:

五、文件读取结束的判定

  • ferror - 在文件读取结束后,用来判断文件是否因为读取过程中遇到错误而结束,遇到错误返回非0值。
  • feof - 在文件读取结束后,用来判断文件是否因为读取过程中遇到文件结束标志而结束。

        作用:当已知文件读取结束,但读取结束的原因可能是多样的,比如说读取失败、读取过程中发生错误等结束,也可能是正常读取到末位而结束。
        feof当文件读取结束的时候,判断是读取失败结束,还是遇到文件尾结束。

        写一个代码完成文件的拷贝,代码如下:

#include<stdio.h>

int main()
{
	FILE* pfread = fopen("data1.txt", "r");
	if (pfread == NULL)
	{
		perror("fopen");
		return 1;
	}
	FILE* pfwrite = fopen("data2.txt", "w");
	if (pfread == NULL)
	{
		perror("fopen");
		fclose(pfread);
		pfread = NULL;
		return 1;
	}
	//拷贝数据
	int ch = 0;
	while ((ch = fgetc(pfread)) != EOF)//不能用char类型接收,因为ASCII码值不包括-1
	{
		fputc(ch, pfwrite);
	}
	
	//关闭文件
	fclose(pfread);
	fclose(pfwrite);
	pfread = NULL;
	pfwrite = NULL;

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值