【C语言】高效处理文件内容:C语言的文件操作技巧与窍门

目录

一、文件分类

1、按存储介质分类

2、按存储方式分类

二、文本文件

三、二进制码文件 

四、二进制和文件文件的区别 

五、文件缓冲区 

 1、文件缓冲区的刷新方式

2、模拟时钟

3、文件指针

六、文件的API

1、打开文件fopen

2、关闭文件 fclose

3、逐个字符读写 fgetc fputc

4、一次读写一个字符串 fgets fputs

5、一次读写n块文件数据 fread fwrite

 6、格式化读写 fscanf  fprintf​编辑

 1、格式化写  fprintf ( 文件指针,格式字符串,输出表列)

2、格式化读 fscanf ( 文件指针,格式字符串,输出表列)

七、文件的随机读写

1、顺序读写引入随机读写案例

2、随机读写的API

(1)rewind复位文件流指针

 (2)ftell返回文件流指针 距离文件首部的 字节数

(3)fseek文件流指针定位

八、文件加密器


一、文件分类

1、按存储介质分类

磁盘文件:文件的数据 存放在磁盘上(音视频、图片文件、文档文件)

设备文件:通过系统将外部设备抽象文件

2、按存储方式分类

物理上:任何磁盘文件 在物理上都是二进制存储。

逻辑上:磁盘文件分为二进制文件文本文件

二、文本文件

        文本文件基于字符编码,常见编码有 ASCII、UNICODE 等。 一般可以使用文本编辑器直接打开

        例如:

        (1)数 5678 的以 ASCII 存储形式为 ASCII 码:

                 00110101(==53=='5')   00110110   00110111   00111000         共4B

        (2)歌词文件(lrc):文本文件 

三、二进制码文件 

        基于编码,把内存中的数据原样输出到磁盘上 ,一般需要自己判断或使用特定软件分析数据格式

        例如:数 5678 的存储形式为: 二进制码:00010110 00101110

四、二进制和文件文件的区别 

文本文件:

优点:一个字节表示一个意思、便于查看

缺点:空间大、效率低

二进制文件:

优点:效率高、空间小

缺点:不定长、不便于查看

五、文件缓冲区 

 1、文件缓冲区的刷新方式

1、行刷新:遇到换行符刷新。

2、满刷新:缓冲区数据放满 刷新

3、强制刷新使用fflush函数 将缓冲刷新

4、关闭耍新 :程序运行结束的时候 将缓冲区数据 全部刷新

2、模拟时钟

