C++基础5:关于文件操作

1. 流

  • 流:数据从一个对象到另一个对象的传输。
  • 功能:标准输入输出+文件处理
分类含义
文本流一串ASCII字符
二进制流一串二进制

2. 流类型

标准库定义了三大类流类型:标准I/O流、文件流、字符串流

  • 标准I/O流

    • ios是抽象类
    • ostream是cout、clog、cerr的类
    • istream是cin的类
  • 文件流类型

    • ifstream从文件读取数据
    • ofstream向文件写入数据
    • iofstream文件读写数据
  • 字符串流类型

    • istringstream从string读取数据
    • ostringstream向string写入数据
    • iostringstream读写string数据

3. 流对象

通常标准I/O流对象是全局对象不需要定义,而文件流对象和字符串流对象需要用户定义。

标准I/O流对象有以下四个:

No.全局流对象名称缓存
1cout标准输出流带缓存
2cin标准输入流带缓存
3clog标准日志流带缓存
4cerr标准错误流无缓存
注意:流对象通常都不能复制。

4. 流对象状态

流对象状态在某一个时刻必定处于以下四个状态之一。

No.状态含义
1good()前一个流操作成功
2eof()到输入尾/文件尾
3fail()发生意外事情(读取失败)
4bad()发生意外严重事情(磁盘读取失败)

5. I/O操作

I/O操作主要有如下五种:

  • 输入操作:in >> x或者getline(in,s)

  • 输出操作:out << x

  • 操作符

  • 流状态

  • 格式化

  • 输出流默认设置

类型进制宽度对齐填充精度
整数十进制0右对齐空格1
实数十进制0右对齐空格6位数
字符串-0右对齐空格字符串实际长度
  • 格式控制
  • 格式控制成员函数
流对象.格式控制函数(实参)
  • 预定义格式控制函数
预定义格式控制函数(实参)
  • 流的输出控制格式
作用格式控制成员函数预定义格式控制函数预定义格式控制符/操作子效果持续
进制flags() setf() unsetf()setiosflags()dec oct hex showbase能持续
宽度width(n)setw(n)-不能持续
对齐flags() setf() unsetf()setiosflags()right left internal能持续
填充fill©setfill©-能持续
精度precision(n)setprecision(n)-能持续
  • 流的输出控制格式:dec oct hex
  • 数据输入成员函数
    字符输入成员函数:get()
    字符串输入成员函数:getline()
  • 数据输出成员函数:put()
  • 完整案例
#include <iostream>
using namespace std;

int main(){
        int n = 10;
        float f = 0.1;
        string s = "Hello";
        cout.width(10); // 设置宽度为10
        cout.fill('*');
        cout.flags(ios::hex); // 设置为8进制输出
        cout << n << endl;
        cout.width(10);
        cout << 64 << endl;
        cout.width(10);
        cout << f << endl;
        cout.width(10);
        cout << s << endl;

        cout.precision(2);
        cout.flags(ios::fixed); // 保留小数点后几位有效数字
        cout << 1113.14141421 << endl;

}
  • 预定义格式的控制方法

a. 头文件

#include <iomanip> // setw() setfill() 

b. 宽度为10,空白处已*填充,以八进制输出,左对齐

cout << setw(10) << setfill('*') << hex << left << n << endl;
// 宽度为10,空白处已*填充,以八进制输出,左对齐

c. 保留小数点后两位有效数字

  cout << setprecision(2) << fixed << 1113.1415926 << endl;
        // 保留小数点后两位有效数字
  • 完整案例
#include <iostream>
#include <iomanip> // setw() setfill() 
using namespace std;

int main(){
        int n = 10;
        float f = 0.1;
        string s = "Hello";

        cout << setw(10) << setfill('*') << hex << left << n << endl;
        // 宽度为10,空白处已*填充,以八进制输出,左对齐
        cout << setw(10) << 64 << endl;
        cout << setw(10) << f << endl;
        cout << setw(10) << s << endl;
        cout << setprecision(2) << fixed << 1113.1415926 << endl;
        // 保留小数点后两位有效数字
}

6. 对齐方式

flagmanipulator作用
ios::leftleft居左
ios::rightright居右
ios::internalinternal输出符号或进制后填充
  • 成员函数方式
