C语言文件操作
一:打开和关闭文件
打开和关闭文件分以下三个步骤:
- 1.定义文件指针
定 义一个结构体类型的指针: FILE* read;
类型:FILE 类型的结构体
需要包含的头文件:标准输入输出头文件(stdio.h) - 2.打开文件
2.1 fopen(“文件路径”,“文件打开方式”);
函数原型:FILE* fopen(char* fileName,char* mode);
2.2 文件路径:
2.2.1 相对路径
2.2.2 绝对路径
2.2.3 路径中 单反斜杠 要改为 双反斜杠 或者 斜杠, 即 \ 改为 \ \ 或者 / 。
2.3 文件打开方式:
r : read 只读 没有创建文件的功能
w : write 只写 如果文件不存在,就创建一个文件,如果文件存在就清空原来的文件
a : append 追加模式 在原来的文件末尾接着写,不会清空原本存在文件中的内容(文件不存在时,也会创建文件)
组合方式 :
+ : 可读可写 组合 : r+ w+ a+
b : 二进制(binary) 组合 : ab+ wb+ rb+ a+b w+b r+b rb wb ab
注意:返回NULL,表示打开文件失败 要进行安全处理(判空处理) - 3.关闭文件
fclose(read);
示例:
未正常打开文件有没有进行判空处理:
#define _CRT_SECURE_NO_WARNINGS//VS中替换_s系列函数需要添加的宏定义
#include<stdio.h>
#include<stdlib.h>
int main()
{
//自定义类型的文件 另外两种形式的文件 1.标准输入(stdin)键盘输入 2.标准输出(stdout)屏幕输出
FILE* read = fopen("mast.txt", "r");
fclose(read);
system("pause");
return 0;
}
运行上述程序会出现以下的错误:Expression:stream!=NULL或者Expression:stream.valid(),
这是由于无法打开文件,read在异常赋值后,又被关闭造成的。
因而为预防类似的文件错误,我们常常需要进行错误预防。提醒错误,并提前结束进程。防止释放非法的文件结构体指针。
错误预防改进:
#define _CRT_SECURE_NO_WARNINGS//VS中替换_s系列函数需要添加的宏定义
#include<stdio.h>
#include<stdlib.h>
int main()
{
//自定义类型的文件 另外两种形式的文件 1.标准输入(stdin)键盘输入 2.标准输出(stdout)屏幕输出
FILE* read = fopen("mast.txt", "r");//Expression:stream!=NULL 或者 Expression:stream.valid()错误通常是文件打开失败
if (read == NULL)
{
printf("文件打开失败!\n");
return 0; //这里要提前关闭程序,文件打开失败不能关闭文件
}
fclose(read);
system("pause");
return 0;
}
当第一次打开文件时,文件不存在,创建一个文件并进行操作。当文件存在时进行读写:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
int main()
{
FILE* readInfo = fopen("game.txt", "r");
if (readInfo == NULL)//文件为空,打开失败
{
readInfo = fopen("game.txt", "w+");//那么就以写的方式进行打开
}
fclose(readInfo);
system("pause");
return 0;
}
二:以字符方式读写文件
以字符方式进行文件的读写过程是靠以下两个函数进行实现的:
读操作: fgetc(FILE* read)
返回 : 返回的值是读取的字符
写操作: fputc(char ch,FILE* read)
将字符 ch 写入 read 文件指针所指向的文件中去
需要注意的是文件指针在读写的过程中是会自动进移动的
示例:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
int main()
{
FILE* write = fopen("write.txt", "w");
if (write == NULL)//文件判空处理
{
printf("文件打开失败!\n");
return 0;//文件打开失败就提前结束程序
}
char str[] = "天津科技大学,人工智能学院!";//待写入的数据
int count = 0;
while (str[count] != '\0')//当未到达字符串末尾时,进行循环写入
{
//将字符挨个读入文件
fputc(str[count++], write);//文件指针自动往后移动
}
//*****************************************************************************
//***注意:将一串字符串挨个读入文件时,字符串结尾标记 '\0' 需要我们进行单独写入。***
fputc('\0', write);//字符'\0'需另外写入文件,如果不写入'\0'则文件write.txt中内容不构字符串
//***注意:将一串字符串挨个读入文件时,字符串结尾标记 '\0' 需要我们进行单独写入。***
//******************************************************************************
fclose(write);//时刻不要忘记关闭文件
FILE* read = fopen("write.txt", "r");
if (read == NULL)//文件判空处理
{
printf("打开文件失败!\n");
return 0;//文件打开失败就提前结束程序
}
char readChar = fgetc(read);
char readStr[100];
count = 0;
//文件结束标记:EOF
while (readChar != EOF)
{
//从文件中挨个读取字符,并进行存储到数组readStr中
readStr[count++] = readChar;
readChar = fgetc(read);
}
printf("readStr = %s\n", readStr);
fclose(read);//时刻不要忘记关闭文件
system("pause");
return 0;
}
这里需要注意的是,将一串字符串挨个读入文件时,字符串结尾标记 ‘\0’ 需要我们进行单独写入。
否则当我们挨个进行读取并且存储到字符数组中时,字符数组并不能形成字符串(字符数组末尾元素没有’ \0 '标记),在用%s打印时会出现找不到结尾的情况
文件操作将一个文件中的小写转化为大写:
通过文件操作,将一个文件中的小写字母转换为大写
没有办法利用文件指针去做定点操作
只能将文件读取出来再进行小写转换成大写再写入文件
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
void a2A(char* str)//函数实现将数组中小写字符转换成大写字符
{
int i = 0;
while (str[i] != '\0')
{
if (str[i] >= 'a' && str[i] <= 'z')
{
str[i] -= 32;
}
i++;
}
}
int main()
{
FILE* read = fopen("aaa.txt", "r");
if (read == NULL)//判空处理
{
printf("打开文件失败!\n");
return 0;
}
char str[1024] = "";
int count = 0;
char readChar = fgetc(read);
while (readChar !=EOF)
{
str[count++] = readChar;
readChar = fgetc(read);
}
fclose(read);//关闭文件指针
FILE* write = fopen("aaa.txt", "w");
if (write == NULL)//判空处理
{
printf("文件打开失败!\n");
return 0;
}
a2A(str);//将数组中小写转大写
count = 0;
while (str[count] != '\0')
{
fputc(str[count], write);
count++;
}
fputc('\0', write);//'\0'需要进行单独写入
fclose(write);//关闭文件指针
system("pause");
return 0;
}
三:以字符串方式读写文件
用到的三个函数:
文件读取操作:
函数:fgets(buf,STLEN,fp);
- buf是char类型数组的名称,STLEN是字符串的大小(字符串的大小是指占用的空间,字符串的长度是指字符的个数),fp是指向FILE的指针
- fgets()读取输入直到第一个换行符的后面,或者读到文件末尾,或者读取STLEN-1个字符。然后fgets()在末尾添加一个空字符使之成为一个字符串。字符串的大小是其字符数加上一个空字符。如果fgets()在读到字符上限之前已经读完一整行,它会把表示行结尾的换行符放到空字符的前面。fgets()函数在遇到EOF时将返回NULL值,可以利用这一机制检查是否到达文件结尾;如果未遇到EOF则返回之前传给它的第一个参数地址。
- 当fp指向的文件为多行时,需要进行循环读取
文件写入操作:fputs(buf,fp);
- buf是字符串的地址,fp用于指向目标文件
- 同puts()不同的是,fputs()在打印字符串时不会在其末尾添加换行符。
判断文件指针是否到达文件末尾:feof();
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
/*
fgets():文件读操作
fputs():文件写操作
feof();判断文件指针是否到文件末尾
*/
int main()
{
FILE* write = fopen("剑客排行榜.txt", "w");
if (write == NULL)//判空处理
{
printf("文件打开失败!\n");
return 0;
}
char str1[] = "1.LittleStar";
fputs(str1, write);
char str2[] = "2.haha";
fputs(str2, write);
fclose(write);//关闭文件指针
FILE* read = fopen("剑客排行榜.txt", "r");
if (read == NULL)//判空处理
{
printf("文件打开失败!\n");
return 0;
}
char str3[100] = "";
while (!feof(read))//文件指针read所指向的文件为多行时,需要进行循环读取
{
fgets(str3, 100, read);
printf("str3=%s", str3);//当文件中有换行时,需要循环读取
}
//不使用feof()函数:
//while (fgets(str3, 100, read))//当文件中有换行时,需要循环读取
//{
// printf("str3=%s", str3);
//}
fclose(read);
system("pause");
return 0;
}
四:格式化读写
前面的以字符形式读写和以字符串形式进行读写大多读写的形式没有规律
那么格式化读写则适用于我们需要进行有规律有格式的进行读写
使得读写内容易于表格化呈现,常用于结构体类型内容的读写
fprintf(): 写操作
fscanf(): 读操作
fprintf()、fscanf()函数的用法和printf()、scanf()的用法类似
区别在于:fprintf()、fscanf()在使用时需要第一个参数需要传入待处理的文件的文件指针
fprintf()的第一个参数为stdout时同printf()函数使用一致,表示写入到屏幕上,否者就写入到文件指针所指向的文件中去
fscanf()的第一个参数为stdin时同scanf()函数使用一致,表示从键盘的输入读取内容。否者就从文件指针所指向的内容进行读取操作
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
struct girlFriend
{
char girlName[20];
int girlNum;
int age;
};
int main()
{
//写操作:
FILE* write = fopen("girl.txt", "w");
if (write == NULL)//判空操作
{
printf("文件打开失败!\n");
return 0;
}
struct girlFriend girl[3];
for (int i = 0; i < 3; i++)
{
printf("请输入第%d个女朋友的信息:\n", i + 1);
scanf("%s%d%d", girl[i].girlName, &girl[i].girlNum, &girl[i].age);
fprintf(write, "%s\t%d\t%d\n", girl[i].girlName, girl[i].girlNum, girl[i].age);//写入到文件指针write所指向的文件中去
}
fclose(write);//关闭文件指针
//读操作:
FILE* read = fopen("girl.txt", "r");
if (read == NULL)//判空操作
{
printf("文件打开失败!\n");
return 0;
}
int count = 0;
struct girlFriend readInfo[3];
while (fscanf(read, "%s\t%d\t%d\n", readInfo[count].girlName,
&readInfo[count].girlNum, &readInfo[count].age)!=EOF)//从文件指针read所指向的文件中读取内容并存到结构体readInfo中
{
printf("%s\t%d\t%d\n", readInfo[count].girlName, readInfo[count].girlNum, readInfo[count].age);
count++;
}
fclose(read);//关闭文件指针
system("pause");
return 0;
}
五:以字节流/二进制的形式读写
读操作:
fread(char *buff,int count,size_t size,FILE *fp);
buff:读到的内存 count:读取的次数 size:大小 fp:所读取文件的文件指针
写操作:
fwrite(char *buff,nt count,size_t size,FILE *fp);
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
struct girlFriend
{
char girlName[20];
int girlNum;
int age;
};
int main()
{
//写操作:
FILE* write = fopen("girl.txt", "w");
if (write == NULL)//判空操作
{
printf("文件打开失败!\n");
return 0;
}
struct girlFriend girl[3];
for (int i = 0; i < 3; i++)
{
printf("请输入第%d个女朋友的信息:\n", i + 1);
scanf("%s%d%d", girl[i].girlName, &girl[i].girlNum, &girl[i].age);
fwrite(girl + i, 1, sizeof(struct girlFriend), write);//运用字节流/二进制形式写一次
}
fclose(write);//关闭文件指针
system("pause");
return 0;
}
进行上面操作后我们已经将内容写入到文件girl.txt中了
但是我们打开文件girl.txt发现里面存在乱码,这是由于对齐的缘故,这是正常的
我们仍可利用fread()函数可以进行正常的读取
读取:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
/*
读操作:
fread(char *buff,int count,size_t size,FILE *fp);
写操作:
fwrite(char *buff,nt count,size_t size,FILE *fp);
*/
struct girlFriend
{
char girlName[20];
int girlNum;
int age;
};
int main()
{
//写操作:
FILE* write = fopen("girl.txt", "w");
if (write == NULL)//判空操作
{
printf("文件打开失败!\n");
return 0;
}
struct girlFriend girl[3];
for (int i = 0; i < 3; i++)
{
printf("请输入第%d个女朋友的信息:\n", i + 1);
scanf("%s%d%d", girl[i].girlName, &girl[i].girlNum, &girl[i].age);
fwrite(girl + i, 1, sizeof(struct girlFriend), write);//运用字节流/二进制形式写一次
}
fclose(write);//关闭文件指针
读操作:
FILE* read = fopen("girl.txt", "r");
if (read == NULL)//判空操作
{
printf("文件打开失败!\n");
return 0;
}
struct girlFriend readInfo[3];
for (int i = 0; i < 3; i++)
{
fread(readInfo + i, 1, sizeof(struct girlFriend), read);//读取一次
printf("%s\t%d\t%d\n", readInfo[i].girlName, readInfo[i].girlNum, readInfo[i].age);
}
printf("\n");
fclose(read);//关闭文件指针
FILE* readFile = fopen("girl.txt", "r");
struct girlFriend readStr[3];
fread(readInfo, 3, sizeof(struct girlFriend), read);//读三次,每次都读取的是sizeof(struct girlFriend)大小,读完
for (int i = 0; i < 3; i++)
{
printf("%s\t%d\t%d\n", readInfo[i].girlName, readInfo[i].girlNum, readInfo[i].age);
}
printf("\n");
fread(readInfo, 1, sizeof(struct girlFriend)*3, read);//读一次,每次都读取的是sizeof(struct girlFriend)*3大小,读完
for (int i = 0; i < 3; i++)
{
printf("%s\t%d\t%d\n", readInfo[i].girlName, readInfo[i].girlNum, readInfo[i].age);
}
printf("\n");
fclose(readFile);
system("pause");
return 0;
}
我们可以看到虽然直接打开文件看到的是乱码,但是我们是可以用fwrite()正常的进行读取出来。
六:文件指针
- ftell(FILE* fp)
- 参数为文件指针类型
- 返回值为long类型的值,表示文件中的文件指针的位置
- 通过两个不同状态的文件指针的位置,可用来判断文件指针移动了多少字节
- fseek(FILE* fp,long num,int pos)
- fp:已打开的待查找的文件指针的类型
- num:偏移量(offset),表示从指定位置要移动的距离,必须是一个long类型的数据,可正(相对指定位置往前移动)可负(后移)
- pos:模式(即指定位置),为宏常量。
模式 | 偏移量的起始点 | 值 |
---|---|---|
SEEK_SET | 文件开始处 | 0 |
SEEK_CUR | 当前位置 | 1 |
SEEK_END | 文件末尾 | 2 |
fseek()使用的示例:
调用 | 效果 |
---|---|
fseek(fp,0L,SEEK_SET); | 定位至文件开始处 |
fseek(fp,10L,SEEK_SET); | 定位至文件中的第10个字节 |
fseek(fp,10L,SEEK_CUR); | 从文件当前位置前移2个字节 |
fseek(fp,0L,SEEK_END); | 定位置文件结尾 |
fseek(fp,-10L,SEEK_SET); | 从文件结尾处回退10个字节 |
- rewind(FILE* fp):将文件指针还原到文件开始的位置
示例:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
int main()
{
FILE* read = fopen("getStr.txt", "r");
if(read==NULL)//判空操作
{
printf("文件打开失败!\n");
return 0;
}
printf("l = %d\n", ftell(read));
fseek(read, 0L, SEEK_END); //文件指针移动到末尾
printf("l = %d\n", ftell(read));
fclose(read);
system("pause");
return 0;
}
j结果:
文件指针的用例:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
int main()
{
FILE* read = fopen("getStr.txt", "r");
fseek(read, 0L, SEEK_END); //文件指针移动到末尾
int size = ftell(read); //size即为文件的大小
char* str = (char*)malloc(sizeof(char) * size);
//fseek(read, 0L, SEEK_SET);
rewind(read); //与上一句等效,将文件指针移动到文件开始的位置
fgets(str, size, read);
puts(str);
fclose(read);
system("pause");
return 0;
}
七:文件重定向
文件重定向就是把文件当做输入输出,将操作都是在文件里完成。
将输出文件从stdout重定向到output.txt中
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
int main()
{
freopen("output.txt", "w", stdout);
printf("天津科技大学!\n");
int num = 6;
for (int i = 0; i < 3; i++)
{
printf("%d", num);
}
return 0;
}
将output.txt重定向为屏幕输出,将input.txt重定向为键盘输入
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
int main()
{
freopen("output.txt", "w", stdout);
freopen("input.txt", "r", stdin);
int year, month, day;
scanf("%d%d%d", &year, &month, &day);
char str[20] = "";
scanf("%s", str);
printf("%d %d %d\n", year, month, day);
printf("%s", str);
return 0;
}
运行前output.txt为空文件
input.txt:
运行后:
output.txt: