一、文件概述
1.磁盘文件
指一组相关数据的有序集合,通常存储在外部介质(如磁盘)上,使用时才调入内存。从用户或者操作系统使用的角度(逻辑上)把文件分为文本文件和二进制文件。
a.文本文件
1.基于字符编码的文件,常见编码有ASCII、UNICODE等;
2.一般可以使用文本编辑器直接打开;
3.数5678,以ASCII存储形式为:00110101 00110110 00110111 00111000。
b.二进制文件
1基于值编码的文件,自己根据具体应用,指定某个值是什么意思;
2.把内存中的数据按其在内存中的存储形式原样输出到磁盘上;
3.数5678,以二进制存储形式为:00010110 00101110。
2.设备文件
在操作系统中把每一个与主机相连的输入、输出设备看作是一个文件,把它们的输入、输出等同于对磁盘文件的读和写。
二、对文本文件的操作
1.打开文件
(1)函数
#include<stdio.h>
FILE* fopen(const char* filename,const char* mode);
参数:filename:需要打开的文件名(包括路径)
mode:打开文件的模式设置
返回值:成功:文件指针;失败:NULL
(2)打开文件的模式
打开模式 | 含义 | 备注 |
---|---|---|
r或rb | 以只读的方式打开一个文本文件(文件不存在则报错) | 1.b是二进制的意思,只在windows中有效,在Linux用r和rb的结果一样。 2.Unix和Linux下所有的文本文件行都是\n结尾,而Windows所有的文本文件行都是\r\n结尾。 3.Unix和Linux下,“文本”与“二进制”模式没有区别,"\r\n"作为两个字符原样输入输出 4.Windows下,以“文本”方式打开文件,即不加b时: a.读取文件时,系统会将所有的\r\n转换为\n b.写入文件时,系统会将\n转换为\r\n 5.Windows下,以“二进制”方式打开文件时,不会进行\r\n与\n的互相转换
|
w或wb | 以写的方式打开文件(文件存在则清空,文件不存在则创建) | |
a或ab | 以追加的方式打开文件,在末尾添加内容(文件不存在则创建) | |
r+或rb+ | 以可读、可写的方式打开文件(若文件不存在则报错) | |
w+或wb+ | 以可读、可写的方式打开文件(文件存在则清空,文件不存在则创建) | |
a+或ab+ | 以追加的方式打开文件,在末尾添加内容(文件不存在则创建) |
2.关闭文件
(1)关闭文件的意义
1.打开的文件会占用内存资源,如果总是打开不关闭,会消耗很多内存。
2.一个进程同时打开的文件数是有限制的,超过最大同时打开文件数,再次调用 fopen 打开文件会失败。
3.如果没有明确的调用 fclose 关闭打开的文件,那么程序在退出的时候,操作系统会统一关闭。
(2)函数
#include<stdio.h>
int fclose(FILE* stream);
参数:stream:文件指针
返回值:成功:0;失败:-1
3.按照字符读写文件
(1)写文件
#include<stdio.h>
int fputc(int ch,FILE* stream);
参数:ch:需要写入文件的字符
stream:文件指针
返回值:成功:成功写入文件的字符;失败:-1
(2)读文件
#include<stdio.h>
int fgetc(FILE* stream);
参数:stream:文件指针
返回值:成功:成功读取到的字符;失败:-1
(3)检测文件结尾
1.EOF:#define EOF (-1)
在 C 语言中 , EOF 表示文件结束符 ( end of file )。在 while 循环中以 EOF 作为文件结束标志,这种以 EOF 作为文件结束标志的文件,必须是文本文件。在文本文件中 , 数据都是以字符的 ASCII 代码值的形式存放,ASCII 代码值的范围是 0 ~ 127 , 不可能出现 -1 , 因此可以用 EOF 作为文件结束标志。
2.feof():判断的是最后一次“读操作的内容”,不是当前位置内容(上一个内容)
当把数据以二进制形式存放到文件中时,就会有 -1 值的出现,因此不能采用EOF作为二进制文件的结束标志 . 为解决这一个问题,ANSI C 提供一个 feof函数,用来判断文件是否结束。feof 函数既可用以判断二进制文件又可用以判断文本文件。
#include<stdio.h>
int feof(FILE* stream);
参数:stream:文件指针
返回值:非0:已经到文件结尾;0:没有到文件结尾
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void main()
{
//以写的模式打开文件
FILE* fp1 = fopen("D:/c.txt", "w");
int ch = 'h';
//以字符的形式写入文件
fputc(ch, fp1);
//关闭文件
fclose(fp1);
//以读的模式打开文件
FILE* fp2 = fopen("D:/c.txt", "r");
if (!fp2)
{
return;
}
//feof:判断是否到了文件结尾
while (!feof(fp2))
{
//以字符的形式读文件,文件在读取时光标流会自动向下移动
char c = fgetc(fp2);
printf("%c", c);//h
}
//关闭文件
fclose(fp2);
}
4.按照行读写文件
(1)写文件
#include<stdio.h>
int fputs(const char* str,FILE* stream);
参数:str:字符串
stream:文件指针
返回值:成功:0;失败:-1
(2)读文件
#include<stdio.h>
int fgets(char* str,int size,FILE* stream);
参数:str:字符串
size:指定最大读取字符串的长度
stream:文件指针
返回值:成功:成功读取到的字符串;读到文件尾或者出错:NULL
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string>
void main()
{
FILE* fp1 = fopen("D:/c.txt", "w");
//const char* ch = "hello world\n !";
char ch[] = "hello world\n !";
fputs(ch, fp1);//一共两行,即fputs不会自动在结尾加一个\n,字符串结束符\0不会写入文件
fclose(fp1);
FILE* fp2 = fopen("D:/c.txt", "r");
if (!fp2)
{
return;
}
char* data=(char*)malloc(sizeof(char)*20);
while (!feof(fp2))
{
fgets(data, 20, fp2);//会自动加上\0作为字符串结束
printf("%s", data);
}
fclose(fp2);
free(data);
}
//输出结果:
//hello world
// !
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string>
#include <time.h>
void main()
{
srand((size_t)time(NULL));
int a, b, c;
char* p = (char*)malloc(sizeof(char) * 20);
FILE* fp = fopen("D:/加减乘除.txt", "w");
for (size_t i = 0; i < 10; i++)
{
a = rand() % 10 + 1;//1~10的数
b = rand() % 10 + 1;
c = rand() % 4;//0~3的数
memset(p, 0, 20);
switch (c)
{
case 0:
sprintf(p, "%d+%d=\n", a, b);//按格式化将数据写入字符串
break;
case 1:
sprintf(p, "%d-%d=\n", a, b);
break;
case 2:
sprintf(p, "%d*%d=\n", a, b);
break;
case 3:
sprintf(p, "%d/%d=\n", a, b);
break;
default:
break;
}
fputs(p, fp);
}
fclose(fp);
free(p);
}
//加减乘除.txt文件内容:(11行)
8*10=
9*6=
2+7=
7+10=
10/2=
9/5=
7/6=
10*6=
6*8=
9/6=
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string>
#include <time.h>
void main()
{
FILE* fp1 = fopen("D:/加减乘除.txt", "r");
if (!fp1)
{
return;
}
FILE* fp2 = fopen("D:/加减乘除结果.txt", "w");
char* p = (char*)malloc(sizeof(char) * 20);
int a, b, value;
char c;
while (!feof(fp1))
{
memset(p, 0, 20);
a = 0; b = 0; c = ' ';
fgets(p, 20, fp1);
sscanf(p, "%d%c%d=", &a, &c, &b);//按格式化读字符串中的数据
if (a == 0)//除掉最后一行
{
break;
}
switch (c)
{
case '+':value = a + b;
break;
case '-':value = a - b;
break;
case '*':value = a * b;
break;
case '/':value = a / b;
break;
default:
break;
}
sprintf(p, "%d%c%d=%d\n", a, c, b, value);
fputs(p, fp2);
}
free(p);
fclose(fp1);
fclose(fp2);
}
//加减乘除结果.txt文本内容:(11行)
8*10=80
9*6=54
2+7=9
7+10=17
10/2=5
9/5=1
7/6=1
10*6=60
6*8=48
9/6=1
5.按照格式化读写文件
类似于上面例子中的sscanf与sprintf.
(1)写文件
#include<stdio.h>
int fprintf(FILE* stream,const char* format,...);
根据参数format字符串来转化并格式化数据,然后将结果输出到stream指定的文件中
参数:stream:文件指针
format:字符串格式,格式和printf()一样
返回值:成功:实际写入文件的字符个数;失败:-1
(2)读文件
#include<stdio.h>
int fscanf(FILE* stream,const char* format,...);
从stream指定的文件读取字符串,并根据参数format字符串来转换并格式化数据,遇到空格,换行结束
参数:stream:文件指针
format:字符串格式,格式和scanf()一样
返回值:成功:成功转换的值的个数;失败:-1
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void main()
{
FILE* fp1 = fopen("D:/info.txt", "w");
char name[] = "Lisa";
int age = 25;
fprintf(fp1, "%s's age is %d,\0 she is a girl", name, age);
fclose(fp1);
}
//info.txt文本内容:
Lisa's age is 25,
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void main()
{
FILE* fp1 = fopen("D:/info.txt", "w");
char name[] = "Lisa";
int age = 25;
fprintf(fp1, "%s's age is %d, she is a\ngirl", name, age);
fclose(fp1);
}
//info.txt文本内容:
Lisa's age is 25, she is a
girl
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void main()
{
//info.txt文本内容:2020 hello world
FILE* fp = fopen("D:/info.txt", "r");
if (!fp)
{
return;
}
int a = 0;
char* info = (char*)malloc(sizeof(char) * 50);
memset(info, 0, 50);
fscanf(fp, "%d%s", &a, info);
printf("%d,%s",a, info);//2020,hello
fscanf(fp, "%s", info);
printf("%s", info);//world
fclose(fp);
free(info);
}
6.按照块读写二进制文件
(1)写文件
#include<stdio.h>
int fwrite(const void* ptr,size_t size,size_t nmemb,FILE* stream);
参数:ptr:准备写入文件数据的地址
size:写入文件内容的块数据大小
nmemb:写入文件的块数
stream:文件指针
(写入文件的数据总大小:size*nmemb)
返回值:成功:实际写入文件的块个数,即nmemb;失败:0
(2)读文件
#include<stdio.h>
int fread( void* ptr,size_t _ElementSize,size_t _ElementCount,FILE* _stream);
参数:ptr:存储读到的文件数据的地址
_ElementSize:块数据大小
_ElementCount:读取的块个数
_stream:文件指针
返回值:成功:实际成功读取到的内容块数,如果此值比_ElementCount小并且大于0,说明读到文件的结尾;失败:0
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
FILE* fp = fopen("D:/块数据.txt", "w");
fwrite(arr, sizeof(int), 10, fp);
// fwrite(arr, 20, 2, fp);//正确,只要数据大小是40即可
fclose(fp);
}
结果:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string>
void main()
{
FILE* fp = fopen("D:/块数据.txt", "r");
if (!fp)
{
return;
}
int arr[10] = { 0 };
fread(&arr, 4, 10, fp);
for (size_t i = 0; i < 10; i++)
{
printf("%d", arr[i]);//12345678910
}
fclose(fp);
}
存储结构体:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct student {
std::string name;
int age;
std::string address;
};
void main()
{
struct student stu[3] = {
{"张三",20,"北京昌平"},
{"李四",22,"北京朝阳"},
{"王五",19,"北京海淀"}
};
FILE* fp = fopen("D:/存储结构体.txt", "w");
for (size_t i = 0; i < 3; i++)
{
fwrite(&stu[i], sizeof(struct student), 1, fp);
}
fclose(fp);
}
读取结构体:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct student {
char name[21];
int age;
char address[51];
};
void main()
{
FILE* fp = fopen("D:/存储结构体.txt", "r");
if (!fp)
{
return;
}
struct student* stu=(struct student*)malloc(sizeof(struct student)*3);
for (size_t i = 0; i < 3; i++)
{
fread(&stu[i], sizeof(struct student), 1, fp);
printf("%s\n", stu[i].name);
printf("%d\n", stu[i].age);
printf("%s\n", stu[i].address);
}
fclose(fp);
free(stu);
}