[转](14条消息) C++文件流fstream相关操作

本文介绍了C++中fstream库用于文件操作的基本方法,包括open函数的参数解析,如ios::app、ios::in和ios::out等模式,以及文件的读写、定位和二进制文件处理。示例代码展示了如何新建、写入CSV文件和处理日志。此外,讨论了文件指针的管理,如seekg和seekp函数的应用。
摘要由CSDN通过智能技术生成

C++文件流fstream相关操作

一、理解Open函数

利用fstream,使用open/close打开或创建,完成后关闭,对文件读入读出使用插入器(<<) 或析取器(>>)完成。参考C++文件写入、读出函数。

  1. 函数void open(…)参数选项
      在fstream类中,有一个成员函数open(),就是用来打开文件的,其原型是:void open(const char* filename, int mode, int access);
      
    打开文件的方式mode在类ios(是所有流式I/O类的基类)中定义,可以用“或”把以上属性连接起来,如 ios::out | ios::binary 常用的值如下:

ios::app:     以追加的方式打开文件  
ios::ate:    文件打开后定位到文件尾,ios:app就包含有此属性  
ios::binary:   以二进制方式打开文件,缺省的方式是文本方式。两种方式的区别见前文  
ios::in:     文件以输入方式打开(文件数据输入到内存)  
ios::out:     文件以输出方式打开(内存数据输出到文件) 
ios::nocreate:  不建立文件,所以文件不存在时打开失败  
ios::noreplace: 不覆盖文件,所以打开文件时如果文件存在失败  
ios::trunc:   如果文件存在,把文件长度设为0  

打开文件的属性access取值如下,可以用“或”或者“+”把以上属性连接起来,如3 或 1|2就是以只读和隐含属性打开文件。

0:普通文件,打开访问  
1:只读文件  
2:隐含文件  
4:系统文件 

C++文件流fstream相关操作
一、理解Open函数
  利用fstream,使用open/close打开或创建,完成后关闭,对文件读入读出使用插入器(<<) 或析取器(>>)完成。参考C++文件写入、读出函数。

  1. 函数void open(…)参数选项
      在fstream类中,有一个成员函数open(),就是用来打开文件的,其原型是:void open(const char* filename, int mode, int access);

打开文件的方式mode在类ios(是所有流式I/O类的基类)中定义,可以用“或”把以上属性连接起来,如 ios::out | ios::binary 常用的值如下:
ios::app:   以追加的方式打开文件
ios::ate:    文件打开后定位到文件尾,ios:app就包含有此属性
ios::binary:  以二进制方式打开文件,缺省的方式是文本方式。两种方式的区别见前文
ios::in:    文件以输入方式打开(文件数据输入到内存)
ios::out:   文件以输出方式打开(内存数据输出到文件)
ios::nocreate: 不建立文件,所以文件不存在时打开失败
ios::noreplace: 不覆盖文件,所以打开文件时如果文件存在失败
ios::trunc:   如果文件存在,把文件长度设为0

打开文件的属性access取值如下,可以用“或”或者“+”把以上属性连接起来,如3 或 1|2就是以只读和隐含属性打开文件。
0:普通文件,打开访问
1:只读文件
2:隐含文件
4:系统文件

2. 功能及应用场景

如果open函数只有文件名一个参数,则是以读/写普通文件打开, 即:file1.open(“c:\config.sys”); <=> file1.open(“c:\config.sys”, ios::in | ios::out, 0); 只读模式(fstream::out)可以创建新文件

1)文件新建并写入
如果该文件不存在则新建并写入,如果该文件存在则清除所有内容并从头开始写入;选择使用的参数:ios::trunc | ios::out | ios::in。

fstream _file;
 _file.open(FILENAME, ios::in);
 if(!_file) //或者_file.fail == true  or  _file.is_open == true
      cout<<FILENAME<<"没有被创建!"<<endl;
 else
      cout<<FILENAME<<"已经存在!"<<endl;

ofstream my_samplefile ("my_saple.txt",ios::trunc|ios::out|ios::in );

2)写入CSV文件
  CSV文件有其特殊性,由于逗号分隔符的存在,写入文件时只需要注意不遗漏必要的逗号,即可生成格式化的CSV文件。需要注意的是在open打开或创建的文件,务必以“.csv”后缀结束。

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>
#include <random>
using namespace std;

