【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