【C语言】高效处理文件内容:C语言的文件操作技巧与窍门
目录
1、格式化写 fprintf ( 文件指针,格式字符串,输出表列)
2、格式化读 fscanf ( 文件指针,格式字符串,输出表列)
一、文件分类
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