int main(){
    std::random_device rd; //obtain a seed
    std::mt19937 gen(rd()); //mersenne_twister_engine
    std::uniform_real_distribution<> dist(-1.0, 1.0);

    ofstream outFile;
    outFile.open("test5000.csv", ios::out);
    // 5000*128
    for(int i=1;i<=5000;i++){
        for(int j=1;j<=127;j++){
            outFile << dist(gen) << ',';
        }
        outFile <<dist(gen) <<endl;
    }
    outFile.close();
}

3)其它应用
  贴一个简单的日志处理程序:

#include <iostream>
#include <fstream>

//2018LP:增加日志
void TestComLog(std::string strTxt)
{
    std::ofstream wObj;
    wObj.open(".\\log\\NetTest20180827.txt", std::ios::app);
    wObj << strTxt << std::endl;
    wObj.close();
} ///< ssl 上下文

二、文件读写 & 文件格式化

ofstream是从内存到硬盘,ifstream是从硬盘到内存。文件读写的步骤:1、包含的头文件:#include 。2、创建流。3、打开文件(文件和流关联)。4、读写 (写操作:<<,put( ), write( ) 读操作: >> , get( ),getline( ), read( ))。5、关闭文件:把缓冲区数据完整地写入文件, 添加文件结束标志, 切断流对象和外部文件的连接。

  1. 操纵符 功能 输入/输出

dec   格式化为十进制数值数据 输入和输出  
endl  输出一个换行符并刷新此流 输出(关闭文件输出流) 
ends  输出一个空字符 输出  
hex   格式化为十六进制数值数据 输入和输出  
oct   格式化为八进制数值数据 输入和输出  
setpxecision(int p) 设置浮点数的精度位数 输出 

  1. 二进制文件的读写
     ①put() put()函数向流写入一个字符

其原型是ofstream &put(char ch),使用也比较简单,如file1.put(‘c’);就是向流写一个字符’c’。

②get() get()函数比较灵活,有3种常用的重载形式:

一种就是和put()对应的形式:ifstream &get(char &ch);功能是从流中读取一个字符,结果保存在引用ch中,如果到文件尾,返回空字符。如file2.get(x);表示从文件中读取一个字符,并把读取的字符保存在x中。

另一种重载形式的原型是: int get();这种形式是从流中返回一个字符,如果到达文件尾,返回EOF,如x=file2.get();和上例功能是一样的。

还有一种形式的原型是:ifstream &get(char *buf,int num,char delim=’\n’);这种形式把字符读入由 buf 指向的数组,直到读入了 num 个字符或遇到了由 delim 指定的字符,如果没使用 delim 这个参数,将使用缺省值换行符’\n’。例如:  file2.get(str1,127,‘A’); //从文件中读取字符到字符串str1,当遇到字符’A’或读取了127个字符时终止。

针对文本文件操作时,get函数和>>的区别:

区别:在读取数据时,get函数包括空白字符(遇空白字符不停止读取)

                  >>在默认情况下拒绝接受空白字符(遇到空白符停止读取)

③读写数据块

要读写二进制数据块,使用成员函数read()和write()成员函数,它们原型如下:

istream& read(unsigned char *buf,int num);
  ostream& write(const unsigned char *buf,int num);

read()从文件中读取 num 个字符到 buf 指向的缓存中,如果在还未读入 num 个字符时就到了文件尾,可以用成员函数 int gcount();来取得实际读取的字符数;而 write() 从buf 指向的缓存写 num 个字符到文件中,值得注意的是缓存的类型是 unsigned char *,有时可能需要类型转换。

成员函数eof()用来检测是否到达文件尾,如果到达文件尾返回非0值,否则返回0。原型是int eof();

  1. 文件定位
      和C的文件操作方式不同的是,C++ I/O系统管理两个与一个文件相联系的指针。一个是读指针,它说明输入操作在文件中的位置;另一个是写指针,它下次写操作的位置。每次执行输入或输出时,相应的指针自动变化。所以,C++的文件定位分为读位置和写位置的定位,对应的成员函数是seekg()和seekp()。seekg()是设置读位置, seekp是设置写位置。它们最通用的形式如下:

1 istream &seekg(streamoff offset,seek_dir origin); //设置读位置
2 ostream &seekp(streamoff offset,seek_dir origin); //设置写位置

streamoff定义于 iostream.h 中,定义有偏移量 offset 所能取得的最大值,seek_dir 表示移动的基准位置,是一个有以下值的枚举.