#include <iostream>
using namespace std;
int main(){
    int n = -11;
    cout.width(6);
    cout.flags(ios::right);
    cout << n << endl;

    cout.width(6);
    cout.flags(ios::left);
    cout << n << endl;

    cout.width(6);
    cout.flags(ios::internal);
    cout << n << endl;
}
  • 操作子方式
#include <iostream>
#include <iomanip>
using namespace std;
int main(){
    int n = -11;
    cout << setw(6) << right << n << endl;
    cout << setw(6) << left << n << endl;
    cout << setw(6) << internal << n << endl;
}
  • 混合方式
#include <iostream>
using namespace std;
int main(){
int n = -11;
    cout.width(6); cout << left << n << endl;
    cout.width(6); cout << right << n << endl;
    cout.width(6); cout << internal << n << endl;
}

7. 整数输出格式

flagmanipulator作用是否默认
ios::decdec十进制
ios::octoct八进制
ios::hexhex十六进制
ios::uppercaseuppercase使用大写输出十六进制
ios::showbaseshowbase输出带有进制的字符
  • 成员函数方式
#include <iostream>
using namespace std;
int main(){
    int n = 11;
    cout.flags(ios::dec);
    cout << n << endl;
    cout.flags(ios::hex);
    cout << n << endl;
    cout.flags(ios::oct);
    cout << n << endl;

    cout.flags(ios::showbase|ios::dec);
    cout << n << endl;
    cout.flags(ios::showbase|ios::oct);
    cout << n << endl;
    cout.flags(ios::showbase|ios::hex);
    cout << n << endl;

    cout.flags(ios::showbase|ios::uppercase|ios::dec);
    cout << n << endl;
    cout.flags(ios::showbase|ios::uppercase|ios::oct);
    cout << n << endl;
    cout.flags(ios::showbase|ios::uppercase|ios::hex);
    cout << n << endl;
}
  • 操作子方式
#include <iostream>
using namespace std;
int main(){
    int n = 11;
    cout << dec << n << endl;
    cout << hex << n << endl;
    cout << oct << n << endl;

    cout << showbase << dec << n << endl;
    cout << oct << n << endl;
    cout << hex << n << endl;

    cout << uppercase << dec << n << endl;
    cout << oct << n << endl;
    cout << hex << n << endl;
}

8. 浮点数输出格式

flag作用是否默认精度
ios::defaultfloat默认浮点数格式最多保留多少位数字
ios::scientific科学计数法输出浮点数小数点后最多保留多少位数字
ios::fixed定点数方式输出实数小数点后最多保留多少位数字

如果浮点数没有小数时默认不显示小时点,使用ios::showpoint可以强制输出浮点数时,必须带小数点。

定点数方式比浮点数方式更精确
取浮点数精确度时,设置ios::fixed

  • 成员函数方式
#include <iostream>     // cout, std::fixed, std::scientific
using namespace std;
int main () {
  double a = 3.1415926534;
  double b = 2006.0;
  double c = 1.0e-10;

  cout.precision(5);
  cout << "default:\n";
  cout << a << endl << b << endl << c << endl;
  
  cout << "fixed:\n";
  cout.flags(ios::fixed);
  cout << a << endl << b << endl << c << endl;

  cout << "scientific:\n";
  cout.flags(ios::scientific);
  cout << a << endl << b << endl << c << endl;

  return 0;
}
  • 操作子方式
#include <iostream>     // std::cout, std::fixed, std::scientific
#include <iomanip>
using namespace std;
int main () {
  double a = 3.1415926534;
  double b = 2006.0;
  double c = 1.0e-10;

  cout << setprecision(5)
       << "default:\n"
       << a << endl << b << endl << c << endl;
  
  cout << "fixed:\n" << fixed
       << a << endl << b << endl << c << endl;

  cout << "scientific:\n" << scientific
       << a << endl << b << endl << c << endl;

  return 0;
}
  • 混合方式
#include <iostream>     // std::cout, std::fixed, std::scientific
int main () {
  double a = 3.1415926534;
  double b = 2006.0;
  double c = 1.0e-10;

  std::cout.precision(5);
  std::cout << "default:\n";
  std::cout << a << '\n' << b << '\n' << c << '\n\n'
  
  std::cout << "fixed:\n" << std::fixed;
  std::cout << a << '\n' << b << '\n' << c << '\n\n'

  std::cout << "scientific:\n" << std::scientific;
  std::cout << a << '\n' << b << '\n' << c << '\n\n';

  return 0;
}

