例12-1. 系数文件
从2开始依次找出500个素数,将这些素数存入文本文件prime.txt中 Found
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
int prime(int n) /* 见例5-5 */
{
......
}
int main()
{
int n=2, count=0;
FILE *fp; /* 定义文件指针 */
if((fp=fopen("prime.txt", "w"))==NULL){ /* 打开文件 */
printf("File open error!\n");
exit(0);
}
while(count<500){
if(prime(n)!=0){ /* 写入文件 */
count++;
fprintf(fp, "%d ", n);
}
n++;
}
if(fclose(fp)){ /* 关闭文件 */
printf("Can not close file\n");
exit(0);
}
return 0;
}
操作系统中的
文件类型主要包含
1. 程序文件,如源文件、目标程序、可执行程序等
2. 数据文件,用于输入输出,如文本文件、图像文件、声音文件等
文件所具有的特点包含以下三点:
1. 数据可永久保存
2. 数据长度不定
3. 数据按顺序存取
C语言中的文件是由一个个字节数据组成的
具有两种基本形式
即
例如,整数1234以不同形式保存时
以文本文件保存时,保存为
以二进制文件保存时,保存为
CPU和内存的速度远高于外部存储的速度,将数据直接从内存写入外部存储效率很低
向磁盘输出数据时,数据先写入缓冲区,等缓冲区装满后,将缓冲区写入磁盘
从磁盘读入数据时,先一次性从磁盘文件将一批数据读入到缓冲区,再从缓冲区逐个读入数据到变量
用文件指针指示文件缓冲区中具体读写的位置
同时使用多个文件时,每个文件都有缓冲区,用不同的文件指针分别指示
FILE为结构类型,用typedef定义在stdio.h文件中
typedef struct{
short level; /* 缓冲区使用量 */
unsigned flags; /* 文件状态标志 */
char fd; /* 文件描述符 */
short bsize; /* 缓冲区大小 */
unsigned char *buffer; /* 文件缓冲区的首地址 */
unsigned char *curp; /* 指向文件缓冲区的工作指针 */
unsigned char hold; /* 其他信息 */
unsigned istemp;
short token;
}FILE;
typedef用于
(1). 将C语言中已有的类型(包括已定义过的自定义类型)重新命名
(2). 新的名称可以代替已有数据类型
(3). 常用于简化对复杂的数据类型定义的描述
int i, j; ==> INTEGER i, j;
int* pt; ==> POINT pt;
定义变量
用typedef将变量名命名为新类型名
用新类型名定义变量
如
typedef int NUM[10];
NUM a <==> int a[10];
文件类型指针指向文件缓冲区,通过移动指针实现对文件的操作
文件处理有四个步骤:
例12-2. 用户信息加密和校验
为了保障系统安全,通常采取用户帐号和密码登录系统。系统用户信息存放在一个文件中,系统帐号名和密码由若干字母与数字字符构成,因安全需要文件中的密码不能是明文,必须要经过加密处理。请编程实现: 输入5个用户信息(包含帐号名和密码)并写入文件f12-2.dat。要求文件中每个用户信息占一行,帐号名和加密过的密码之间用一个空格分隔。密码加密算法: 对每个字符ASCII码的低四位求反,高四位保持不变(即将其与15进行异或运算)
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
struct sysuser{ /* 定义系统用户账号信息结构 */
char username[20];
char password[8];
};
void encrypt(char *pwd)
{
int i;
/* 与15异或(00001111),实现低四位取反,高四位不变 */
for(i=0; i<strlen(pwd); i++)
pwd[i]=pwd[i]^15;
}
int main()
{
FILE *fp; /* 定义文件指针 */
int i;
struct sysuser su;
/* 打开文件,进行写入操作 */
if((fp=fopen("f12-2.txt", "w"))==NULL){
printf("File open error!\n");
exit(0);
}
/* 将5位用户账号信息写入文件 */
for(i=1; i<=5; i++){
printf("Enter %dth sysuser(name password):", i);
scanf("%s%s", su.username, su.password); /* 输入账号和密码 */
encrypt(su.password); /* 加密处理 */
fprintf(fp, "%s %s\n", su.username, su.password); /* 写入文件 */
}
if(fclose(fp)){ /* 关闭文件 */
printf("Can not close the file\n");
exit(0);
}
return 0;
}
if((fp=fopen("f12-2.txt","w")) == NULL){
printf("File open error!\n");
exit(0);
}
将文件指针与文件实体对应,程序对文件指针进行操作,指针fp代表磁盘文件
执行成功,fopen返回包含文件缓冲区等信息在内的FILE型地址,赋给文件指针fp
如果执行不成功,则返回一个NULL(空值)
如果不成功,执行exit(0)关闭所有打开的文件,终止程序的执行
其中,参数0表示程序正常结束,非0参数通常表示不正常的程序结束
| 文本文件(ASCII) | 二进制文件(Binary) | ||
|---|---|---|---|
| 使用方式 | 含义 | 使用方式 | 含义 |
| "r" | 打开只读文件 | "rb" | 打开只读文件 |
| "w" | 建立只写新文件 | "wb" | 建立只写新文件 |
| "a" | 打开添加写文件 | "ab" | 打开添加写文件 |
| "r+" | 打开读/写文件 | "rb+" | 打开读/写文件 |
| "w+" | 建立读/写新文件 | "wb+" | 建立读/写新文件 |
| "a+" | 打开读/写文件 | "ab+" | 打开读/写文件 |
if 读文件
指定的文件必须存在,否则出错;
if 写文件(指定的文件可以存在,也可以不存在)
if 以"w"方式写
if 该文件已经存在
原文件将被删除并重新建立;
else
按指定的名字新建一个文件;
else if 以"a"方式写
if 该文件已经存在
写入的数据将被添加到指定文件原有数据的后面,不会删去原来的内容;
else
按指定的名字新建一个文件(与"w"相同);
if 文件同时读和写
使用"r+", "w+"或"a+"打开文件
if(fclose(fp)){
printf("Can not close the file!\n");
exit(0);
}
将缓冲区中的数据写入磁盘扇区,确保写文件的正常完成
释放文件缓冲区单元和FILE结构体,使文件指针与具体文件脱钩
函数fclose()的返回值为0,表示正常关闭文件,非0值表示无法正常关闭文件
例12-3. 复制用户文件
将例12-2的用户信息文件f12-2.txt文件备份一份,取名为文件f12-3.txt,说明,运算程序前请将文件f12-2.txt与源程序放在同一目录下
#include<stdio.h>
#include<stdlib.h>
int main()
{
FILE *fp1, *fp2;
char ch;
/* 打开文件, 读出数据 */
if((fp1=fopen("f12-2.txt", "r"))==NULL){
printf("File open error!\n");
exit(0);
}
if((fp2=fopen("f12-3.txt", "w"))==NULL){
printf("File open error!\n");
exit(0);
}
while(!feof(fp1)){ /* 从fp1所指的文件中按字符顺序依次读取 */
ch=fgetc(fp1);
if(ch!=EOF) /* 将字符ch写入fp2所指的文件 */
fputc(ch, fp2);
}
if(fclose(fp1)){ /* 关闭文件 */
printf("Can not close the file!\n");
exit(0);
}
if(fclose(fp2)){ /* 关闭文件 */
printf("Can not close the file!\n");
exit(0);
}
return 0;
}
if((fp1=fopen("f12-2.txt", "r"))==NULL){
printf("File open error!\n");
exit(0);
}
if((fp2=fopen("f12-3.txt", "w"))==NULL){
printf("File open error!\n");
exit(0);
}
C语言中允许同时打开多个文件
不同的文件对应不同的文件指针
但不允许同一个文件在关闭前再次打开
| 函数类型 | 函数名 |
|---|---|
| 字符读写函数 | fgetc()/fputc() |
| 字符串读写函数 | fputs()/fgets() |
| 格式化读写函数 | fscanf()/fprintf() |
| 二进制读写函数 | fread()/fwrite() |
| 检测文件结尾函数 | feof() |
| 检测文件读写出错函数 | ferror() |
| 清除末尾标志和出错标志函数 | clearerr() |
| 文件定位函数 | fseek(), rewind(), ftell() |
while(!eof(fp1)){
ch=fgetc(fp1);
if(ch!=EOF) fputc(ch, fp2);
}
函数fgetc()
从fp所指示的磁盘文件上读入一个字符到ch
函数fputc()
将一个字符ch写到fp所指示的磁盘文件上
返回值为-1(EOF),表示写文件失败; 若返回ch表示写文件成功
函数fputs()
用来向指定的文本文件写入一个字符串
s为要写入的字符串,结束符'\0'不写入文件
函数返回值,若执行成功,返回所写的最后一个字符,否则返回EOF
函数fgets()
从文本文件中读取字符串
s可以是字符数组名或字符指针,n为指定读入的字符个数,fp为文件指针
当函数被调用时,最多读取n-1个字符,并将读入的字符串存入s所指向内存地址开始的n-1个连续的内存单元中
当函数读取的字符达到指定的个数,或接收到换行符,或接收到文件结束标志EOF时,将在读取的字符后面自动添加一个'\0'字符;若有换行符,则将换行符保留(换行符在'\0'之前);若有EOF,则不保留
若函数执行成功,返回读取的字符串
若函数执行失败,则返回空指针,此时,s的内容不确定
例12-4. 用户信息校验
例12-2的f12-2.txt文件保存着系统用户信息,编写一个函数checkUserValid()用于登录系统时校验用户的合法性
在程序运行时输入用户名和密码,然后在文件中查找该用户信息,如果用户名和密码在文件中找到,则表示用户合法,返回1,否则返回0
程序运行时,输入一个用户名和密码,调用checkUserValid()函数,如果返回1,则提示"Valid User!",否则输出"Invalid User!"
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct sysuser{
char username[20];
char password[8];
};
int checkUserValid(struct sysuser *psu);
void encrypt(char *pwd)
{
int i;
/* 与15异或(00001111),实现低四位取反,高四位不变 */
for(i=0; i<strlen(pwd); i++)
pwd[i]=pwd[i]^15;
}
int main()
{
struct sysuser su;
printf("Enter username: ");
scanf("%s", su.username);
printf("Enter password: ");
scanf("%s", su.password);
if(checkUserValid(&su)==1) /* 调用函数进行校验 */
printf("Valid user!\n");
else
printf("Invalide user!\n");
return 0;
}
/* 校验用户信息的合法性,成功返回1,否则返回0 */
int checkUserValid(struct sysuser *psu)
{
FILE *fp;
char usr[30], usr1[30], pwd[10];
int check=0; /* 检查结果变量,初始化为0 */
/* 连接生成待校验字符串 */
strcpy(usr,psu->username); /* 复制psu->username到usr1 */
strcpy(pwd,psu->password); /* 复制psu->password到pwd */
encrypt(pwd); /* 调用例12-2的encrypt对密码进行加密 */
/* 连接usr、空格、pwd和\n构成新字符串usr,用于在文件中逐行检查 */
strcat(usr, " "); strcat(usr,pwd); strcat(usr,"\n");
/* 打开文件"f12-2.txt"读入 */
if((fp=fopen("f12-2.txt","r"))==NULL){
printf("File open error!\n");
exit(0);
}
/* 从文件读入用户信息数据,遍历判断是否存在 */
while(!feof(fp)){
fgets(usr1,30,fp); /* 读入一行用户信息作为一个字符串到usr1 */
if(strcmp(usr,usr1)==0){ /* 比较判断usr与usr1是否相同 */
check=1;
break;
}
}
if(fclose(fp)){
printf("Can not close the file!\n"); exit(0);
} /* 关闭文件 */
return check;
}
fprintf(文件指针, 格式字符串, 输出表);
实现指定格式的输出输出函数
FILE *fp;
int n;
float x;
fp=fopen("a.txt", "r");
fscanf(fp, "%d%f", &n, &x); /* 表示从文件a.txt分别读入整型数到变量a,浮点数到变量x */
fp=fopen("b.txt", "w");
fprintf(fp, "%d%f", n, x); /* 表示将变量n和变量x的值写入文件b.txt */
从二进制文件中读入一个数据块到变量
向二进制文件中写入一个数据块
其中,buffer为指针,表示存放数据的首地址
size表示数据块的字节数,count表示要读写的数据块块数,fp为文件指针
判断fp指针是否已经到文件末尾,若已经到文件结束位置,返回1,否则返回0
定位文件指针,使文件指针指向读写文件的首地址,即打开文件时文件指针所指向的位置
将文件指针fp从from位置移动offset,其中,offset为偏移量,类型为long,from表示起始位置,文件首部、当前位置、文件尾部分别对应0, 1, 2,或者常量SEEK_SET, SEEK_CUR, SEEK_END
如fseek(fp, 20L, 0); /* 将文件位置指针移动到离文件首20字节处 */
fseek(fp, -20L, SEEK_END); /* 将文件位置指针移动到离文件尾部前20字节处 */
获取当前文件指针的位置,即相对于文件开头的位移量(字节数),若函数出错,返回-1L
用来检查文件在用各种输入输出函数进行读写时是否出错,若返回值为0,表示未出错,否则表示有错,其中文件指针必须为已经定义过的
用来清除出错标志和文件结束标志,使其值为0
例12-6. 个人资金账户管理
资金账户信息统一放在随机文件中,该随机文件的数据项包括:记录ID,发生日期,发生事件,发生金额(正表示收,负表示支出)和余额。每发生一笔收支,文件要增加一条记录,并计算一次余额
程序要实现三个功能:
(1). 创建资金账户文件并添加收入或支出记录
(2). 输出所有记录,显示资金账户的明细收支流水信息
(3). 查询最后一条记录,获账户余额
按照C程序对文件访问的特点来分,文件可分为顺序访问文件和随机访问文件,简称为顺序文件和随机文件。前面介绍的所有例子都进行的是顺序访问,通过使用fprintf()或fputs()函数创建的数据记录长度并不是完全一致的,这种记录长度不确定的文件访问称为顺序访问。而随机访问文件要求文件中单个记录的长度固定,可直接访问,这样速度快,并且无需通过其他记录查找特定记录。因此随机文件适合银行系统、航空售票系统、销售点系统和其他需要快速访问特定数据的事务处理系统。
#include "stdio.h"
#include "stdlib.h"
#include "process.h"
long size; /* 用来保存sizeof(struct LogData) */
struct LogData{ /* 记录的结构 */
long logid; /* 记录ID */
char logdate[11]; /* 记录发生日期 */
char lognote[15]; /* 记录事件说明 */
double charge; /* 发生费用:正表示收入,负表示支出 */
double balance; /* 余额 */
};
int inputchoice() /* 选择操作参数 */
{
int mychoice;
printf("\nEnter your choice:\n");
printf("1 - Add a new cash LOG.\n2 - List All Cash LOG.\n");
printf("3 - Query Last Cash LOG.\n0 - End program.\n");
scanf("%d", &mychoice);
return mychoice;
}
long getLogcount(FILE *cfptr) /* 获取文件记录总数 */
{
long begin, end, logcount;
fseek(cfptr, 0L, SEEK_SET);
begin = ftell(cfptr);
fseek(cfptr, 0L, SEEK_END);
end = ftell(cfptr);
logcount = (end-begin)/size;
return logcount;
}
void ListAllLog(FILE *cfptr) /* 列出所有收支流水记录 */
{
struct LogData log;
fseek(cfptr,0L,SEEK_SET); /* 定位指针到文件开始位置 */
fread(&log,size,1,cfptr);
printf("logid logdate lognote charge balance\n");
while(!feof(cfptr)){
printf("%6ld %-11s %-15s %10.2lf %10.2lf\n", log.logid, log.logdate, log.lognote, log.charge, log.balance);
fread(&log, size, 1, cfptr);
}
}
void QueryLastLog(FILE *cfptr) /* 查询显示最后一条记录 */
{
struct LogData log;
long logcount;
logcount = getLogcount(cfptr);
if(logcount > 0){ /* 表示有记录存在 */
fseek(cfptr, size*(logcount-1), SEEK_SET); /* 定位最后记录 */
fread(&log, size, 1, cfptr); /* 读取最后记录 */
printf("The last log is:\n");
printf("logid:%-6ld\nlogdate:%-11s\nlognote:%-15s\n", log.logid, log.logdate, log.lognote);
printf("charge:%-10.2lf\nbalance:%-10.2lf\n", log.charge, log.balance); /* 显示最后记录内容 */
}else{
printf("no logs in file!\n");
}
}
void AddNewLog(FILE *cfptr) /* 添加新记录 */
{
struct LogData log, lastlog;
long logcount;
printf("Input logdate(format:2006-01-01):");
scanf("%s", log.logdate);
printf("Input lognote:");
scanf("%s", log.lognote);
printf("Input Charge:Income+ and expend-:");
scanf("%lf", &log.charge);
logcount = getLogcount(cfptr); /* 获取记录数 */
if(logcount>0){
fseek(cfptr,size*(logcount-1),SEEK_SET);
fread(&lastlog, size, 1, cfptr); /* 读入最后记录 */
log.logid = lastlog.logid + 1; /* 记录号按顺序是上次的号+1 */
log.balance = log.charge + lastlog.balance;
}else{ /* 如果文件是初始,记录数为0 */
log.logid = 1;
log.balance = log.charge;
}
rewind(cfptr);
printf("logid= %ld\n", log.logid);
fwrite(&log, sizeof(struct LogData), 1, cfptr); /* 写入记录 */
}
int main()
{
FILE *fp;
int choice;
if((fp = fopen("cashbox.txt", "ab+")) == NULL){
printf("can not open file cashbox.txt!\n");
exit(0);
}
size = sizeof(struct LogData);
while((choice = inputchoice()) != 0){
switch(choice){
case 1: /*添加新记录*/
AddNewLog(fp);
break;
case 2: /*列出所有收支流水记录*/
ListAllLog(fp);
break;
case 3: /*查询最后的余额*/
QueryLastLog(fp);
break;
default:
printf("Input Error.");break;
}
}
if(fclose(fp) != 0){
printf( "Can not close the file!\n" );
exit(0);
}
return 0;
}