基准位置:

ios::beg:  文件开头
  ios::cur:  文件当前位置
  ios::end:  文件结尾

这两个函数一般用于二进制文件,因为文本文件会因为系统对字符的解释而可能与预想的值不同。

//指针移到文件的最前面
seekg(0) ;
//把当前的指针当作0
seekg(0,ios::cur);
//将指针移到文件尾,若再配合file.tellg()则可以求出文件的大小为多少bytes。
seekg(0,ios::end);

考虑一个本地数据记录的文件写入格式,或一个类似log4cpp的日志写入功能如何实现。

  1. 使用运算符<<(写)和getline()进行读写
     <<:以行为单位输入文件,getline():以行为单位 读入内存,能一次读入一行

    函数原型:istream &getline( char *buffer, streamsize num );
    
    功能:getline( )函数用于从文件读取num-1个字符到buffer(内存)中,直到下列情况发生时,读取结束:
    
    1):num - 1个字符已经读入
    
    2):碰到一个换行标志
    
    3):碰到一个EOF
    

三、文件指定行数删除或更新

文件的删除和重命名比较简单,但是以下两个操作都必须在文件关闭后才可以使用

1 //把这个文件删除
2 remove("文件名”);
3 rename("旧文件名","新文件名");

  1. 全部读出到自定义缓存区,修改缓存区某一行后再覆盖写入
      主要思路可选getline函数读取行数据,并计数。修改可以分为两种,一种是定长修改,一种是长度发生变化的修改。两种修改都有一种通用的修改方法,不过这个方法非常没有效率,那就是建立一个tmp文件,把修改过后的内容放到里面,然后删掉原文件把tmp文件改成原文件的名字。这个方法可用但显然不是很科学,而且如果文件内容很大,那么执行效率低下时间可能会挺长的,除了文件长度较小的场景外并不推荐使用。
  2. 修改文件打开属性,定长修改指定数据
     不定长修改某行后,其文件指针会移动,因此会影响到这行后面的部分,所以修改的时候不能该表指定行的长度。如果使用ios::app来打开文件,虽然不会清空文件内容,但是每次写操作都追加到文件末尾(app模式为追加写入),指针偏移操作无效,即使你seekp无效,参考C++ 修改/覆盖指定位置的文件内容或者从某个位置开始截断文件(里面部分细节表述不准确,但是总体思路是对的)。以下内容转载自关于fstream修改文件内容的操作,操作的方法和注意事项比较扼要。
     (1)在进行文件写的时候(非二进制),文件指针很成问题。举个例子:比如文件的内容是"100100100",写入了三个int类型的变量,值为100。接下来我如果想改变第二个100,使其变成200,文件指针需要移动3位,因为前面有三个字符。这时候指针是一个字符一个字符的移动的。

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

int main()
{
    ofstream ofs("cece.txt", ios::out);
    int x = 100;
    ofs << x << x << x;
    ofs.close();
    ofs.open("cece.txt", ios::out | ios::in);
    x = 200;
    ofs.seekp(3);
    ofs << x;
}

(2)而如果把文件换成二进制的写,那么情况就会有些变化,如果还是想改写第二个100的内容,那么文件指针就需要指向sizeof(int)。指针是一个byte一个byte地移动。 int存入的方式就是以int类型,将其原封写入文件,所以二进制的读写比较简单。

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

int main()
{
    ofstream ofs("cece.txt", ios::out | ios::binary);
    int x = 100;
    ofs.write((char *)&x, sizeof(x));
    ofs.write((char *)&x, sizeof(x));
    ofs.write((char *)&x, sizeof(x));
    ofs.close();

    ofs.open("cece.txt", ios::out | ios::in | ios::binary);
    x = 200;
    ofs.seekp(sizeof(int));
    ofs.write((char *)&x, sizeof(x));
}

(3)经实验表明,ios::app Mode下是不能移动文件指针的,而且这个时候tellp()是0。此模式下只能在后面追加着写。

(4).如果想要获得指向末尾的位置指针,需要

ofs.seekp(0, ios::end);
pointer = ofs.tellp();

这个时候如果文件内没有内容,那么pointer的值会是-1。等长修改就是这样,不等长修改的话,如果长度小于原来,可以写进去,然后添0,或者别的,大于原来的,目前只会tmp覆盖原文件的方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值