9. 布尔类型输出格式

flagmanipulator作用默认
ios::boolalphaboolalpha把bool值以字符串true/false输出

10. 其它

flagmanipulator作用默认
ios::showposshowpos输出十进制0或者正数时,带+号
  • 成员函数方式
#include <iostream>     // std::cout, std::showpos, std::noshowpos
using namespace std;
int main () {
  int p = 1;
  int z = 0;
  int n = -1;
  cout.setf(ios::showpos); cout << p << '\t' << z << '\t' << n << endl;
  cout.unsetf(ios::showpos); cout << p << '\t' << z << '\t' << n << endl;
  return 0;
}
  • 操作子方式
#include <iostream>     // std::cout, std::showpos, std::noshowpos
using namespace std;
int main () {
  int p = 1;
  int z = 0;
  int n = -1;
  cout << showpos   << p << '\t' << z << '\t' << n << endl;
  cout << noshowpos << p << '\t' << z << '\t' << n << endl;
  return 0;
}
复数虚部 a+bi 使用 showpos 非常合适。
  • 问题
  1. 浮点数八进制/十六进制输出结果?
  2. 下列代码为什么能转化成十六进制?
#include <iostream>
#include <iomanip> // setiosflags() , ios::hex
using namespace std;
int main(){
        cout << setiosflags(ios::hex) << 10 << endl;
}

最新版C++的iostream库中,在使用setiosflags()前要先使用resetiosflags()清除旧有相应信息。

cout << resetiosflags(ios::basefield)<< setiosflags(ios::hex) << 10 << endl;

以下三类格式化在使用 setiosflags() , 要先使用 resetiosflags() 清除旧有相应信息。
在这里插入图片描述

  • stringstream
  • 字符串转化数字
istringstream iss(str);
int n;
iss >> n;
  • C++11提供如下函数简化字符串转数字

  • stoi() stol() stoul() stoll() stoull()

  • stof() stod() stold()

  • 数字转化字符串

int n;
ostringstream oss;
oss << n;
oss.str();
  • 写成友元的写法
#include <iostream>
#include <sstream>
using namespace std;

class Simple{
	int n;
public:
	Simple():n(0){}
	Simple(int n):n(n){}
	friend ostream& operator<<(ostream& os, const Simple& s){
		return os << "Simple(" << s.n << ")" << endl;
	}
	friend istream& operator>>(istream& is, Simple& s){
		return is >> s.n;
	}
};

int main(){
	ostringstream oss;
	oss << 4 << " " << 3.14 << " " << "abc" << endl	<< Simple(100) << endl;
	cout << oss.str();
	
	istringstream iss("abc 123 0.11 200");
	string s;
	int n;
	float f;
	Simple simple;
	iss >> s >> n >> f >> simple;
	cout << s << n << f << simple;
}
  • 不写成友元的写法
#include <iostream>
#include <sstream>
using namespace std;

class Simple{
	int n;
public:
	Simple():n(0){}
	Simple(int n):n(n){}
	void SetN(int n){this->n = n;}
	int GetN()const{return n;}
};
	ostream& operator<<(ostream& os, const Simple& s){
		return os << "Simple(" << s.GetN() << ")" << endl;
	}
	istream& operator>>(istream& is, Simple& s){
		int n;
		is >> n;
		s.SetN(n);
		return is;
	}

int main(){
	ostringstream oss;
	oss << 4 << " " << 3.14 << " " << "abc" << endl	<< Simple(100) << endl;
	cout << oss.str();
	
	istringstream iss("abc 123 0.11 200");
	string s;
	int n;
	float f;
	Simple simple;
	iss >> s >> n >> f >> simple;
	cout << s << n << f << simple;
}

11. C++文件读写

文件:文件名+文件内容(有序数据集合)
文件名:字符序列
文件数据格式:二进制文件/文本文件

  • C++文件操作流程
  1. 打开文件
  2. 读写文件
  3. 关闭文件
  • 打开/关闭文件
操作代码
定义读文件流对象ifstream 读文件流对象
定义写文件流对象ofstream 写读文件流对象
定义读写文件流对象fstream 读写读文件流对象
成员函数打开文件文件流对象.open(文件名,文件打开方式)
构造函数打开文件文件流类 对象(文件名,文件打开方式)
判断文件是否打开!文件流对象
关闭文件文件流对象.close(变量)
  • 文本文件读写
