前言
本文记录学习c语言文件操作的过程。
一、文件
什么是文件?
广义:文件就是存储在外设中或由各种外设产生的数据集合
狭义:以外存为载体的描述某种事物的数据集合(普通文件)
广义分类:
- 普通文件:如:图片、MP3、视频
- 设备文件:关联某种外设,对该文件的操作就是对该外设的操作。
- 目录文件:(文件夹)
二、普通文件
普通文件按数据组织形式分为:
- 二进制文件:数据的原始二进制位进行组织
- 文本文件:将所有数据转换成可阅读可打印的字符进行存放
文件内容的本质都是二进制。
1.位置指示器
文件内容有多有少,程序读写文件往往都是一小块一小块地逐步读写,文件中每个字节都有一个编号,被称为字节编号或字节位置(不称地址)。
位置指示器:保存当前读写起始位置的一个整型空间。
2.文件的打开与关闭
文件打开函数: fopen
FILE *fopen(const char *__restrict__ _Filename, const char *__restrict__ _Mode)
参数:
_Filename:带路径的文件名
_Mode:文件的打开模式,见下表
mode字串 | 打开方式 | 文件不存在 | 文件存在 | 位置指示器 |
---|---|---|---|---|
“r” | 只读 | 失败 | 打开成功,旧内容不变,只读 | 0 |
“r+” | 读方式打开,可写 | 失败 | 打开成功,旧内容不变,可读可写 | 0 |
“w” | 只覆盖写 | 创建新文件 | 打开成功,清空旧内容,只写 | 0 |
“w+” | 覆盖写打开,可读 | 创建新文件 | 打开成功,清空旧内容,可读可写 | 0 |
“a” | 追加写 | 创建新文件 | 打开成功,旧内容不变,文件尾追加新内容 | 文件尾 |
“a+” | 追加写打开,可读 | 创建新文件 | 打开成功,旧内容不变,文件尾追加新内容并可读 | 开始为0,未写之前随读自动增加,一旦有写操作则到文件尾。 |
除此以外,“rb”、“rb+”、“wb”、“wb+”、“ab”、“ab+”意味着操作二进制文件。
FILE是一个C库定义好的结构体名,返回值为FILE类型的指针,且位于堆区,意味着fopen函数实现中包含malloc动态分配,因此必须有对应的函数来释放内存。
文件关闭函数:fclose
int fclose(FILE *fp)
关闭成功返回 0 ,失败返回 -1 。文件未关闭会造成:
- 内存泄漏
- 数据丢失
文件操作示例:
#include <stdio.h>
int main ()
{
FILE * pFile;
//打开文件
pFile = fopen("myfile.txt","w");//以输出的形式(写)打开文件
//文件操作
if (pFile != NULL) //如果文件打开成功
{
fputs ("fopen example",pFile);//以字符串的形式写入
//关闭文件
fclose(pFile);
}
return 0;
}
3.位置指示器函数
long ftell(FILE *_File) // 返回文件指针相对于起始位置的偏移量。
int fseek(FILE *_File, long _Offset, int _Origin) // 位置指示器定位函数
参数:
_File:FILE类型地址
_Offset:位置指示器相对于_Origin偏移量
_Origin:可填下面三种
#define SEEK_CUR 1 位置指示器的当前位置
#define SEEK_END 2 文件尾,_Offset >= 0
#define SEEK_SET 0 文件头,_Offset <= 0
三、文本文件的读写
1.单个字符的读写
int fgetc(FILE *_File) // 读取单个字符,成功返回字符ASCII码,失败返回-1
int fputc(int _Ch, FILE *_File) // 写单个字符,成功返回字符ASCII码,失败返回-1
2.字符串读写
/* 读字符串函数
功能:功能是从 stream 流中读取 size 个字符存储到字符指针变量 s 所指向的内存空间。
它的返回值是一个指针,指向字符串中第一个字符的地址。
参数:
_Buf: 代表要保存到的内存空间的首地址,可以是字符数组名,
也可以是指向字符数组的字符指针变量名。
_MaxCount: 代表的是读取字符串的长度。
_File: 表示从何种流中读取,可以是标准输入流 stdin,
也可以是文件流,即从某个文件中读取,
标准输入流就是前面讲的输入缓冲区。所以如果是从键盘读取数据的话
就是从输入缓冲区(标准输入流 stdin)中读取数据,所以第三个参数为 stdin。
*/
char * fgets(char *__restrict__ _Buf, int _MaxCount, FILE *__restrict__ _File)
注意:
1.fgets函数一次最多读一行,其次读size - 1个字符。读取一行字符结束后,会在最后写入一个字符串结束符null。
2.换行符‘ \n ’一起读。
// 写字符串,成功返回写入字符个数,失败返回-1
int fputs(const char *__restrict__ _Str, FILE *__restrict__ _File)
示例:读文件(一行)函数
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *MyReadLine(FILE *fp);
void main()
{
FILE *fp = NULL;
char *p = NULL;
fp = fopen("test.txt", "r");
if (fp == NULL)
return 1;
p = MyReadLine(fp);
while (p != NULL)
{
printf("%s", p);
free(p);
p = MyReadLine(fp);
}
fclose(fp);
system("pause");
return 0;
}
char *MyReadLine(FILE *fp)
{
int cur = 0;
int count = 0;
int ch = 0;
char *pout = NULL;
char *pret = NULL;
if (fp == NULL)
{
printf("Error input");
return NULL;
}
cur = ftell(fp);
ch = fgetc(fp);
while(ch != '\n' && ch >= 0)
{
count++;
ch = fgetc(fp);
}
count += 2;
pout = (char *)malloc(count);
memset(pout, 0, count);
fseek(fp, cur, SEEK_SET);
pret = fgets(pout, count, fp);
if (pret == NULL)
{
free(pout);
return NULL;
}
else
{
return pout;
}
}
3.格式化读写
格式化读/写函数 fscanf()和 fprintf(),格式化读/写函数与标准的格式输入/输出函数功能相同,只不过它们的读/写对象不是键盘和显示器,而是文件。fscanf()和 fprintf()函数只适用于 ASCII 码文件的读/写。
// 与printf用法类似,printf往命令行写内容,fprintf往文本文件写
int fprintf(FILE *__restrict__ _File, const char *__restrict__ _Format, ...)
// 类似,从文本文件扫描输入
int fscanf(FILE *__stream, const char *__format, ...)
示例:从文件中读内容,进行处理后存储到另一个文件中。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void main()
{
FILE *fp = NULL;
char name[20] = "";
int age = 0;
float weight = 0;
float heigh = 0;
fp = fopen("test1.txt", "r"); // 文件内容:zhangsan 21 78.4 178.8
if (fp == NULL)
{
printf("fopen failed in %s at &d\n", __FILE__, __LINE__);
return 1;
}
fscanf(fp, "%s%d%f%f", name, &age, &weight, &heigh); // 注意取地址符
fclose(fp);
age++;
weight -= 2;
heigh += 0.5;
fp = fopen("test11.txt", "w");
if (fp == NULL)
{
printf("fopen failed in %s at &d\n", __FILE__, __LINE__);
return 1;
}
fprintf(fp, "%s&%d&%.1f&%.1f", name, age, weight, heigh);
fclose(fp);
// system("pause");
return 0;
}
四、二进制文件的读写
/* 读二进制文件函数
参数:
_DstBuf: 代表要读到的内存空间的首地址
_ElementSize: 代表每个元素占多少字节
_Count: 期望读取的元素个数
_File: 要读取的文件
返回值:
返回实际读取的元素个数。
*/
size_t fread(void *__restrict__ _DstBuf, size_t _ElementSize, size_t _Count,
FILE *__restrict__ _File)
/* 写二进制文件函数
参数:
_Str: 代表要写的内容的首地址
_Size: 代表每个元素占多少字节
_Count: 期望写入的元素个数
_File: 要写入的文件
返回值:
返回写入成功的元素个数。
*/
size_t fwrite(const void *__restrict__ _Str, size_t _Size, size_t _Count,
FILE *__restrict__ _File)
示例:bmp文件图片宽在0x12,高在0x16,各占4字节,查询bmp图片的高和宽。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void main()
{
FILE *fp = NULL;
int w = 0;
int h = 0;
fp = fopen("t1.bmp", "rb");
if (fp == NULL)
return 1;
fseek(fp, 18, SEEK_SET);
fread(&w, sizeof(w), 1, fp); // 读完之后位置指示器移动到四字节之后
fread(&h, sizeof(h), 1, fp);
fclose(fp);
printf("The width of BMP is %d\n", w);
printf("The heigh of BMP is %d\n", h);
system("pause");
return 0;
}