【C++】标准库 - 文件的读写 i/ofstream

简介: 本文章介绍 C++ 标准库中处理文件读写的 fstream ,以及其中的一些使用

一、概述


网上关于使用 C++ 读写文件的内容不是所期待的,所以来写一下。

无论是读文件还是写文件,首先需要打开文件,需要使用两个类

  • ifstream (Input File stream) 用于读取
  • ofstream (Output File stream) 用于写入

这两个类继承自 std::io_base 用于处理 io 流。

需要包含头文件

#include <fstream>

二、打开文件

2.1 - ifstream 打开文件


两种打开文件的方式,

  • 使用成员函数 open,有两个参数:

    1. 待打开的文件名
    2. 文件打开的模式
      ifs.open("file.extension", openMode);
      
  • 不使用成员函数 open ,在构造时直接指定文件名与打开模式

    ifstream ifs("file.extension", openMode);
    

指定文件路径有多种方式

  • 绝对路径 (完整路径,从硬盘的根目录出发),大概为以下格式:
    // Windows 下
    "C:/Documents and Settings/User/Desktop/file.txt"
    // Linux 下
    "/home/user/file.txt"
    
  • 相对路径 (从可执行文件 / exe 文件开始的路径),如果文件位于可执行文件下子目录 subdirectory 下则为
    "subdirectory/file.txt"
    
  • 如文件位于可执行文件所在的目录,则需要直接指定文件名即可

打开模式也来自 ios_base 类,需要指定命名空间 ios_base::ios:: ,由于我们需要使用读取的模式所以为 ios::in 表示 input,输入。

打开后,需要确定是否打开,if (ifs)

操作结束后,需要关闭,使用成员函数 close,ifs.close()

代码示例

#include <iostream>
#include <fstream>

using namespace std;

int main(int argc, char* argv[])
{
   
    // 以读取方式打开
    ifstream ifs("test.txt", ios::in);
    if (ifs) // 确保文件是否打开成功
    {
   
        // 语句
        ifs.close(); // 关闭文件
    }
    else // 否则
        cerr << "Unable to open the file !" << endl;

    return 0;
}

当我们使用 ifstream 打开文件时,打开模式 ios::in 为可选内容,此选项为默认选项。

2.2 - ofstream 打开文件


使用 ofstream 以写入的方式打开文件,同样有两种方式,一种直接声明,一种使用函数 open ,与 ifstream 相同。

对于写入,文件的打开方式有很多。

文件打开方式 速记 说明
ios::out output (输出) 指定文件的打开方式为写入,一般为必要参数,但在使用 ofstream 对象时,为默认参数
ios::app append (添加) 当打开文件用于写入时,会在已有数据之后写入,不覆盖原有内容。使用此种打开方式,每次写入都会置于文件末尾,即便之前更改了位置。
ios::trunc truncate (截断) 当打开文件时,如果文件存在内容则清空文件内容
ios::ate at end (在结束处) 以写入方式打开文件,并将文件指针置于末尾。与 ios::app 不同之处为,如果改变了指针位置,写入不一定会在文件末尾

对于这些写入文件的打开方式,如果文件不存在则会创建。

用于指定多个打开模式,使用操作符或 | 。英文发音为 pipe 。

代码示例:

#include <iostream>
#include <fstream>

using namespace std;

int main(int argc, char* argv[])
{
   
    ofstream ofs("test.txt", ios::out | ios::trunc); // 声明流与打开方式
    if (ofs) // 文件是否打开 
    {
   
        // 操作语句
        ofs.close(); // 关闭文件
    }
    else // 否则
        cerr << "Error to open file !" << endl;

    return 0;
}

2.3 - fstream 以读取和写入的方式打开

使用 fstream 可以以读写一起的方式打开文件,工作原理与 ifstream 和 ofstream 相同。

原型为

fstream flux("file.extension", ios::in|ios::out | [ios::trunc|ios::ate]);

中括号表示,需要使用两者中之一的打开方式

  • 这是唯一的办法用以读取和写入的方式打开文件
  • 中括号在实际代码中是不需要写的
  • ios::app 是不可以选择的打开方式,此种文件打开方式,只允许在写入时使用,而此处 fstream 用于读取和写入

使用此种打开方式,文件必须存在!ios::in|ios::out 必须指定,由于文件已经存在,打开时需要指定 ios::ate 用于保留原始内容,或指定 ios::trunc 用于清空内容。

ifsteam, ofstream, fstream 都支持多种字符串格式

// const char *
explicit basic_fstream(const char* _Filename, ios_base::openmode _Mode = ios_base::in | ios_base::out, int _Prot = (int) ios_base::_Openprot);

// const string &
explicit basic_fstream(const string& _Filename, ios_base::openmode _Mode = ios_base::in | ios_base::out, int _Prot = (int) ios_base::_Openprot);

// const wchar_t *
explicit basic_fstream(const wchar_t* _Filename, ios_base::openmode _Mode = ios_base::in | ios_base::out, int _Prot = (int) ios_base::_Openprot);

