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类型的结构体变量,这个结构体储存了文件相关信息.当然,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 在遇到以下情况时会终止读取

  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

  • 功能:以二进制方式把数据写入文件
  • 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++ 中文件 IO 操作详解_c++文件io-优快云博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值