为什么使用文件
程序运行结束后会释放内存,不会保留其中产生的数据,而文件可以把数据保存下来.
文件的类型
- 从功能上分类:分为程序文件和数据文件.程序文件存储可以运行的程序,比如exe文件;数据文件储存程序运行时需要用到的数据。
- 从储存方式上分类:分为二进制文件和文本文件.不同储存方式的文件读取时需要不同类型的读取器.
需要注意的是:
文本文件不是字面意思上储存的是文本内容,而是将其ASCLL储存下来,而其ASCLL码仍要转换成二进制。所以无论二进制文件还是文本文件存储的都是二进制,只是由于存储时发生的转换不同,可能会导致同样的数据以这两种方式存储得到不同的二进制。
比如要存123这个数字:
存储到文本文件: 123——>49 50 51(实际上就是字符'1' '2' '3'的ASCLL码)——>0011 0001 0011 0010 0011 0011(分别是49,50,51的二进制代码,也是文件中真实存储的数据)
存储到二进制文件: 123——>0111 1011(这是123的二进制,也是文件中真实存储的数据)
文件名(文件标识)的组成

C语言文件操作
打开文件
在对文件读写之前需要把文件打开。而c语言中打开文件主要用到的是fopen函数:
![]()
- filename:要打开的文件的文件名
- mode:文件使用方式
- 如果打开成功,返回一个FILE类型的指针;如果打开失败,返回NULL。
mode可能的取值如下:

要注意的是,当使用方式是w,wb,w+,wb+中的任意一种时,文件原有内容会被清除。
关于FILE指针:
一但你试图在程序中使用某个文件,都会在内存中开辟一个文件信息区,其实是在内存上创建了一个名叫FILE类型的结构体变量,这个结构体储存了文件相关信息.当然,FILE这一结构体类型是系统提前创建的. 而文件指针就是指向这一结构体变量的指针.在进行文件操作时,这个FILE指针就代表了文件流。
举个例子:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
FILE* p = fopen("c.txt","w+");
if (p)
{
printf("打开成功\n");
}
}
在上述例子中我尝试以“w+”的方式打开名为“c.txt”的文件,如果这个文件不存在就新建一个。
注意:
如果filename仅仅是一个文件名的话,那就表示在默认目录下查找。除此之外,filename还可以是一条路径(绝对路径/相对路径),可以指定打开哪个位置的某文件。比如filename可以是“.../c.txt”或者“c:\yyy\xxx\c.txt”。
在程序中顺序读写文件
我们对文件的读写是通过函数来进行的,下面是一些可以支持我们顺序读写的函数:

