标签:
1.C语言文件概述
我们对文件的概念已经非常熟悉了,比如常见的 Word 文档、txt 文件、源文件等。文件是数据源的一种,最主要的作用是保存数据。
在操作系统中,为了统一对各种硬件的操作,简化接口,不同的硬件设备也都被看成一个文件。对这些文件的操作,等同于对磁盘上普通文件的操作。例如,通常把显示器称为标准输出文件,printf
就是向这个文件输出,把键盘称为标准输入文件,scanf 就是从这个文件获取数据。
文件 | 硬件设备 |
---|---|
stdin | 标准输入文件,一般指键盘;scanf()、getchar() 等函数默认从 stdin 获取输入。 |
stdout | 标准输出文件,一般指显示器;printf()、putchar() 等函数默认向 stdout 输出数据。 |
stderr | 标准错误文件,一般指显示器;perror() 等函数默认向 stderr 输出数据(后续会讲到)。 |
stdprn | 标准打印文件,一般指打印机。 |
我们不去探讨硬件设备是如何被映射成文件的,大家只需要记住,在C语言中硬件设备可以看成文件,有些输入输出函数不需要你指明到底读写哪个文件,系统已经为它们设置了默认的文件,当然你也可以更改,例如让 printf 向磁盘上的文件输出数据。操作文件的正确流程为:打开文件 --> 读写文件 --> 关闭文件。文件在进行读写操作之前要先打开,使用完毕要关闭。
2.C语言文件的打开与关闭
在C语言中,文件操作都是由库函数来完成的,这节介绍文件的打开和关闭。
FILE *fopen(char *filename, char *mode);
filename
为文件名(包括文件路径),mode
为打开方式,它们都是字符串。fopen()
会获取文件信息,包括文件名、文件状态、当前读写位置等,并将这些信息保存到一个FILE类型的结构体变量中,然后将该变量的地址返回。
FILE是在stdio.h头文件中定义的一个结构体,用来保存文件信息。如果希望接收 fopen() 的返回值,就需要定义一个 FILE 类型的指针。例如:
FILE *fp = ("demo.txt", "r");表示以“只读”方式打开当前目录下的 demo.txt 文件,并使 fp 指向该文件,这样就可以通过 fp 来操作 demo.txt 了。fp 通常被称为文件指针。又如:
FILE *fp = fopen("D:\\demo.txt","rb");表示以二进制方式打开 D 盘下的 demo.txt 文件,允许读和写。
打开方式 | 说明 |
---|---|
r | 以只读方式打开文件,只允许读取,不允许写入。该文件必须存在。 |
r+ | 以读/写方式打开文件,允许读取和写入。该文件必须存在。 |
rb+ | 以读/写方式打开一个二进制文件,允许读/写数据。 |
rt+ | 以读/写方式打开一个文本文件,允许读和写。 |
w | 以只写方式打开文件,若文件存在则长度清为0,即该文件内容消失,若不存在则创建该文件。 |
w+ | 以读/写方式打开文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。 |
a | 以追加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留(EOF符保留)。 |
a+ | 以追加方式打开可读/写的文件。若文件不存在,则会建立该文件,如果文件存在,则写入的数据会被加到文件尾后,即文件原先的内容会被保留(原来的EOF符 不保留)。 |
wb | 以只写方式打开或新建一个二进制文件,只允许写数据。 |
wb+ | 以读/写方式打开或建立一个二进制文件,允许读和写。 |
wt+ | 以读/写方式打开或建立一个文本文件,允许读写。 |
at+ | 以读/写方式打开一个文本文件,允许读或在文本末追加数据。 |
ab+ | 以读/写方式打开一个二进制文件,允许读或在文件末追加数据。 |
- if( (fp=fopen("D:\\demo.txt","rb") == NULL ){
- printf("Error on open D:\\demo.txt file!");
- getch();
- exit(1);
- }
fclose()
函数把文件关闭,以释放相关资源,避免数据丢失。fclose()
的原型为:
int fclose(FILE *fp);fp 为文件指针。例如:
fclose(fp);文件正常关闭时,fclose() 的返回值为0,如果返回非零值则表示有错误发生。
3.C语言中文本文件与二进制文件的区别
在学习C语言fopen()函数后,知道它的第二个参数是标志字符串。如果字符串中出现‘b‘,则表明是以打开二进制(binary)文件,否则是打开文本文件。
CRLF(0X0D
0X0A)
作为换行符,而C语言本身以LF(0X0A)
作为换行符,所以以文本方式写入数据时,会将LF(0X0A)
替换为CRLF(0X0D
0X0A)
,而读取数据时又会替换回来。
CR(0X0D)表示回车符,也就是 ‘\r‘;CL(0X0A)表示换行符,也就是 ‘\n‘。在Linux和其他一些系统中,文本文件的换行符就是LF(0X0A),与C语言的换行符一样。所以也就沒有了文本方式和二进制方式的区分,使不使用‘b‘标志都是一样的。
CTRLZ(0x1A)
就认为文件已经结束。所以,若使用文本方式打开二进制文件,就很容易出现文件读不完整,或內容不对的错误。即使是用文本方式打开文本文件,也要谨慎使用,比如复制文件,就不应该使用文本方式。
4.C语言以字符形式读写文件
在C语言中,读写文件比较灵活,既可以每次读写一个字符,也可以读写一个字符串,甚至是任意字节的数据(数据块)。本节介绍以字符形式读写文件。
以字符形式读写文件时,每次可以从文件中读取一个字符,或者向文件中写入一个字符。主要使用两个函数:fgetc()
和fputc()
。
int fgetc (FILE *fp);fp 为文件指针。fgetc() 读取成功时返回读取到的字符,读取到文件末尾或读取失败时返回
EOF
。EOF 不绝对是 -1,也可以是其他负数,这要看编译器的实现。fgetc() 使用举例:
- char ch;
- FILE *fp = fopen("D:\\demo.txt", "r+");
- ch = fgetc(fp);
D:\\demo.txt
文件中读取一个字符,并保存到变量ch中。
- #include<stdio.h>
- int main(){
- FILE *fp;
- char ch;
- //如果文件不存在,给出提示并退出
- if( (fp=fopen("D:\\demo.txt","rt")) == NULL ){
- printf("Cannot open file, press any key to exit!");
- getch();
- exit(1);
- }
- //每次读取一个字节,直到读取完毕
- while( (ch=fgetc(fp)) != EOF ){
- putchar(ch);
- }
- putchar(‘\n‘); //输出换行符
- fclose(fp);
- return 0;
- }
(ch=fgetc(fp))
!= EOF
。fget() 每次从位置指针所在的位置读取一个字符,并保存到变量 ch,位置指针向后移动一个字节。当文件指针移动到文件末尾时,fget() 就无法读取字符了,于是返回
EOF,表示文件读取结束了。
int feof ( FILE * fp );当指向文件末尾时返回非零值,否则返回零值。
int ferror ( FILE *fp );出错时返回非零值,否则返回零值。
- #include<stdio.h>
- int main(){
- FILE *fp;
- char ch;
- //如果文件不存在,给出提示并退出
- if( (fp=fopen("D:\\demo.txt","rt")) == NULL ){
- printf("Cannot open file, press any key to exit!");
- getch();
- exit(1);
- }
- //每次读取一个字节,直到读取完毕
- while( (ch=fgetc(fp)) != EOF ){
- putchar(ch);
- }
- putchar(‘\n‘); //输出换行符
- if(ferror(fp)){
- puts("读取出错");
- }else{
- puts("读取成功");
- }
- fclose(fp);
- return 0;
- }
int fputc ( int ch, FILE *fp );ch 为要写入的字符,fp 为文件指针。fputc() 写入成功时返回写入的字符,失败时返回EOF,返回值类型为 int 也是为了容纳这个负数。例如:
fputc(‘a‘, fp);或者:
char ch = ‘a‘; fputc(ch, fp);表示把字符 ‘a‘ 写入fp所指向的文件中。
- #include<stdio.h>
- int main(){
- FILE *fp;
- char ch;
- //判断文件是否成功打开
- if( (fp=fopen("D:\\demo.txt","wt+")) == NULL ){
- printf("Cannot open file, press any key to exit!\n");
- getch();
- exit(1);
- }
- printf("Input a string:\n");
- //每次从键盘读取一个字符并写入文件
- while ( (ch=getchar()) != ‘\n‘ ){
- fputc(ch,fp);
- }
- fclose(fp);
- return 0;
- }
char *fgets ( char *str, int n, FILE *fp );str 为字符数组,n 为要读取的字符数目,fp 为文件指针。
- #define N 101
- char str[N];
- FILE *fp = fopen("D:\\demo.txt", "r");
- fgets(str, N, fp);
- #include <stdio.h>
- #include <stdlib.h>
- #define N 100
- int main(){
- FILE *fp;
- char str[N+1];
- if( (fp=fopen("d:\\demo.txt","rt")) == NULL ){
- printf("Cannot open file, press any key to exit!\n");
- getch();
- exit(1);
- }
- while(fgets(str, N, fp) != NULL){
- printf("%s", str);
- }
- fclose(fp);
- system("pause");
- return 0;
- }
C语言中文网
http://c.biancheng.net
一个学习编程的好网站!
int fputs( char *str, FILE *fp );str 为要写入的字符串,fp 为文件指针。写入成功返回非负数,失败返回EOF。例如:
- char *str = "http://c.biancheng.net";
- FILE *fp = fopen("D:\\demo.txt", "at+");
- fputs(str, fp);
- #include<stdio.h>
- int main(){
- FILE *fp;
- char str[102] = {0}, strTemp[100];
- if( (fp=fopen("D:\\demo.txt", "at+")) == NULL ){
- printf("Cannot open file, press any key to exit!\n");
- getch();
- exit(1);
- }
- printf("Input a string:");
- gets(strTemp);
- strcat(str, "\n");
- strcat(str, strTemp);
- fputs(str, fp);
- fclose(fp);
- return 0;
- }
C
C++ Java Linux Shell
,打开 D:\\demo.txt,文件内容为:
C语言中文网
http://c.biancheng.net
一个学习编程的好网站!
C C++ Java Linux Shell
size_t fread ( void *ptr, size_t size, size_t count, FILE *fp );fwrite() 函数用来向文件中写入块数据,它的原型为:
size_t fwrite ( void * ptr, size_t size, size_t count, FILE *fp );对参数的说明:
- #include<stdio.h>
- #define N 5
- int main(){
- //从键盘输入的数据放入a,从文件读取的数据放入b
- int a[N], b[N];
- int i, size = sizeof(int);
- FILE *fp;
- if( (fp=fopen("D:\\demo.txt", "rb+")) == NULL ){
- printf("Cannot open file, press any key to exit!\n");
- getch();
- exit(1);
- }
- //从键盘输入数据 并保存到数组a
- for(i=0; i<N; i++){
- scanf("%d", &a[i]);
- }
- //将数组a的内容写入到文件
- fwrite(a, size, N, fp);
- //将文件中的位置指针重新定位到文件开头
- rewind(fp);
- //从文件读取内容并保存到数组b
- fread(b, size, N, fp);
- //在屏幕上显示数组b的内容
- for(i=0; i<N; i++){
- printf("%d ", b[i]);
- }
- printf("\n");
- fclose(fp);
- return 0;
- }
"rb+"
方式打开文件,数据以二进制形式写入文件,一般无法阅读。rewind(fp);
的作用。更多关于rewind函数的内容请点击:C语言rewind函数。
文件的后缀不一定是 .txt,它可以是任意的,你可以自己命名,例如 demo.ddd、demo.doc、demo.diy 等。【示例】从键盘输入两个学生数据,写入一个文件中,再读出这两个学生的数据显示在屏幕上。
- #include<stdio.h>
- #define N 2
- struct stu{
- char name[10]; //姓名
- int num; //学号
- int age; //年龄
- float score; //成绩
- }boya[N], boyb[N], *pa, *pb;
- int main(){
- FILE *fp;
- int i;
- pa = boya;
- pb = boyb;
- if( (fp=fopen("d:\\demo.txt", "wb+")) == NULL ){
- printf("Cannot open file, press any key to exit!\n");
- getch();
- exit(1);
- }
- //从键盘输入数据
- printf("Input data:\n");
- for(i=0; i<N; i++,pa++){
- scanf("%s %d %d %f",pa->name, &pa->num,&pa->age, &pa->score);
- }
- //将数组 boya 的数据写入文件
- fwrite(boya, sizeof(struct stu), N, fp);
- //将文件指针重置到文件开头
- rewind(fp);
- //从文件读取数据并保存到数据 boyb
- fread(boyb, sizeof(struct stu), N, fp);
- //输出数组 boyb 中的数据
- for(i=0; i<N; i++,pb++){
- printf("%s %d %d %f\n", pb->name, pb->num, pb->age, pb->score);
- }
- fclose(fp);
- return 0;
- }
Input data: Tom 2 15 90.5↙ Hua 1 14 99↙ Tom 2 15 90.500000 Hua 1 14 99.000000
int fscanf ( FILE *fp, char * format, ... ); int fprintf ( FILE *fp, char * format, ... );fp 为文件指针,format 为格式控制字符串,... 表示参数列表。与 scanf() 和 printf() 相比,它们仅仅多了一个 fp 参数。例如:
- FILE *fp;
- int i, j;
- char *str, ch;
- fscanf(fp, "%d %s", &i, str);
- fprintf(fp,"%d %c", j, ch);
- #include<stdio.h>
- #define N 2
- struct stu{
- char name[10];
- int num;
- int age;
- float score;
- } boya[N], boyb[N], *pa, *pb;
- int main(){
- FILE *fp;
- int i;
- pa=boya;
- pb=boyb;
- if( (fp=fopen("D:\\demo.txt","wt+")) == NULL ){
- printf("Cannot open file, press any key exit!");
- getch();
- exit(1);
- }
- //从键盘读入数据,保存到boya
- printf("Input data:\n");
- for(i=0; i<N; i++,pa++){
- scanf("%s %d %d %f", pa->name, &pa->num, &pa->age, &pa->score);
- }
- pa = boya;
- //将boya中的数据写入到文件
- for(i=0; i<N; i++,pa++){
- fprintf(fp,"%s %d %d %f\n", pa->name, pa->num, pa->age, pa->score);
- }
- //重置文件指针
- rewind(fp);
- //从文件中读取数据,保存到boyb
- for(i=0; i<N; i++,pb++){
- fscanf(fp, "%s %d %d %f\n", pb->name, &pb->num, &pb->age, &pb->score);
- }
- pb=boyb;
- //将boyb中的数据输出到显示器
- for(i=0; i<N; i++,pb++){
- printf("%s %d %d %f\n", pb->name, pb->num, pb->age, pb->score);
- }
- fclose(fp);
- return 0;
- }
Input data: Tom 2 15 90.5↙ Hua 1 14 99↙ Tom 2 15 90.500000 Hua 1 14 99.000000打开 D:\\demo.txt,发现文件的内容是可以阅读的,格式非常清晰。用 fprintf() 和 fscanf() 函数读写配置文件、日志文件会非常方便,不但程序能够识别,用户也可以看懂,可以手动修改。
- #include<stdio.h>
- int main(){
- int a, b, sum;
- fprintf(stdout, "Input two numbers: ");
- fscanf(stdin, "%d %d", &a, &b);
- sum = a + b;
- fprintf(stdout, "sum=%d\n", sum);
- return 0;
- }
void rewind ( FILE *fp );fseek() 用来将位置指针移动到任意位置,它的原型为:
int fseek ( FILE *fp, long offset, int origin );参数说明:
起始点 | 常量名 | 常量值 |
---|---|---|
文件开头 | SEEK_SET | 0 |
当前位置 | SEEK_CUR | 1 |
文件末尾 | SEEK_END | 2 |
fseek(fp, 100, 0);值得说明的是,fseek() 一般用于二进制文件,在文本文件中由于要进行转换,计算的位置有时会出错。
- #include<stdio.h>
- #define N 3
- struct stu{
- char name[10]; //姓名
- int num; //学号
- int age; //年龄
- float score; //成绩
- }boys[N], boy, *pboys;
- int main(){
- FILE *fp;
- int i;
- pboys = boys;
- if( (fp=fopen("d:\\demo.txt", "wb+")) == NULL ){
- printf("Cannot open file, press any key to exit!\n");
- getch();
- exit(1);
- }
- printf("Input data:\n");
- for(i=0; i<N; i++,pboys++){
- scanf("%s %d %d %f", pboys->name, &pboys->num, &pboys->age, &pboys->score);
- }
- fwrite(boys, sizeof(struct stu), N, fp); //写入三条学生信息
- fseek(fp, sizeof(struct stu), SEEK_SET); //移动位置指针
- fread(&boy, sizeof(struct stu), 1, fp); //读取一条学生信息
- printf("%s %d %d %f\n", boy.name, boy.num, boy.age, boy.score);
- fclose(fp);
- return 0;
- }
size_t fread ( void *ptr, size_t size, size_t count, FILE *fp );它返回成功读写的块数,该值小于等于 count。如果我们让参数 size 等于1,那么返回的就是读取的字节数。
- #include <stdio.h>
- #include <stdlib.h>
- int copyFile(char *fileRead, char *fileWrite);
- int main(){
- char fileRead[100]; // 要复制的文件名
- char fileWrite[100]; // 复制后的文件名
- // 获取用户输入
- printf("要复制的文件:");
- scanf("%s", fileRead);
- printf("将文件复制到:");
- scanf("%s", fileWrite);
- // 进行复制操作
- if( copyFile(fileRead, fileWrite) ){
- printf("恭喜你,文件复制成功!\n");
- }else{
- printf("文件复制失败!\n");
- }
- return 0;
- }
- /**
- * 文件复制函数
- * @param fileRead 要复制的文件
- * @param fileWrite 复制后文件的保存路径
- * @return int 1: 复制成功;2: 复制失败
- **/
- int copyFile(char *fileRead, char *fileWrite){
- FILE *fpRead; // 指向要复制的文件
- FILE *fpWrite; // 指向复制后的文件
- int bufferLen = 1024*4; // 缓冲区长度
- char *buffer = (char*)malloc(bufferLen); // 开辟缓存
- int readCount; // 实际读取的字节数
- if( (fpRead=fopen(fileRead, "rb")) == NULL || (fpWrite=fopen(fileWrite, "wb")) == NULL ){
- printf("Cannot open file, press any key to exit!\n");
- getch();
- exit(1);
- }
- // 不断从fileRead读取内容,放在缓冲区,再将缓冲区的内容写入fileWrite
- while( (readCount=fread(buffer, 1, bufferLen, fpRead)) > 0 ){
- fwrite(buffer, readCount, 1, fpWrite);
- }
- free(buffer);
- fclose(fpRead);
- fclose(fpWrite);
- return 1;
- }
要复制的文件:d://1.mp4 将文件复制到:d://2.mp4 恭喜你,文件复制成功!如果文件不存在,会给出提示,并终止程序:
要复制的文件:d://123.mp4 将文件复制到:d://333.mp4 d://cyuyan.txt: No such file or directory
FILE *fp;这里的FILE,实际上是在stdio.h中定义的一个结构体,该结构体中含有文件名、文件状态和文件当前位置等信息,fopen 返回的就是FILE类型的指针。
- typedef struct _iobuf {
- int cnt; // 剩余的字符,如果是输入缓冲区,那么就表示缓冲区中还有多少个字符未被读取
- char *ptr; // 下一个要被读取的字符的地址
- char *base; // 缓冲区基地址
- int flag; // 读写状态标志位
- int fd; // 文件描述符
- // 其他成员
- } FILE;
long int ftell ( FILE * fp );注意:fp 要以二进制方式打开,如果以文本方式打开,函数的返回值可能没有意义。
- long fsize(FILE *fp){
- fseek(fp, 0, SEEK_END);
- return ftell(fp);
- }
long size = fsize(fp); fread(buffer, 1, 1, fp);fread() 函数将永远读取不到内容。
- long fsize(FILE *fp){
- long n;
- fpos_t fpos; //当前位置
- fgetpos(fp, &fpos); //获取当前位置
- fseek(fp, 0, SEEK_END);
- n = ftell(fp);
- fsetpos(fp,&fpos); //恢复之前的位置
- return n;
- }
- #include<stdio.h>
- #include<stdlib.h>
- #include<conio.h>
- long fsize(FILE *fp);
- int main(){
- long size = 0;
- FILE *fp = NULL;
- char filename[30] = "D:\\1.mp4";
- if( (fp = fopen(filename, "rb")) == NULL ){ //以二进制方式打开文件
- printf("Failed to open %s...", filename);
- getch();
- exit(EXIT_SUCCESS);
- }
- printf("%ld\n", fsize(fp));
- return 0;
- }
- long fsize(FILE *fp){
- long n;
- fpos_t fpos; //当前位置
- fgetpos(fp, &fpos); //获取当前位置
- fseek(fp, 0, SEEK_END);
- n = ftell(fp);
- fsetpos(fp,&fpos); //恢复之前的位置
- return n;
- }
- /**
- * 文件复制函数
- * @param fSource 要复制的原文件
- * @param offsetSource 原文件的位置偏移(相对文件开头),也就是从哪里开始复制
- * @param len 要复制的内容长度,小于0表示复制offsetSource后边的所有内容
- * @param fTarget 目标文件,也就是将文件复制到哪里
- * @param offsetTarget 目标文件的位置偏移,也就是复制到目标文件的什么位置
- * @return 成功复制的字节数
- **/
- long fcopy(FILE *fSource, long offsetSource, long len, FILE *fTarget, long offsetTarget){
- int bufferLen = 1024*4; // 缓冲区长度
- char *buffer = (char*)malloc(bufferLen); // 开辟缓存
- int readCount; // 每次调用fread()读取的字节数
- long nBytes = 0; //总共复制了多少个字节
- int n = 0; //需要调用多少次fread()函数
- int i; //循环控制变量
- fseek(fSource, offsetSource, SEEK_SET);
- fseek(fTarget, offsetTarget, SEEK_SET);
- if(len<0){ //复制所有内容
- while( (readCount=fread(buffer, 1, bufferLen, fSource)) > 0 ){
- nBytes += readCount;
- fwrite(buffer, readCount, 1, fTarget);
- }
- }else{ //复制len个字节的内容
- n = (int)ceil((double)((double)len/bufferLen));
- for(i=1; i<=n; i++){
- if(len-nBytes < bufferLen){ bufferLen = len-nBytes; }
- readCount = fread(buffer, 1, bufferLen, fSource);
- fwrite(buffer, readCount, 1, fTarget);
- nBytes += readCount;
- }
- }
- fflush(fTarget);
- free(buffer);
- return nBytes;
- }
fcopy(fSource, 0, -1, fTarget, 0);
- /**
- * 向文件中插入内容
- * @param fp 要插入内容的文件
- * @param buffer 缓冲区,也就是要插入的内容
- * @param offset 偏移量(相对文件开头),也就是从哪里开始插入
- * @param len 要插入的内容长度
- * @return 成功插入的字节数
- **/
- int finsert(FILE *fp, long offset, void *buffer, int len){
- long fileSize = fsize(fp);
- FILE *fpTemp; //临时文件
- if(offset>fileSize || offset<0 || len<0){ //插入错误
- return -1;
- }
- if(offset == fileSize){ //在文件末尾插入
- fseek(fp, offset, SEEK_SET);
- if(!fwrite(buffer, len, 1, fp)){
- return -1;
- }
- }
- if(offset < fileSize){ //从开头或者中间位置插入
- fpTemp = tmpfile();
- fcopy(fp, 0, offset, fpTemp, 0);
- fwrite(buffer, len, 1, fpTemp);
- fcopy(fp, offset, -1, fpTemp, offset+len);
- freopen(FILENAME, "wb+", fp );
- fcopy(fpTemp, 0, -1, fp, 0);
- fclose(fpTemp);
- }
- return 0;
- }
- int fdelete(FILE *fp, long offset, int len){
- long fileSize = getFileSize(fp);
- FILE *fpTemp;
- if(offset>fileSize || offset<0 || len<0){ //错误
- return -1;
- }
- fpTemp = tmpfile();
- fcopy(fp, 0, offset, fpTemp, 0); //将前offset字节的数据复制到临时文件
- fcopy(fp, offset+len, -1, fpTemp, offset); //将offset+len之后的所有内容都复制到临时文件
- freopen(FILENAME, "wb+", fp ); //重新打开文件
- fcopy(fpTemp, 0, -1, fp, 0);
- fclose(fpTemp);
- return 0;
- }
标签:
原文地址:http://blog.csdn.net/shuimanting520/article/details/51360169