#include<unistd.h>
void test()
{
    int i=0;
    while(1)
    {
        printf(r%02d:%02d",i/60,i%60);//r回到行首
        fflush(stdout);//强制刷新
        sleep(1);
        i++;
}

3、文件指针

        所有操作文件的库函数 都需要借助文件指针操作文件

为每一个进程 默认打开的3个文件指针:

(1)stdin:标准输入默认为当前终端(键盘)
我们使用的scanf、getchar 函数默认从此终端获得数据

(2)stdout:标准输出﹑默认为当前终端(屏幕)
我们使用的printf、puts 函数默认输出信息到此终端
(3)stderr:标准错误输出设备文件默认为当前终端(屏幕)

当我们程序出错使用:perror函数时信息打印在此终端

例:fgets(buf,sizeof(buf),stdin);

        fwrite("hello world",12,1,stdout);//12块1字节输出

六、文件的API

文件的操作步骤:打开 读写 关闭

1、打开文件fopen

        fopen中f描述其为库函数,其底层使用open函数完成系统调用。

定义:FILE *fopen(const char *path, const char *mode);

path:文件的路径

mode:打开文件方式

返回值:

        成功:就是打开的文件指针

        失败:NULL

文件的打开方式mode:r、w、a、+、t、b

r:只读的方式打开

w:只写的方式打开

a:追加的方式打开(清空重写)

+:可读可写方式打开

t:以文本文件方式打开 (默认方式,可省略)

b:以二进制方式打开(必须显示说明)

 组合方式:

模式 功能
r或rb 以只读方式打开一个文件(不创建文件)
w或wb 以写方式打开文件(使文件长度截断为0字节(删除),创建一个文件)
a或ab 以添加方式打开文件,即在末尾添加内容,当文件不存在时,创建文件用于追加写入
r+或rb+ 以可读、可写的方式打开文件(不创建新文件)
w+或wb+ 以可读、可写的方式打开文件
(使文件长度为0字节(删除),创建一个文件)
a+或ab+ 以添加方式打开文件,打开文件并在末尾更改文件(如果文件不存在,则创建文件)

2、关闭文件 fclose

定义:int fclose(FILE *fp);

返回值:

        成功返回     0

        失败返回  非0

void test()
{
	FILE* fp = NULL;
	fp = fopen("test.txt", "r");
	if (fp = NULL)//文件不存在
	{
		perror("fopen");
		return;
	}
	fclose(fp);
}

3、逐个字符读写 fgetc fputc

一次一个字节:fputc

函数的定义:int fputc(int c, FILE *stream)

函数的说明:fputc将c的值写到stream所代表的文件中。

返回值:
        如果输出成功,则返回输出的字节值;

        如果输出失败,则返回一个EOF

EOF是在stdio.h文件中定义的宏符号常量,值为-1,EOF只是在文本文件中有效

 写入hello world字符串:

void test()
{
	FILE* fp = NULL;
	fp = fopen("test.txt", "w");
	if (fp = NULL)
	{
		perror("fopen");
		return;
	}
	char* file_data = "hello word";
	while (*file_data != '')
	{
		fputc(*file_data, fp);
		file_data++;
	}
	fclose(fp);
}

一次一个字节:fgetc

函数定义:int fgetc(FILE *stream);

函数说明:fgetc 从stream所标示的文件中读取一个字节,将字节值返回

返回值:
        以t的方式:读到文件结尾返回EOF
        以b的方式:读到文件结尾,使用feof函数(后面会讲)判断结尾

 逐个读字符串:

void test()
{
	FILE* fp = NULL;
	fp = fopen("test.txt", "r");
	if (fp = NULL)
	{
		perror("fopen");
		return;
	}
	while(1)
	{
		char ch = fgetc(fp);
		if (ch != EOF)
			break;
		printf("%c", ch);
	}
	fclose(fp);
}

4、一次读写一个字符串 fgets fputs

写一个字符串:int fputs(const char *s, FILE *stream)

读一个字符串:char *fgets(char *s, int size, FILE *stream)

读取一个字符串(遇到换行符 结束) ,读取一行文件数据

成功返回目的数组的首地址,即 s

失败返回 NULL

 有test.txt文件下test.txt文件中数据往b.txt中的测试程序:

void test06()
{
    FILE *fp_r = fopen("test.txt", "r");
    if (fp_r == NULL)
    {
         perror("fopen");
         return;
    }
    FILE *fp_w = fopen("b.txt", "w");
    if (fp_w == NULL)
    {
        perror("fopen");
        return;
    }

    while (1)
    {
        char buf[128] = "";//存放读到的数据并写到b.txt中
        //读一行
        char *ret = fgets(buf, sizeof(buf), fp_r);
        if (ret == NULL)
        break;

        //写一行
        fputs(buf, fp_w);
    }

 fclose(fp_r);
 fclose(fp_w);
}

5、一次读写n块文件数据 fread fwrite

块写:size_t fwrite(void *ptr, size_t size, size_t n, FILE *stream);

函数说明:fwrite 函数将ptr 指向的内存里的数据,向stream 所标示的文件中写入数据,一块是size个字节,共n块。

:将内存的数据 原样写入磁盘文件

返回值: 实际写入的块数

快写实例: 

#include <string.h>
typedef struct
{
    int num;
    char name[30];
}STU;
void test()
{
    STU A[3]={{1,"lucy"},{2,"bob"},{3,"张三",{4,"李四"}};
    int n = sizeof(hero) / sizeof(hero[0]);
    FILE *fp = fopen("S.txt", "w");
    if (fp == NULL)
    {
         perror("fopen");
        return;
     }
    fwrite(A, sizeof(A), n, fp);
    fclose(fp);
}

快读:size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

: 磁盘数据 原样 输入内存

返回值: 实际读到的整块数:不足一块 不计数 但数据是读到的。

块读实例 

#include <string.h>
void test02()
{
    STU A[3];
    memset(A, 0, sizeof(A));
    FILE *fp = fopen("S.txt", "r");
    if (fp == NULL)
    {
        perror("fopen");
        return;
    }
    fread(A, sizeof(STU), 3, fp);
    int i = 0;
    for (i = 0; i < 3; i++)
    {
        printf("%d %sn", A[i].num, A[i].name);
    }
    fclose(fp);
}

 6、格式化读写 fscanf  fprintf

 1、格式化写  fprintf ( 文件指针,格式字符串,输出表列)

#include <string.h>
typedef struct
{
    int num;
    char name[30];
}STU;
void test()
{
    STU A[3]={{1,"lucy"},{2,"bob"},{3,"张三",{4,"李四"}};
    int n = sizeof(hero) / sizeof(hero[0]);
    FILE *fp = fopen("S.txt", "w");
    if (fp == NULL)
    {
         perror("fopen");
        return;
     }
    //格式化写
   int i = 0;
    for (i = 0; i < n; i++)
    {
        fprintf(fp, "%s %d n", A[i].num, A[i].name);
    }

    fclose(fp);
}

2、格式化读 fscanf ( 文件指针,格式字符串,输出表列)

#include <string.h>
void test02()
{
    STU A[3];
    memset(A, 0, sizeof(A));
    FILE *fp = fopen("S.txt", "r");
    if (fp == NULL)
    {
        perror("fopen");
        return;
    }
    int i = 0;
    for (i = 0; i < 3; i++)
    {
        fscanf(fp, "%d %s",&A[i].num, A[i].name);
    }
    int i = 0;
    for (i = 0; i < 3; i++)
    {
        printf("%d %sn", A[i].num, A[i].name);
    }
    fclose(fp);
}

七、文件的随机读写

文件默认是流指针顺序读写,读写顺序移动流指针 用户不能更改

                             随机读写:用户可以更改文件流指针的位置

1、顺序读写引入随机读写案例

void test()
{
	FILE* fp = NULL;
	fp = fopen("test.txt", "w+");
	if (fp = NULL)
	{
		perror("fopen");
		return;
	}
	fpus("hello world", fp);//此时流指针指向该字符串末尾
	char buf[100] = "";
    //读时从流指针开始往后顺序读,会读不到字符串hello world
	fgets(buf, sizeof(buf), fp);
	printf("读到的数据:%sn", buf);//无数据显示
	fclose(fp);
}

顺序读写解决该问题:文件关闭重新打开后流指针会复位。但文件会打开两次关闭两次。

void test()
{
	FILE* fp = NULL;
	fp = fopen("test.txt", "w+");
	if (fp = NULL)
	{
		perror("fopen");
		return;
	}
	fpus("hello world", fp);//此时流指针指向该字符串末尾
    //流指针复位
    fclose(fp);
    fp=fopen("test.txt","r");
    //读取数据
	char buf[100] = "";
	fgets(buf, sizeof(buf), fp);
	printf("读到的数据:%sn", buf);
	fclose(fp);
}

2、随机读写的API

(1)rewind复位文件流指针

void rewind(FILE *stream);

void test()
{
	FILE* fp = NULL;
	fp = fopen("test.txt", "w+");
	if (fp = NULL)
	{
		perror("fopen");
		return;
	}
	fpus("hello world", fp);//此时流指针指向该字符串末尾
    //复位文件流指针
    rewind(fp);
    //读取数据
	char buf[100] = "";
	fgets(buf, sizeof(buf), fp);
	printf("读到的数据:%sn", buf);
	fclose(fp);
}

 (2)ftell返回文件流指针 距离文件首部的 字节数

long ftell(FILE *stream);

void test()
{
	FILE* fp = NULL;
	fp = fopen("test.txt", "w+");
	if (fp = NULL)
	{
		perror("fopen");
		return;
	}
	fpus("hello world", fp);//此时流指针指向该字符串末尾
    //复位文件流指针
    long len=ftell(fp);
	printf("文件的大小:%ldn", len);
	fclose(fp);
}

(3)fseek文件流指针定位

int fseek(FILE *stream, long offset, int whence);

 whence 定位流指针的起始位置

                文件开头 SEEK_SET 0

                文件当前位置 SEEK_CUR 1

                文件末尾 SEEK_END 2

offset:流指针的偏移量,如果为正数向右偏移(单位为字节数)为负数就是向左偏移

案例:一次性读取文件数据

主要步骤:

(1)将文件流指针定位到尾部

(2)ftell返回文件流指针的偏移量 (文件总大小len)

(3)根据文件总大小 从堆区申请空间 len+1

(4)使用fread一次性将文件数据读取 

 实现代码:

#include <stdlib.h>
#include <string.h>
void test06()
{
    FILE *fp = fopen("c.txt", "r");
    if (fp == NULL)
    {
        perror("fopen");
        return;
    }

     fseek(fp, 0, 2);
     long len = ftell(fp);
    //复位文件流指针
    rewind(fp);
    printf("文件总大小:len=%ldn", len);

     unsigned char *text = (unsigned char *)calloc(1, len + 1);
     fread(text, len, 1, fp);

     printf("%sn", text);
     fclose(fp);

     //释放堆区空间(不是必须操作)
     if (text != NULL)
     {
         free(text);
        text = NULL;
     }
 }

八、文件加密器

main.c

#include <stdio.h>
#include <stdlib.h>
#include "fun.h"

int main(int argc ,char *argv[])
{
	while(1)
	{	
		


		int cmd=0;
		print_help();
		printf("请输入指令:");
		scanf("%d",&cmd);



		if(cmd==1)
		{
			char dest_file[31]="";
			char src_file[31]="";
			unsigned long file_length=0;
			char  *read=NULL;
			unsigned int password=0;
			//从键盘获取源文件和目的文件名字
			get_file_name(dest_file,src_file);
			
			//从文件中读出内容

			read = read_src_file(&file_length,src_file);
			//获取加密password
			printf("请输入密码:");
			scanf("%u",&password);
			//字符数组加密
			read=file_text_encrypt(read,file_length,password);
			
			//保存文件
			save_file(read,file_length,dest_file);
			

		}
		else if (cmd==2)
		{
			char dest_file[31]="";
			char src_file[31]="";
			unsigned long file_length=0;
			char  *read=NULL;
			unsigned int password=0;
			//从键盘获取源文件和目的文件名字
			get_file_name(dest_file,src_file);
			
			//从文件中读出内容

			read = read_src_file(&file_length,src_file);
			//获取加密password
			printf("请输入密码:");
			scanf("%u",&password);
			//字符数组加密
			read=file_text_decrypt(read,file_length,password);
			
			
			//保存文件
			save_file(read,file_length,dest_file);
			
		}
		else if (cmd==3)
		{
			break;
		}
		else
		{
			printf("输入指令出错!!!n");
		}

	
	}
	
	return 0;
}

 fun.c

#include <stdio.h>
#include <stdlib.h>

void print_help()
{
	printf("********1:加密文件***********n");
	printf("********2:解密文件***********n");
	printf("********3:退出程序***********n");
}


void get_file_name(char * dest_file_name,char * src_file_name)
{
	printf("请输入源文件的名称:");
	scanf("%s",src_file_name);
	printf("请输入目的文件的名称:");
	scanf("%s",dest_file_name);
	return;
}


char *read_src_file(unsigned long  *file_length,char *src_file_name)
{
	char *data=NULL;
	FILE *fp;

	
	fp=fopen(src_file_name,"r");//只读的方式打开文件
	if(fp==NULL)
	{
		perror("fopen");
		return NULL;
	}
	
	//流指针go尾部
	fseek(fp,0,2);
	//流指针的偏移量
	*file_length = ftell(fp);
	//流指针复位
	rewind(fp);

	//申请空间保存文件
	data=(char *)calloc(1,*file_length);
	if(NULL==data)
	{
		perror("calloc");
		return NULL;
	}

	//一次性读
	fread(data,*file_length,1,fp);

	fclose(fp);
	return data;

}


char *file_text_encrypt(char * src_file_text,unsigned long int length,unsigned int password)
{
	char *data=NULL;
	unsigned int i=0;
	for(i=0;i<length;i++)
	{
		src_file_text[i] += password;
	}
	
	return src_file_text;

	
}


void save_file(char* text,unsigned long int length,char * file_name)
{	
	char *data=NULL;
	FILE *fp;
	fp=fopen(file_name,"w");
	if(NULL==fp)
	{
		perror("fp");
		return;
	}
	
	//写
	fwrite(text,length,1,fp);

	fclose(fp);
	if(text !=NULL)
	{
		free(text);
		text =NULL;

	}
	return;
}


char *file_text_decrypt(char * src_file_text,unsigned long int length,unsigned int password)
{
	char *data=NULL;
	unsigned int i=0;
	for(i=0;i<length;i++)
	{
		src_file_text[i] -= password;
	}
	
	return src_file_text;	
}

 fun.h

#ifndef __FUN_H__
#define __FUN_H__

extern void print_help();
extern void get_file_name(char * dest_file_name,char * src_file_name);
extern char *read_src_file(unsigned long  *file_length,char *src_file_name);
extern char *file_text_encrypt(char * src_file_text,unsigned long int length,unsigned int password);
extern void save_file(char* text,unsigned long int length,char * file_name);
extern char *file_text_decrypt(char * src_file_text,unsigned long int length,unsigned int password);

#endif