// const wstring &
explicit basic_fstream(const wstring& _Filename, ios_base::openmode _Mode = ios_base::in | ios_base::out, int _Prot = (int) ios_base::_Openprot);

//....

ios_base::_Openprot 为打开保护

#include <fstream>
#include <string>
using namespace std;

int main(int argc, char* argv[])
{
   
    string my_file = "text.txt";
    fstream fs(my_file.c_str(), ios::in);
    if (fs) // 如果打开文件成功
    {
   
        // 操作语句
        fs.close(); // 关闭文件
    }
    else // 否则
        cerr << "Error to open file !" << endl;

    return 0;
}

三、读取和写入文本文件

3.1 - 读取文件内容


对于文件的读取,有多种不同的方法,如下

  • getline(stream, 字符串) :用于读取完整一行内容
  • stream.get(字符) :用于读取一个字符
  • stream >> 变量 :用于从文件中获取内容知道遇到分隔符 (空格,换行,...)

3.1.1 - getline


getline 需要两个参数,使用 ifstream 或 fstream 创建的流,和 用于存储内容的“目标”,通常为一个字符串,需要包含头文件 <string>

示例:

#include <iostream>
#include <string>
#include <fstream>

using namespace std;
int main(int argc, char* argv[])
{
   
    ifstream ifs("test.txt", ios::in); // 以读取模式打开
    if (ifs)
    {
   
        string context;
        getline(ifs, context); 
        cout << context; // 显示行

        ifs.close();
    }
    else
        cerr << "Unable to open the file !" << endl;

    return 0;
}

为了读取完整的文件,需要一行一行的读取,需要一个重复的循环,getline 会读取到文件结束

if (ifs)
{
   
    string line;
    while (getline(ifs, line))
    {
   
        cout << line << endl;
    }
}

getline 有一个重载方法,第三个参数为结束字符,这个结束字符的默认值为换行符 \n ,因此,我们可以一行一行的读取内容。

getline 只有在使用 ifstream 和 fstream 时有效,在 ofstream 时不再可用。

3.1.2 - get


get 函数用于读取一个字符,当然在使用循环的情况下,也可以读取完整的文件。

语法为

stream.get(character);

这个方法读取文件中的一个字符,然后将其存储在 char 类型的 character 变量中。

示例

#include <iostream>
#include <fstream>

using namespace std;

int main(int argc, char* argv[])
{
   
    ifstream ifs("test.txt", ios::in);
    if (ifs)
    {
   
        char character; // 字符型变量用于存储读取的字符
        ifs.get(character);
        cout << character;
        ifs.close();
    }
    else
        cerr << "Unbale to open the file !" << endl;

    return 0;
}

为了读取完整的文件,循环与 getline 相同。

3.1.3 - >>


这个符号应该不陌生,应该见到过与 cin 一起使用的情况,对于文件的运行原理也一样。这个操作符从文件中读取内容,直到遇到分隔符如空格或者换行符等。

比如一个文件中有以下内容

12 345
test

SDZ

可见,文件中存在两个整数和两个字符串,若我们需要全部获取到,需要声明两个 int 和 两个 string

示例:

#include <iostream>
#include <string>
#include <fstream>

using namespace std;
int main(int argc, char* argv[])
{
   
    ifstream ifs("test.txt", ios::in);
    if (ifs)
    {
   
        int integer1, integer2;
        string string1, string2;

        ifs >> integer1 >> integer2 >> string1 >> string2;

        ifs.close();
    }
    else
        cerr << "Unable to open the file !" << endl;

    return 0;
}

3.2 - 写入文件


写入文件也有多种方式

  • stream << 需要写入的内容; 向文件中写入一个任意的元素 (string, int ...)
  • stream.put(字符); 向文件中写入单个字符

3.2.1 - <<


这个符号对于了解 C++ 的人来说不应算陌生,它被用于 cout ,对于文件功能与显示到标准输出基本一致。
此操作符允许向文件中写入字符,或字符串,或整数...

操作符的使用语法为

stream << element1 << element2 << ...;

如代码所示与 cout 的使用并无太多区别。代码示例:

#include <iostream>
#include <string>
#include <fstream>

using namespace std;

int main(int argc, char* argv[])
{
   
    ofstream ofs("test.txt", ios::out | ios::trunc);
    if (ofs)
    {
   
        string name = "Zhangsan";
        int age = 23;
        ofs << "Birthday: " << 25 << '/' << 6 << '/' << 1998 << endl;
        ofs << "Hello, " << name << ", you are " << age << " years old.";
        ofs.close();
    }
    else
        cerr << "Unable to open the file !" << endl;

    return 0;
}

我们向文件中写入 一些字符串,字符,一些整数和换行符。不需要显式声明类型,在使用时不用刻意区分,文本文件中内容如下:

Birthday: 25/6/1998
Hello, Zhangsan, you are 23 years old

