计算机文件,是存储在某种长期储存设备或临时存储设备中的一段数据流,并且归属于计算机文件系统管理之下。
所谓“长期储存设备”一般指磁盘、光盘、磁带等。而“短期存储设备”一般指计算机内存。需要注意的是,存储于长
期存储设备的文件不一定是长期存储的,有些也可能是程序或系统运行中产生的临时数据,并于程序或系统退出后删除。
文件是什么:.exe .txt .ppt .jpg .mp4 .avi
Linux和Unix操作系统:everything is a file
C语言有两种文件:文本文件(看得懂的文件)和二进制文件(看不懂的文件)
打开和关闭文件:fopen函数和fclose函数(自己了解)
顺序读写:
读单个字符:fgetc函数(函数)和getc函数(宏定义)
写单个字符:fputc函数(函数)和putc函数(宏定义)
例子1
/*单个字符的读写操作*/
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *fp1;
FILE *fp2;
int ch;
if((fp1 = fopen("hello.txt", "r")) == NULL)
{
printf("文件打开失败!\n");
exit(EXIT_FAILURE);
}
if((fp2 = fopen("fishc.txt", "w")) == NULL)
{
printf("文件打开失败!\n");
exit(EXIT_FAILURE);
}
while((ch = fgetc(fp1)) != EOF)
{
fputc(ch, fp2);
}
fclose(fp1);
fclose(fp2);
return 0;
}
写多个字符:fputs函数
读多个字符:fgets函数
例子2
/*多个字符的读写操作*/
#include <stdio.h>
#include <stdlib.h>
#define MAX 1024
int main(void)
{
char buffer[MAX];
FILE *fp;
//char string = "To the new life";
if((fp = fopen("hello.txt", "w")) == NULL)
{
printf("打开文件失败!\n");
exit(EXIT_FAILURE);
}
fputs("Line one: To the new life.\n", fp);
fputs("Line two: My life.\n", fp);
/*会打印两次,因第三行只有EOF而fgets会选择保留先前的字符串并输出*/
fclose(fp);
if((fp = fopen("hello.txt", "w")) == NULL)
{
printf("打开文件失败!\n");
exit(EXIT_FAILURE);
}
while( !feof(fp) )
{
fgets(buffer, MAX, fp);
printf("%s", buffer);
}
return 0;
}
格式化读写文件:fscanf函数和fprintf函数
例子3
/*用time函数读取当前日期并使用fprintf()函数打印出来*/
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
int main(void)
{
FILE *fp;
struct tm *P;
time_t t;
time(&t);
p = localtime(&t);
if((fp = fopen("hello.txt", "w")) == NULL)
{
printf("打开文件失败!\n");
exit(EXIT_FAILURE);
}
fprintf(fp, "%d-%d-%d", 1900 + (*p).tm_year, l + (*p).tm_mon, (*p).tm_mday);
fclose(fp);
int year, month, day;
if((fp = fopen("hello.txt", "r")) == NULL)
{
printf("打开文件失败!\n");
exit(EXIT_FAILURE);
}
fscanf(fp, "%d-%d-%d", &year, &month, &day);
printf("%d-%d-%d\n", year, month, day);
fclose(fp);
return 0;
}
二进制形式读写文件
直接读写文件:fread函数和fwrite函数
例子4
/*用fwrite函数和fread函数将一个结构体写入到函数中并打印出来*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Date
{
int year;
int month;
int day;
};
struct Book
{
char name[30];
char author[30];
char publisher[30];
struct Date date;
};
int main(void)
{
FILE *fp;
struct Book *book_for_write;
struct Book *book_for_read;
book_for_write = (struct Book *)malloc(sizeof(struct Book));
book_for_read = (struct Book *)malloc(sizeof(struct Book));
if(book_for_write == NULL || book_for_read == NULL)
{
printf("分配内存失败!\n");
exit(EXIT_FAILURE);
}
strcpy((*book_for_write).name, "C和指针");
strcpy((*book_for_write).author, "Victor Lee");
strcpy((*book_for_write).publisher, "北京大学出版社");
(*book_for_write).date.year = 2010;
(*book_for_write).date.month = 10;
(*book_for_write).date.day = 1;
if((fp = fopen("hello.txt", "w")) == NULL)
{
printf("文件打开失败!\n");
exit(EXIT_FAILURE);
}
fwrite(book_for_write, sizeof(struct Book), 1, fp); //将结构体写入文件
fclose(fp); //写入完成,关闭文件
if((fp = fopen("hello.txt", "r")) == NULL)
{
printf("文件打开失败!\n");
exit(EXIT_FAILURE);
}
fread(book_for_read, sizeof(struct Book), 1, fp); //用结构体将文件内容读出
printf("书名:%s\n", (*book_for_read).name);
printf("作者名:%s\n", (*book_for_read).author);
printf("出版商:%s\n", (*book_for_read).publisher);
printf("出版日期:%d-%d-%d\n", (*book_for_read).date.year, (*book_for_read).date.month, (*book_for_read).date.day);
fclose(fp);
return 0;
}
随机读写文件
ftell函数:获取位置指示器
rewind函数:将位置指示器移动到文件头的位置 如 rewind (fp);
fseek函数:用于设置文件流的指示器,可以偏移到任意位置;
例子5
/*获取位置指示器的值,知道位置指示器能实现随机读写(允许从文件的任意一个位置进行读写)*/
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *fp;
if((fp = fopen("hello.txt", "w")) == NULL)
{
printf("文件打开失败!\n");
exit(EXIT_FAILURE);
}
printf("%ld\n", ftell(fp));
fputc('V', fp);
printf("%ld\n", ftell(fp));
fputs("ictor\n", fp);
printf("%ld\n", ftell(fp));
fclose(fp);
return 0;
}
例子6
/*要求录入学生的姓名,学号和成绩到指定的文件中,并打印出第二位学生的个人信息*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Student
{
int num;
char name[20];
float score;
};
int main(void)
{
FILE *fp;
struct Student *s, *s1, *s2, *s3; //可以用结构体数组,由用户自己输入
s = (struct Student *)malloc(sizeof(struct Student));
s1 = (struct Student *)malloc(sizeof(struct Student));
s2 = (struct Student *)malloc(sizeof(struct Student));
s3 = (struct Student *)malloc(sizeof(struct Student));
(*s1).num = 1001;
strcpy((*s1).name, "victor");
(*s1).score = 89.0;
(*s2).num = 1002;
strcpy((*s2).name, "ramon");
(*s2).score = 95.0;
(*s3).num = 1003;
strcpy((*s3).name, "kim");
(*s3).score = 90.0;
if((fp = fopen("stu.txt", "w")) == NULL)
{
printf("打开文件失败!\n");
exit(EXIT_FAILURE);
}
fwrite(s1, sizeof(struct Student), 1, fp);
fwrite(s2, sizeof(struct Student), 1, fp);
fwrite(s3, sizeof(struct Student), 1, fp);
fclose(fp);
if((fp = fopen("stu.txt", "r")) == NULL)
{
printf("打开文件失败!\n");
exit(EXIT_FAILURE);
}
fseek(fp, sizeof(struct Student), SEEK_SET);
fread(s, sizeof(struct Student), 1, fp);
printf("学号位%d的学生%s的成绩为%0.2f\n", (*s).num, (*s).name, (*s).score);
fclose(fp);
return 0;
}
使用fseek函数需要考虑可移植性问题:
1.对于以二进制模式打开的文件,fseek函数在某些操作系统可能不支持SEEK_END位置
2.对于以文本模式打开的文件,fseek函数的whence参数只能取SEEK_SET才是有意义的,
并且传递给offset参数要么是0,要么是上一次对同个文件调用ftell函数获得的返回值
标准流和错误处理
当一个程序在被执行时,C语言会自动打开三个面向终端的文件流:标准输入流(stdin) 标准输出流(stdout) 标准错误输出流(stderr)
例子7
/*当文件打开遇到错误时,将其错误信息输出到标准错误输出流*/
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *fp;
if((fp = fopen("notexit.txt", "r")) == NULL)
{
printf("标准输出!\n");
fputs("打开文件失败!\n", stderr);
exit(EXIT_FAILURE);
}
fclose(fp);
return 0;
}
重定向:由于标准输出和标准错误输出通常都是直接打印到屏幕上,为了区分它们,可以使用linux shell的重定向功能
重定向标准输入:<
重定向标准输出:>
重定向标准错误输出:2>
错误处理
错误指示器:ferror
使用clearerr函数可以人为地清除文件末尾指示器和错误指示器的状态
ferror函数只能检测是否出错,但无法获取错误原因。不过,大多数系统函数在出现错误的时候会将错误原因记录在errno(仅显示错误码)中 <errno.h>
perror函数可以直观地打印出错误原因,用法:perror("打开文件失败,原因是");
strerror函数:直接返回错误码对应的错误信息
IO缓冲区:与内存池原理类似
在调用fputs函数时,数据写在缓冲区中,在调用fclose函数时,数据才写入文件(可用阻塞函数验证,如getchar函数)
fflush函数:刷新缓冲区,适用于在关闭文件之前就将程序代码保存在文件中
标准IO缓冲提供三种类型的缓冲模式
按块缓存:全缓存,即在填满缓冲区后才进行实际的设备读写操作;
按行缓存:在接收到换行符之前,数据都是先缓存在缓冲区的;
不缓存:顾名思义,允许你直接读写设备上的数据。
setvbuf函数:用于指定一个数据流的缓存模式
例子8
#include <stdio.h>
#include <string.h>
int main(void)
{
char buff[1024];
memset(buff, '\0', sizeof(buff));
setvbuf(stdout, buff, _IOFBF, 1024);
fprintf(stdout, "Welcome to Wuhan!\n");
fflush(stdout);
fprintf(stdout, "输入任意字符后才会显示该行字符!\n");
getchar();
return 0;
}