操作代码
提取运算符读文件文件流对象 >> 变量
插入运算符写文件文件流对象 << 变量
成员函数读文件文件流对象.get(变量)
成员函数写文件文件流对象.put(变量)
  • 二进制文件读写
操作代码
读函数read()
写函数write()
测试文件结束eof()
  • 文件打开方式
类型代码
ios::in
ios::out
添加末尾ios::app
已存在文件ios::nocreate
未打开文件ios::noreplace
二进制ios::binary
  • 文件对象指针位置函数
操作代码
获取读位置对象.tellg()
获取写位置对象.tellp()
设置读位置对象.seekg()
设置写位置对象.seekp()
  • 函数后缀 p 表示 put(输出),后缀 g 表示 get(输入)。

  • 如果文件是以 ios::app 文本追加方式打开,指针位置默认在文件结束,其他情况默认在文件开头。

  • 文件对象状态函数

操作代码
判断文件对象状态是否正常对象.good()
重置文件对象状态对象.clear()

流式文件类型

  1. stream 流文件
  2. 文件指针 FILE*

stream流文件读写

  • ifstream 文件读
ifstream fin(文件路径);
fin >> 变量
fin.close();
  • ofstream 文件写
ofstream fout(文件路径);
fout << 变量
fout.close();
  • fstream 文件写
fstream fs(文件路径,ios::in|ios::out|ios::app);
if(fs){
  fs << 变量;
  fs.seekg(ios::beg);
  fs >> 变量;
  fs.close();
}
  • fstream 的打开模式是否创建不存在的文件
No.打开模式是否创建不存在的文件
1ios::in
2ios::out
3ios::in/ios::out
4ios::in/ios::out/ios::app
  • 先读后写
fstream fs("test.txt",ios::in|ios::out|ios::app);
if(fs){
    // 读文件
    string str;
    while(fs >> str){
        cout << str << endl;
    }
    fs.clear();// 清除错误
    // 写文件
    while(cin >> str){
        fs << str << endl;
    }
}
  • 其他案例
#include <iostream>
#include <fstream>
using namespace std;

int main(){
        fstream fs("./test3",ios::in|ios::out|ios::app);
        if(fs){
                string str;
                fs >> str;
                cout << str << endl;
                cout << fs.eof();
                fs.clear(); // 清除eof状态
                string str2;
                cin >> str2;
                fs << str2;
        }
}
  • 先写后读
fstream fs("test.txt",ios::in|ios::out|ios::app);
if(fs){
    // 写文件
    string str;
    while(cin >> str)
        fs << str << endl;
    
    // 读文件  
    fs.seekg(ios::beg);
    while(fs >> str)
        cout << str << endl;
    // 后续如果对fs操作,需要清空fs状态
}else{
    cerr << "file not exist " << endl;
}
  • 其他案例
#include <iostream>
#include <fstream>
using namespace std;

int main(){
	fstream fs("./test3",ios::in|ios::out|ios::app);
	if(fs){
		string str;
		cin >> str;
		fs << str; // 每次写完文件读写指针位于文件末尾
		fs.seekg(ios::beg); // 把文件读写指针移动到文件开头
		string str2;
		fs >> str2;
		cout << str2 << endl;
		fs.close();
	}
}

文件指针 FILE 读写

  • FILE 文件指针读
FILE* fp = fopen(文件路径,"r");
fscanf(,fp);
fclose(fp);
  • FILE文件指针写
FILE* fp = fopen(文件路径,"w");
fprintf(,fp);
fclose(fp); 

对象的序列化与反序列化
序列化:把对象转化成文本/二进制
反序列化:把文本/二进制转化成对象

文件重定向

freopen(文件路径,操作,标准IO)
操作:读 (r) 写(w)
  • 重定向写
freopen("out.txt","w",stdout);
cout << "out data" <<endl;
  • 重定向读
freopen("in.txt","r",stdin);
string str;
cin >> str;
  • 几种常见的文件读取方式对比

    • ifstream + fin
    • freopen+cin+sync_with_stdio(false)
    • FILE* + fscanf
    • freopen+scanf
    • freopen+cin
  • 实现日志模块

freopen("test.log","w",stderr);
cerr << "FATAL";
cerr << "ERROR";
clog << "WARNING";
clog << "INFO";
clog << "DEBUG";
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值