3.2.2 - put


put 被较少使用,由于不像 << 一样,只接受一个字符
使用时,只需要替换上述写入部分的代码

if (ofs)
{
   
    char chr = 'X';
    ofs.put(chr);
    // 等价于 ofs.put('X');

    ofs.close();
}

四、文件中的位置


与 C 语言相似,也存在文件读写指针的概念,用于处理文件中的写入位置。

4.1 - 知悉指针当前的位置


为了知道我们当前在文件中所处的位置,需要知道打开的方式

如果使用 ifstream 打开,存在方法 tellg()

如果使用 ofstream 打开, 存在方法 tellp()

这两种方法,返回当前文件指针所在的位置,从文件开始的字节编号。

stream.tell[g | p];

4.2 - 移动指针位置


为了移动文件读写指针的位置,同样依赖文件的打开方式

如果使用 ifstream 打开

存在方法 seekg ,这个方法包含两个参数,第一个为字节的编号,第二个为从哪里开始计算这个编号,第二个参数可以为以下数值

  • ios::beg ,从文件开始处
  • ios::cur , 从当前文件读写指针处
  • ios::end , 从文件结尾处

默认值为 ios::beg

如果使用 ofstream 打开

方法几乎为同样的名称 seekp ,它的工作方式与 seekg 一致

stream.seek[g | p](10, ios::beg);

五、一些实用的方法


  • stream.eof() ,为了得知文件读写指针是否到达了文件尾;
  • stream.ignore(num, characterEnd) ,用于忽略 num 个字符,或忽略所有直到遇到指定的 characterEnd 字符;
  • stream.clear() ,用于将所有的标志位状态置为初始状态;
  • stream.fail() ,用于测试文件流的打开是否正常,可用于检测一个文件是否存在

标志位

英语为 flag ,即一个位,可以有两种值,0 或 1,对于 fstream 来讲,存在四个标志位,

  • goodbit
  • eofbit
  • failbit
  • badbit

5.1 - eof


这个方法返回一个布尔值,它会检查标志位 eofbit 是否为 true,即是否到达文件尾,否则为 false

eofbit 变为 true 的情况,要么是没有更多数据可以读取,要么是因为不能继续写入。

5.2 - ignore


这个方法需要两个参数,需要忽略的字符数量以及一个终止的字符。它将忽略掉第一个参数数量个字符,直到遇到第二个参数指定的字符。此种方法可以用作计算文件的行数。

但需要注意两点

  1. 用于计算行数,需要知道一行的字符数量
  2. 文件必须使用读取的模式打开

如果不提前知道文件的行数,也可以使用一种简便的方法,需要包含:

#inlcude <limits>
using namespace std;

在第一个参数处可以设置

numeric_limits<int>::max()

这个方法返回一个 int 最大能存储达的数值。一般为 2147483647

使用举例

stream.ignore(numeric_limits<int>::max(), '\n');

5.3 - clear


此方法用于重新置位 fstream 的四个标志位。

在文件读写指针到达文件尾时,eofbit 变为 true,如果此时需要回到文件首,如果不重新置位,则 eofbit 会一直处于 true 的状态,这时就会出问题

5.4 - fail


fail 方法用于实现 faibit 和 badbit 的检测,如果结果为 false,则表示文件流被正确创建,且打开成功。也保证了文件的存在

六、参考链接


目录
相关文章
|
2月前
|
算法 C++ 容器
C++标准库(速查)总结
C++标准库(速查)总结
79 6
|
2月前
|
存储 算法 C++
C++ STL 初探:打开标准模板库的大门
C++ STL 初探:打开标准模板库的大门
112 10
|
2月前
|
存储 程序员 C++
C++常用基础知识—STL库(2)
C++常用基础知识—STL库(2)
79 5
|
2月前
|
存储 自然语言处理 程序员
C++常用基础知识—STL库(1)
C++常用基础知识—STL库(1)
70 1
|
2月前
|
Linux C++
Linux c/c++文件的基本操作
在Linux环境下使用C/C++进行文件的基本操作,包括文件的创建、写入、读取、关闭以及文件描述符的定位。
24 0
Linux c/c++文件的基本操作
|
3月前
|
编译器 API C语言
超级好用的C++实用库之跨平台实用方法
超级好用的C++实用库之跨平台实用方法
44 6
|
3月前
|
安全 C++
超级好用的C++实用库之环形内存池
超级好用的C++实用库之环形内存池
65 5
|
3月前
|
缓存 网络协议 Linux
超级好用的C++实用库之套接字
超级好用的C++实用库之套接字
35 1
|
3月前
|
存储 算法 安全
超级好用的C++实用库之sha256算法
超级好用的C++实用库之sha256算法
128 1
|
2月前
|
Linux C++
Linux c/c++文件虚拟内存映射
这篇文章介绍了在Linux环境下,如何使用虚拟内存映射技术来提高文件读写的速度,并通过C/C++代码示例展示了文件映射的整个流程。
62 0