1、概述
文件有磁盘文件和设备文件;前者指一组数据的有序集合,通过存储在外部介质(磁盘)上,使用时才调用内存;
后者指在操作系统中把每一个与主机相连的输出、输入设备看做是一个文件,把它们的输入、输出等同于对磁盘文件的读写。
磁盘文件分类
计算机的存储在物理上是二进制的,所以物理上所有的磁盘文件本质都是一样的,以字节为单位进行顺序存储;
从用户或者操作系统使用的角度(逻辑上)把文件分为:
文本文件:基于字符编码的文件;二进制文件:基于值编码的文件
文本文件和二进制文件
1)文本文件:
基于字符编码,常用编码:ASCII、UNICODE;一般使用文本编辑器直接打开;
数5678以ASCII存储形式为:00110101 00110110 00110111 00111000
2)二进制文件
基于值编码,根据具体应用,指定某个值是什么意思;把内存中的数值按其在内存中的存储形式原样输出到磁盘上,
数5678以二进制形式存储:00010110 00101110
2、文件的打开与关闭
文件指针
指C语言中,用一个指针变量指向一个文件。
typedef struct
{
short level; //缓冲区满或者空的程度
unsigned flags; //文件状态标志
char fd; //文件描述符
unsigned char hold; //如无缓冲区不读字符
short bsize; //缓冲区的大小
unsigned char *buffer; //数据缓冲区的位置
unsigned ar; //指针,当前的指向
unsigned istemp; //临时文件,指示器
short token; //用于有效性的检查
}FILE;
FILE是系统typedef定义出来有关信息的一种结构体类型,结构中含有文件名、文件状态和文件当前位置等信息。
声明FILE结构体类型信息包含在头文件"stdio.h"中,一般设置一个指向FILE类型变量的指针变量,然后通过它来应用这些FILE类型变量,通过文件指针就可对它所指的文件进行各种操作;
C语言中,有三个特殊文件指针有系统默认打开,用户无需定义即可直接使用:
stdin:标准输入,默认以当前终端(键盘),使用scanf、getchar函数;
stdout:标准输出,默认以当前终端(屏幕),使用printf、puts函数;
stderr:标准出错,默认以当前终端(屏幕),使用perror函数
例子
#include<stdio.h>
int main()
{
printf("aaaaaaaaaaaa\n");
//fclose(stdout);//关闭标准输出文件指针//stdout和屏幕关联,关闭后屏幕无法打印
printf("bbbbbbbbbbbbb\n");
//打印库函数调用失败原因
perror("mike");//函数括号内只能写字符串
fclose(stderr);//关闭关联打印库函数调用失败原因
perror("jiang");
int a;
printf("请输入a:");
// fclose(stdin);//关闭和键盘关联
scanf("%d",&a);
printf("a = %d\n",a);
return 0;
}
文件打开
#include <stdio.h>
FILE * fopen(const char * filename, const char * mode);
功能:打开文件
参数:
filename:需要打开的文件名,根据需要加上路径
mode:打开文件的模式设置
返回值:
成功:文件指针
失败:NULL
第一个参数的几种形式:
FILE *fp_passwd = NULL;
//相对路径,打开当前目录passwd 文件:源文件所在的目录
FILE *fp_passed = fopen("passwd","r");
//打开当前目录(test)下passwd.txt文件
fp_passwd = fopen("./test/passwd.txt", "r");
//打开当前目录上一级目录(相对当前目录)passwd.txt文件
fp_passwd = fopen("../passwd.txt","r");
//绝对路径:打开C盘test目录下一个passwd.txt文件
fp_passwd = fopen("c:/test/passwd.txt", "r");
第二个参数的几种形式(打开文件的方式):
打开模式 | 含义 |
r或rb | 以只读方式打开一个文本文件(不创建文件,若文件不存在则报错) |
w或wb | 以写方式打开文件(如果文件存在则清空文件,文件不存在则创建一个文件) |
a或ab | 以追加方式打开文件,在末尾添加内容,若文件不存在则创建文件 |
r+或rb+ | 以可读、可写的方式打开文件(不创建新文件) |
r+或rb+ | 以可读、可写的方式打开文件(不创建新文件) |
w+或wb+ | 以可读、可写的方式打开文件(如果文件存在则清空文件,文件不存在则创建一个文件) |
a+或ab+ | 以添加方式打开文件,打开文件并在末尾更改文件,若文件不存在则创建文件 |
Notes:
b是二进制模式,b只是在Windows有效,在Linux用r和rb的结果一样
Unix和Linux下所有文件的文本文件行都是\n结尾,而Windows所有文本文件行都是\r\n结尾
在Windows下,以文本方式打开文件,不加b:
当读取文件时,系统会将所有的“\r\n”转换为“\n”;
当写入文件时,系统会将所有的\n转换为\r\n;
以二进制方式打开文件,则读写都不会进行这样的转换
在Unix/Linux平台下,文本和二进制模式没有区别,“\r\n”作为两个字符原样输入输出
#include<stdio.h>
int main(void){
FILE * fp=NULL;
//"\\"这样路劲形式,只能在Windows下使用
//'/'这样路劲形式,Windows和Linux下都可用,建议使用这样
//路劲可以相对路径,也可绝对路径
fp = fopen("./test","w");
if(fp==NULL) //返回空,说明打开失败
{
//perror:标准出错打印函数,能打印调用库函数出错原因
perror("open");
return -1;
}
return 0;
}
文件关闭
任何文件在使用之后应该关闭:
打开文件会占用内存资源,总是打开不关闭,会消耗很多内存;
一个进程同时打开的文件数是有限制的,超过最大同时打开文件数,再次调用fopen打开文件会失败
没有明确的调用fclose关闭打开文件,程序在退出时候,操作系统会统一关闭;
#include <stdio.h>
int fclose(FILE * stream);
/*
功能:关闭先前fopen()打开的文件。此动作让缓冲区的数据写入文件中,并释放系统所提供的文件资源。
参数:
stream:文件指针
返回值:
成功:0
失败:-1*/
//演示
FILE * fp = NULL;
fp=fopen("ass.txt", "r");
fclose(fp);
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
//#include<fcnt1.h>//err:LINUX?
int main()
{
printf("aaaaaaaaaaaa\n");
fclose(stdout);//关闭标准输出文件指针//stdout和屏幕关联,关闭后屏幕无法打印
// close(1);
//stdout = fopen("F:\DEV_Cdbug\01.txt","w");//err:赋值数据类型不一致?
//int fd = fopen("01.txt",O_WRONLY,0777);
//int fd = fopen("01.txt","w");
FILE* fd = fopen("01.txt","w");//ok
//dup(1); //lINUx
printf("fd = %d\n",fd);
printf("bbbbbbbbbbbbb\n");
printf("ccccccccccccc\n");
printf("dddddddddddddd\n");
printf("dddddddddddddd\n");
printf("dddddddddddddd\n");
fclose(fd);
fd = NULL;
return 0;
}
/*printf:aaaaaaaaaaaa
01.txt
fd = -1924138448
bbbbbbbbbbbbb
ccccccccccccc
dddddddddddddd
dddddddddddddd
dddddddddddddd
*/
文件的顺序读写
按照字符读写文件fgetc、fputc
注:字符串数组使用:char *p[]={"1hello\n","2hello\n","3hello\n"};
1)写文件
#include <stdio.h>
int fputc(int ch, FILE * stream);
功能:将ch转换为unsigned char后写入stream指定的文件中
参数:
ch:需要写入文件的字符
stream:文件指针
返回值:
成功:成功写入文件的字符
2失败:返回-1
例子:fputc(),写字符ch到给定输出流stream,在内部,在写入前将字符转换为 unsigned char
#include<stdio.h>
#include<string.h>
int main()
{
//定义文件指针fp
FILE* fp =NULL;
fp = fopen("01.txt","w");
char buf[]="a test for fputc";
int i=0;
int len = strlen(buf);
for(i=0;i<len;i++){
//向文件fp写入字符buf[i]
int ch = fputc(buf[i],fp);
printf("ch=%c\n",ch);
}
return 0;
}
2)文件结尾
在C语言中,EOF表示文件结束符(end of file)。在while循环中以EOF作为文件结束标志,前提必须是文本文件;
文本文件中,数据都是以字符的ASCII代码值形式存放,ASCII代码值范围:0-127,不可能出现-1,故用EOF作为文件结束标志;
#define EOF -1
当数据以二进制形式放到文件中时,就有-1值的出现,不能采用EOF作为二进制文件的结束标志,ANSI C 提供一个feof函数,用来判断文件是否结束。feof函数既可用来判断二进制文件,也可判断文本文件。
#include <stdio.h>
int feof(FILE * stream);
功能:检测是否读取到了文件结尾。判断的是最后一次“读操作的内容”,不是当前位置内容(上一个内容)。
参数:
stream:文件指针
返回值:
非0值:已经到文件结尾
0:没有到文件结尾//???
例子
#include<stdio.h>
#include<string.h>
//1、如果时文本文件,可以通过-1(EOF)判断文件是否结尾
//2\如果是二进制文件,不能以-1判断文件结尾
//3、feof()判断文件是否结尾,任何文件都能判断
void write_file()
{
//1、打开文件
FILE *fp = NULL;
//一些方式打开文件,果文件不存在,自动新建,
fp = fopen("03.txt","w");
if(fp == NULL)
{
perror("write_file fopen");
return ;
}
//2、写文件fputc(),一次只能写一个字符
fputc('a',fp);
fputc('b',fp);
fputc('c',fp);
fputc(-1,fp);
fputc('d',fp);
fputc(-1,fp);
fputc('e',fp);//
//3、关闭文件
fclose(fp);
fp = NULL;
}
void read_file()
{
//1、打开文件
FILE *fp = fopen("03.txt","r");
if(fp == NULL)
{
perror("read_file fopen");
return ;
}
//2、读文件fgetc(),每次只读一个字符
char ch;
while(1)
{
ch = fgetc(fp);
//if(ch == -1)//文本文件以”-1“结尾,”-1“为隐藏。
// if(ch == EOF)//EOF为内部定义的宏,值为-1;
if(feof(fp)) //如果文件结尾,返回真
{
break;
}
//printf("ch = %c\n",ch);
printf("ch = %d\n",ch);
}
//3、关闭文件
fclose(fp);
fp = NULL;
}
int main()
{
//1、写文件
write_file();
//2、读文件
read_file();
return 0;
}
/*
ch = a
ch = b
ch = c
ch =
ch = d
ch =
ch = e
--------------------------------
*/
3)读文件
#include <stdio.h>
int fgetc(FILE * stream);
功能:从stream指定的文件中读取一个字符
参数:
stream:文件指针
返回值:
成功:返回读取到的字符
失败:-1
//两种结尾方式:文本文件:EOF;二进制文件and文本文件:feof
char ch;
#if 0
while ((ch = fgetc(fp)) != EOF)
{
printf("%c", ch);
}
printf("\n");
#endif
while (!feof(fp)) //文件没有结束,则执行循环
{
ch = fgetc(fp);
printf("%c", ch);
}
printf("\n");
按照行读写文件fgets、fputs
1)写文件
#include <stdio.h>
int fputs(const char * str, FILE * stream);
功能:将str所指定的字符串写入到stream指定的文件中,字符串结束符 '\0' 不写入文件。
参数:
str:字符串
stream:文件指针
返回值:
成功:0
失败:-1
#include<stdio.h>
#include<string.h>
int main()
{
//
FILE* fp =NULL;
fp = fopen("01.txt","w");
char *buf[] = { "123456\n", "bbbbbbbbbb\n", "ccccccccccc\n" };//使用一个字符数组指针
int i = 0;
int n = 3;
for (i = 0; i < n; i++)
{
int flag = fputs(buf[i], fp);
printf("flag = %d\n", flag ); //写入标志位,成功为0
}
return 0;
}
/*
flag = 0
flag = 0
flag = 0
*/
2)读文件
#include <stdio.h>
char * fgets(char * str, int size, FILE * stream);
功能:从stream指定的文件内读入字符,保存到str所指定的内存空间,直到出现换行字符、读到文件结尾或是已读了size - 1个字符为止,最后会自动加上字符 '\0' 作为字符串结束。
参数:
str:字符串
size:指定最大读取字符串的长度(size - 1)
stream:文件指针
返回值:
成功:成功读取的字符串
读到文件尾或出错: NULL
#include<stdio.h>
#include<string.h>
int main()
{
//
FILE* fp =NULL; //创建文件指针
fp = fopen("01.txt","r");// 打开文件,成功返回文件指针
char buf[100] = {0}; //定义并初始化字符数组
while (!feof(fp)) //文件结束标志符:!0;没有结束返回0值
{
memset(buf, 0, sizeof(buf));///将申请到空间清0
char *p = fgets(buf, sizeof(buf), fp);//从fp指定文件内容读取siezof(buf)长度的字符,保存到buf所指向的内存空间
if (p != NULL) //读到fp文件结果:p=NULL
{
printf("buf = %s", buf);
}
}
return 0;
}
/*
buf = asdfsdf
buf = asdfasd
buf = asdfasdf
buf = asdfadfgh
*/
3)训练:文件版四则运算
有个文件大小不确定,每行内容都是一个四则运算表达式,还没有算出结果,写一个程序,自动算出其结果后修改文件。
#include<stdio.h>
#include<string.h>
void write_file()
{
//1、打开文件
FILE *fp = fopen("03.txt","w");
//2、写文件
fputs("10+10=\n",fp);
fputs("10-10=\n",fp);
fputs("10*10=\n",fp);
fputs("10+5=\n",fp);
fputs("10-6=\n",fp);
fputs("10/2=",fp);//不加\n,最后为文件结束符可能提前退出循环导致最后一行没有读取
//3、关闭文件
fclose(fp);
}
int calc(int a,int b,char ch)
{
switch(ch)
{
case '+':
return a+b;
case '-':
return a-b;
case '*':
return a*b;
case '/':
return a/b;
default :
return 0;
}
return 0;
}
void read_file()
{
//1、打开文件
FILE *fp = fopen("03.txt","r");
//2、读文件
char buf[1024];
char tmp[1024*4]={0};
while(1)
{
memset(buf,0,sizeof(buf));
//遇到\n,文件结束符,出错,结束本次读取
fgets(buf,sizeof(buf),fp);
if(strlen(buf)>0)//对读取的内容判断是否为空,不为空打印,解决上面漏掉最后一行
{
//printf("buf=%s",buf);
int a,b;
char ch;
sscanf(buf,"%d%c%d=\n",&a,&ch,&b);//从buf里的字符串读取格式化输入
sprintf(buf,"%d%c%d=%d\n",a,ch,b,calc(a,b,ch)); //格式化输出
strcat(tmp,buf);//字符追加函数:把buf所指向的字符串追加到tmp所指向的字符串结尾
}
//如果文件结束,跳出循环
if(feof(fp))
{
break;
}
}
//printf("%s",tmp);
//3、关闭文件
fclose(fp);
//关闭文件后,以“w”重新打开,目的是清空文件,重新写内容
fp = fopen("03.txt","w");
fputs(tmp,fp); //按行输出文件指针fp所指向文件内容
}
int main()
{
//1、打开文件
//2、读文件
write_file();
read_file();
return 0;
}
/*
10+10=20
10-10=0
10*10=100
10+5=15
10-6=4
10/2=5
*/
按照格式化文件fprintf、fscanf
1)写文件
#include <stdio.h>
int fprintf(FILE * stream, const char * format, ...);
功能:根据参数format字符串来转换并格式化数据,然后将结果输出到stream指定的文件中,指定出现字符串结束符 '\0' 为止。
参数:
stream:已经打开的文件
format:字符串格式,用法和printf()一样
返回值:
成功:实际写入文件的字符个数
失败:-1
fprintf(fp,"%d %d %d\n", 1,2,3);
2)读文件
#include <stdio.h>
int fscanf(FILE * stream, const char * format, ...);
功能:从stream指定的文件读取字符串,并根据参数format字符串来转换并格式化数据。
参数:
stream:已经打开的文件
format:字符串格式,用法和scanf()一样
返回值:
成功:参数数目,成功转换的值的个数
失败: - 1
int a = 0;
int b = 0;
int c = 0;
fscanf(fp, "%d %d %d\n", &a, &b, &c);
printf("a = %d, b = %d, c = %d\n", a, b, c);
3)训练:文件版排序
#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>
//1、sprintf使用方法,
//2、利用随机种子产生随机数,
//3、定义数组(堆区空间分配内存),尝试使用动态数组,在栈区分配内存
//1、sscanf使用方法,
//2、feof判断文件结尾,
//3、文件中每行数字后的\n,通过 sscanf函数读取,
#define M 500
void readfile()
{
FILE *fp = fopen("05.txt","w");
//产生500随机数,/将随机数转换成字符写入文件,每行写一个
srand((unsigned int)time(NULL));//随机种子
char str[M];
int num;
//str = (int *)malloc(M*1);
int i =0;
for(i=0;i<M;i++)
{
//str1[i]=rand() % 100 +1;
num=rand() % 100 ;
//格式化一个字符串
sprintf(str,"%d\n",num);
//往文件写内容
fputs(str,fp);
// fputs(str,stdout);
}
//关闭文件
fclose(fp);
}
void writefile()
{
//读出文件
FILE *fp = fopen("05.txt","r");
if(fp==NULL)//打开失败
{
perror("writefile fopen");
return;
}
int num,i;
int a[1024];
char buf[M];
for(i=0;i<M;i++)
// while(1)
{
//读取一行内容,放在buf中,包含\n也读取进去
fgets(buf,sizeof(buf),fp);
if(feof(fp))//如果到饿了文件结尾,跳出循环
{
break;
}
sscanf(buf,"%d\n",&a[i]);
//a[i]=num;
// i++;
}
int n =i;//数组元素个数
int j=0;
printf("n = %d\n",n);
//数组排序
int tmp;
for(i=0;i<n-1;i++)
{
for(j=0;j<n-1-i;j++)
{
if(a[j]>a[j+1])
{
tmp=a[j];
a[j]=a[j+1];
a[j+1]=tmp;
}
}
}
char dst[4*1024]={0};
char src[512];
//for(int i=0;i<n;i++)//bug 可能i有重复定义
for(i=0;i<n;i++)
{
//printf("%d ",a[i]);
sprintf(src,"%d\n",a[i]);
//printf("src = %s",src);
strcat(dst,src);
}
printf("\n");
//关闭文件
fclose(fp);
//关闭文件,重新写文件
fp = fopen("05.txt","w");
fputs(dst,fp);
fclose(fp);
}
int main()
{
//打开文件
readfile();
//读出文件
writefile();
return 0;
}
按照块读写文件fread、fwrite
1)写文件
#include <stdio.h>
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:以数据块的方式给文件写入内容
参数:
ptr:准备写入文件数据的地址
size: size_t 为 unsigned int类型,此参数指定写入文件内容的块数据大小
nmemb:写入文件的块数,写入文件数据总大小为:size * nmemb
stream:已经打开的文件指针
返回值:
成功:实际成功写入文件数据的块数目,此值和nmemb相等
失败:0
#include<stdio.h>
typedef struct Stu
{
char name[50];
int id;
}Stu;
int main(){
FILE *fp=NULL;
fp = fopen("03.txt", "w");
Stu s[3];
int i = 0;
for (i = 0; i < 3; i++)
{
sprintf(s[i].name, "stu%d%d%d", i, i, i);//格式化输出
s[i].id = i + 1;
}
int ret = fwrite(s, sizeof(Stu), 3, fp);//s写入数据首地址,数据占内存字节数、写入文件个数、文件指针
printf("ret = %d\n", ret);
}
2)读文件
#include <stdio.h>
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:以数据块的方式从文件中读取内容
参数:
ptr:存放读取出来数据的内存空间
size: size_t 为 unsigned int类型,此参数指定读取文件内容的块数据大小
nmemb:读取文件的块数,读取文件数据总大小为:size * nmemb
stream:已经打开的文件指针
返回值:
成功:实际成功读取到内容的块数,如果此值比nmemb小,但大于0,说明读到文件的结尾。
失败:0
#include<stdio.h>
typedef struct Stu
{
char name[50];
int id;
}Stu;
int main(){
FILE *fp=NULL;
fp = fopen("03.txt", "r");
Stu s[3];
int i = 0;
int ret = fread(s, sizeof(Stu), 3, fp);//
printf("ret = %d\n", ret);
for (i = 0; i < 3; i++)
{
printf("s = %s, %d\n", s[i].name, s[i].id);
}
}
3)训练:大文件拷贝