ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

C/C++文件操作干货

C/C++文件操作干货

为什么使用文件

程序运行结束后会释放内存,不会保留其中产生的数据,而文件可以把数据保存下来。


文件的类型

  • 从功能上分类:分为程序文件和数据文件.程序文件存储可以运行的程序,比如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指针内存放fd和用户级缓存区等内容,fd是操作系统的文件的用户入口

举个例子:

#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在遇到以下情况时会终止读取

  1. 遇到换行符\n(即读取到一行的末尾),并且他会把换行符也读取到

  2. 读取了n-1个字符

  3. 遇到文件结束(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

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,只不过它们封装了输入/输出流,所以不用入FILE流指针。在此举一个例子:

#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++ 中文件 IO 操作详解_c++文件io-CSDN博客

返回列表