所谓所有输出(入)流可以浅薄的理解为你可以在显示器上输出(输入),也可以在文件中输出,当然,只有文件就表示只能输出(输入)到文件中.
接下来对这些函数进行一一介绍:
fgetc
- 功能:从输入流中读取一个字符(包括换行,空格等字符)
- stream:目标输入流
- 返回值:返回这个字母的ASCLL码,失败返回EOF
eg:
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> int main() { FILE* p = fopen("c.txt","r+");//c.txt中只有1个字符 if (!p) { printf("打开失败\n"); return -1; } printf("%c\n",fgetc(p)); printf("%d", fgetc(p)); return 0; }
fputc
- 功能:在输出流中输出一个字符
- character:要输出的字符的ASCLL码,实际上传字符就行,会自动转换
- stream:目标输出流
- 返回值:返回character
eg:
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> int main() { FILE* p = fopen("c.txt", "w"); if (!p) { printf("打开失败\n"); return -1; } fputc('a',p); return 0; }
fgets
- 功能:从一个输入流中读取一个字符串,并在字符串结尾添加‘\0’
- str:存放读取出来的字符串的位置
- num:表示最多读取num-1个字符(剩下一个位置给'\0')
- stream:目标输入流
- 返回值:读取成功返回str,读取失败或读取到文件结尾返回NULL
fgets在遇到以下情况时会终止读取:
遇到换行符
\n(即读取到一行的末尾),并且他会把换行符也读取到。读取了
n-1个字符。遇到文件结束(EOF)(即读取到文件末尾)。
eg:
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> int main() { FILE* p = fopen("c.txt", "r"); if (!p) { printf("打开失败\n"); return -1; } char arr[100]; fgets(arr,5,p); printf("%s",arr); return 0; }
fputs
- 功能:向一个输出流输出一个字符串(不会输出末尾的‘\0’)
- str:要输出的字符串的首地址
- stream:目标输出流
- 返回值:写入成功返回一个非负整数,失败返回EOF(-1)
eg:
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> int main() { FILE* p = fopen("c.txt", "w"); if (!p) { printf("打开失败\n"); return -1; } fputs("abcdefg",p); return 0; }
fscanf
只比scanf多出一个stream参数,与scanf基本一样
- 功能:从输入流中按照指定格式读取数据
- stream:目标输入流
- format:格式化字符串,指定如何解析输入数据
...:可变参数,用于存储读取到的数据(类似
scanf)返回值:表示成功匹配并赋值的参数个数,如果文件结束或者读取失败返回EOF
eg:
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> int main() { FILE* p = fopen("c.txt", "r"); if (!p) { printf("打开失败\n"); return -1; } int x = 1; char y = 'l'; int num = fscanf(p,"%d,%c",&x,&y); printf("%d\n", x); printf("%c\n", y); printf("%d\n", num); return 0; }
如上例,显然x格式化读取成功了,但是由于c.txt中的格式与format后面的格式(,%c)不匹配,所以没有读取成功,因为只匹配到一个,所以返回值是1。
fprintf
只比printf多出一个stream参数,与printf基本一样
- 功能:将数据按指定格式写入输出流
- stream:目标输出流
- format:格式化字符串,指定输出格式
...:可变参数,输出的数据源(类似 printf)
返回值:表示成功写入流的数据的字节数,如果写入失败返回EOF
eg:
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> int main() { FILE* p = fopen("c.txt", "w"); if (!p) { printf("打开失败\n"); return -1; } int x = 10; char y = 'y'; fprintf(p,"%d,%c",x, y); return 0; }
fread
- 功能:以二进制的方式读取文件中的数据
ptr:指向要存储读取数据的内存块的指针
size:每个数据项的字节大小
count:要读取的数据项数量
stream:文件指针,指向要读取的文件
返回值:读取成功时返回成功读取到的数据数量即count,读取失败或者读取到文件结尾时返回小于count的值。
eg:
见下。
fwrite
- 功能:以二进制方式把数据写入文件
ptr:指向要写入数据的内存地址(如数组、结构体等)
size:每个数据项的字节大小(通常用
sizeof计算)count:要写入的数据项数量
stream:文件指针(通过
fopen打开)返回值:写入成功时返回成功写入的数据数量即count,写入失败时返回小于count的值。
eg:
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> int main() { FILE* p = fopen("c.txt", "rb+"); if (!p) { printf("打开失败\n"); return -1; } int arr[5] = {1,5,2,3,7}; fwrite(arr,sizeof(arr[0]),5,p); fseek(p,0,SEEK_SET);//调整光标位置以便读取数据 int brr[5] = {0}; fread(brr,sizeof(int),5,p); for(int i = 0;i<5;i++) { printf("%d ",brr[i]); } return 0; }
sscanf和sprintf
- 功能:对s指向的字符串进行格式化的读取。
- 返回值:返回成功匹配并赋值的参数个数,如果到达字符串末尾而未匹配,返回
EOF。
- 功能:把格式化的数据转换为字符串并存放到str指向的内存块中。
- 返回值:成功时返回写入的字符数(不包括结尾的 null 字符),失败时返回负值。
由于这两个函数及其类似于printf和scanf以及fprintf,fscanf,所以不过多介绍,在此举一个例子:
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> int main() { int a = 0; char b = 0; sscanf("ab1csd","ab%dc%cd",&a,&b); char* ptr[20] = {0}; sprintf(ptr,"a的值是:%d,b的值是:%c",a,b); printf("%s",ptr); return 0; }
在程序中逆序读写文件

光标也叫文件指针,打开文件时,光标会在文件的开始位置。
所谓顺序读写就是输入(输出)时,光标会随着输入(输出)的动作移动,读写完一个数据,光标就移动向下一个数据,按顺序全部读写(然而,文件一旦关闭,重开时光标会移动到起始位)。所谓逆序读写就是在读写文件时在合理范围内改变光标位置,想要读写哪儿就移动到哪儿去。这也是通过函数来控制。
下面是文件光标控制函数:
fseek
- 功能:调整光标位置
- stream:要调整光标位置的目标流
- origin:规定偏移量为0的地方(偏移起始位置),有三种选择:SEEK_SET(文件开头),SEEK_CUR(目前光标的位置),SEEK_END(文件末尾)
- offset:表示要移动到举例origin位置偏移量为offset的地方
- 返回值:成功返回0,失败返回非0
eg:
fseek(stream,-8,SEEK_CUR)就是把光标从当前位置向前移动8位。
fseek(stream,2,SEEK_SET)就是把光标移动到从文件开始位置向后2位的地方。
ftell
- 功能:返回目前光标位置相对于起始位置的偏移量
- stream:要查询的目标流
- 返回值:返回目前光标位置相对于起始位置的偏移量
rewind
- 功能:直接将光标移动到文件起始位置
- stream:要调整光标位置的目标流
- 返回值:无
文件读取结果的判定函数
用于判断文件流是否到达文件结尾。是返回非0值,否返回0
用于判断文件流是否发生错误。是返回非0值,否返回0
关闭文件
关闭文件使用C语言库函数:
![]()
关闭成功返回0,失败返回-1.
后记
c程序启动时,默认打开三个标准流(其实就是打开若干文件):
stdin-标准输入流(大多数环境是从键盘输入)
stdout-标准输出流(大多数环境是输出到显示器)
stderr-标准错误流(大多数环境是输出到显示器)
我们平时用到的scanf函数实际上就使用了stdin,而printf使用了stdout,perror使用了stderr。
C++文件操作干货
C++中的文件操作相对于C语言来说是比较简单的,因为其对复杂的过程做了封装。
标准库提供了三种主要的文件流类来处理文件IO操作:
ofstream:用于写入文件(output file stream)ifstream:用于读取文件(input file stream)fstream:用于读写文件(file stream)
打开文件
每个流类都提供了open()方法;也可以在构造函数中直接指定文件名和打开模式,这时文件会直接被打开:
#define _CRT_SECURE_NO_WARNINGS
#include<fstream>
using namespace std;
int main()
{
//使用构造函数打开文件
std::ofstream outFile("example.txt", std::ios::out);//可以省略std::ios::out,因为默认就是这样
std::ifstream inFile("example.txt", std::ios::in);//可以省略std::ios::in,因为默认就是这样
std::fstream ioFile("example.txt", std::ios::in | std::ios::out);//可以省略std::ios::in | std::ios::out,
//因为默认就是这样
//使用open函数打开文件
ofstream outFile2;
outFile2.open("example.txt", std::ios::out);//std::ios::out可以省略,因为默认就是这样
//......
}
文件的打开模式有如下几种,多个打开模式使用“|”连接:
std::ios::in:读取模式,文件必须已存在,否则打开失败std::ios::out:写入模式,如果文件存在,截断文件(清空内容)如果文件不存在,创建新文件std::ios::app:追加模式,如果文件不存在,创建新文件std::ios::ate:打开后定位到文件末尾std::ios::trunc:打开时截断文件(删除原有内容)std::ios::binary:二进制模式
检查文件是否打开
//通过类成员函数is_open()判断
if (!outFile.is_open()) {
std::cerr << "无法打开文件!" << std::endl;
return 1;
}
//或者更简洁的方式
if (!outFile) {
std::cerr << "文件打开失败!" << std::endl;
return 1;
}
通过对象实现文件读写
ofstream对象实现文件写入
#define _CRT_SECURE_NO_WARNINGS
#include<fstream>
#include<iostream>
#include<string>
int main()
{
//使用构造函数打开文件
std::ofstream outFile("example.txt", std::ios::out);
if (!outFile)
{
std::cout << "文件打开失败" << std::endl;
}
//写入数值类型数据
outFile << 1 <<5.55<< std::endl;
//写入字符串数据
outFile << "abcd" << std::endl;
//写入自定义类型数据
std::string mystring = "i am a string";
outFile << mystring << std::endl;
}

ifstream对象实现文件读取
#define _CRT_SECURE_NO_WARNINGS
#include<fstream>
#include<iostream>
#include<string>
int main()
{
//使用构造函数打开文件
std::ifstream inFile("example.txt", std::ios::in);
if (!inFile)
{
std::cout << "文件打开失败" << std::endl;
}
//读入数值类型数据
double x = 0;
inFile >> x;
std::cout << x <<std::endl;
//读入字符串数据
char arr[20] = {0};
inFile >> arr;
std::cout << arr << std::endl;
//读入自定义类型数据
std::string mystring;
inFile >> mystring;
std::cout << mystring;
}

#include <fstream>
#include <iostream>
#include <string>
int main() {
// 打开文件
std::ifstream inFile("example.txt");
if (inFile.is_open()) {
// 逐行读取
std::string line;
while (std::getline(inFile, line)) {
std::cout << line << std::endl;
}
}
else {
std::cerr << "无法打开文件进行读取!" << std::endl;
}
// 基础版本(使用默认换行符分隔)
//std::istream& getline(std::istream & is, std::string & str);
// 可指定分隔符版本
//std::istream& getline(std::istream & is, std::string & str, char delim);
return 0;
}

fstream对象实现文件读写
#include <fstream>
#include <iostream>
#include <string>
int main() {
// 打开文件进行读写,不截断
std::fstream ioFile("example.txt", std::ios::in | std::ios::out);
if (ioFile.is_open()) {
// 读取现有内容
std::string content;
std::getline(ioFile, content);
std::cout << "读取的内容: " << content << std::endl;
// 定位到文件末尾进行追加
ioFile.seekp(0, std::ios::end);
ioFile << "这是追加的内容" << std::endl;
// 定位到文件开头重新读取
ioFile.seekg(0, std::ios::beg);
while (std::getline(ioFile, content)) {
std::cout << content << std::endl;
}
ioFile.close();
}
return 0;
}

二进制读写
二进制读写使用的是成员函数write和read,而不再是>>或者<<运算符重载。
#include <fstream>
#include <iostream>
//ostream& write(const char* s, streamsize count);//写二进制数据
//istream& read(char* s, streamsize count);//读二进制数据
int main() {
// 写入二进制数据
{
std::ofstream outFile("binary.bin", std::ios::binary);//这里不用|std::ios::out,因为无论
//传入什么都会自动|上std::ios::out
int numbers[] = { 1, 2, 3, 4, 5 };
outFile.write(reinterpret_cast<char*>(numbers), sizeof(numbers));
}
// 读取二进制数据
{
std::ifstream inFile("binary.bin", std::ios::binary);
int readNumbers[5];
inFile.read(reinterpret_cast<char*>(readNumbers), sizeof(readNumbers));
for (int i = 0; i < 5; ++i) {
std::cout << readNumbers[i] << " ";
}
std::cout << std::endl;
}
return 0;
}

文件光标定位成员函数
tellg(): 是 "tell get" 的缩写,用于返回输入文件指针的当前位置(对于ifstream等输入流)。
tellp(): 是 "tell put" 的缩写,用于返回输出文件指针的当前位置(对于ofstream等输出流)。
seekg():是 "seek get" 的缩写,用于设置输入文件指针的位置(对于ifstream等输入流)。
seekp(): 是 "seek put" 的缩写,用于设置输出文件指针的位置(对于ofstream等输出流)。
eg:
// 移动到绝对位置
file.seekg(offset); // 从文件开头偏移offset字节
// 移动到相对位置
file.seekg(offset, std::ios_base::beg); // 从开头偏移
file.seekg(offset, std::ios_base::cur); // 从当前位置偏移
file.seekg(offset, std::ios_base::end); // 从结尾偏移
// 移动到绝对位置
file.seekp(offset);
// 移动到相对位置
file.seekp(offset, std::ios_base::beg);
file.seekp(offset, std::ios_base::cur);
file.seekp(offset, std::ios_base::end);
值得一提的是tellg和tellp的返回值以及seekp和seekg的第一个参数的类型不是整型而是一个自定义类型:std::streampos,但它可以用整型去构造。
状态检查成员函数
good():检查流是否正常
eof():检查是否到达文件末尾
fail():检查是否发生非致命错误
bad():检查是否发生致命错误
clear():清除错误标志
文件关闭
使用文件流类的close函数,例如inFile.close()。
本文详细介绍了C语言中文件的使用,包括为何使用文件、文件类型(程序文件、数据文件、文本与二进制)、文件名结构、标准流和文件指针、文件的打开与关闭、顺序读写与逆序操作,以及判定文件读取结束的方法。同时讨论了顺序表和链表在数据存储中的优缺点。









1231

被折叠的 条评论
为什么被折叠?



