当前位置: 首页 > article >正文

C++(C++的文件I/O)

一、C++的文件IO

在C++中把文件的读写操作都封装在标准库中,ifstream类主要用于读取文件内容,ofstream主要用于写入文件内容,fstream类可读可写。

打开文件操作:
1、使用构造函数打开文件
fstream(const char *filename, openmode mode);
功能:创建操作文件的类对象,并顺便打开文件
filename:文件的路径
mode:打开的方式或权限
    默认参数是:O_RDWR
    ios::app 添加输出,O_WRONLY|O_CREAT|O_APPEND 
    ios::in 为读取打开文件 O_RDONLY
    ios::out 为写入打开文件 O_WRONLY|O_CREAT|O_TRUNC
    ios::binary 以二进制模式打开文件,相当于C语言中fopen函数的带b的打开方式
    ios::ate 当已打开时寻找到EOF,打开文件后顺序设置文件位置指针
    ios::trunc 文件存在则清空文件 O_TRUNC
​
ifstream( const char *filename, openmode mode);
mode
    默认参数是:O_RDONLY
    
ofstream( const char *filename, openmode mode);
mode
    默认参数是:O_WRONLY|O_CREAT|O_TRUNC
2、使用open成员函数打开文件
void open( const char *filename);
void open( const char *filename, openmode mode);
功能:与构造函数参数相同
3、如何判断文件打开成功或失败
方法1:直接使用类对象进行逻辑判断,因它们重载逻辑运算符。
方法2:调用good函数,该函数用于判断对象的上一次操作是否成成功,所以也可判断文件打开是否成功。
文本格式读写:

注意:像使用cout一样写入数据,使用cin一样读取数据。

注意:如果需要对一个结构、类进行文本格式读写,最好给它重载输入、输出运算符,不光cin、cout可以使用,ofstream、ifsteam也可以使用。

练习1:设计一个员工类,写入若干个员工信息到emp.txt文件,然后再读取出来测试是否写入成功。

二进入格式读写:

注意:如果是在Windows系统下读写二进制文件,mode参数中要有ios::binary,就像在C语言中要加b。

istream& read( char *buffer, streamsize num );
功能:读取一块数据到内存
buffer:一般情况下需要强制类型转换,特别是结构对象或类对象。
注意:返回值与标准C和Linux系统读取函数不同,需要调用gcount函数获取读取了多少个字节数据。
​
ostream& write(const char *buffer, streamsize num);
功能:把一块内存中的数据写入文件
注意:返回值与标准C和Linux系统读取函数不同,需要调用good函数判断写入是否成功。

注意:如果结构、类成员中有指针成员或string类的成员变量,不能以二进制格式直接把对象保存到文件中,最好以文本格式保存。

练习:使用C++语言实现cp命令。

随机读写:
istream &seekg( off_type offset, ios::seekdir origin );
ostream &seekp( off_type offset, ios::seekdir origin );
功能:以偏移值+基础位置设置文件的位置指针,之所以这样设计是为了兼容那些有两个文件位置(读写分开)操作系统,使用方法与lseek、fseek类型。
ios::seekdir origin
    ios::beg SEEK_SET
    ios::cur SEEK_CUR
    ios::end SEEK_END
​
istream &seekg( pos_type position );
ostream &seekp( pos_type position );
功能:以绝对位置设计文件的位置指针
pos_type:
    把文件的位置指针移动到文件的第几个字节。
​
pos_type tellg();
pos_type tellp();
功能:获取文件的位置指针,与ftell函数的功能相同。
注意:由于操作系统的文件位置指针是两个,读取各一个,所有C++语言提供了两g和p两套位置指针函数,但在Linux系统和Windows系统下,读写操作共用一个位置指针,所以使用p、g没有区别。

特殊格式的读写:
fmtflags flags();
fmtflags flags( fmtflags f );
功能:获取当前流的格式标志
​
fmtflags setf( fmtflags flags );
fmtflags setf( fmtflags flags, fmtflags needed );
功能:设置当前流的格式化标志为flags
​
void unsetf( fmtflags flags );
清除与当前流相关的给定的标志flags 
操作符描述输入输出
boolalpha启用boolalpha标志XX
dec启用dec标志XX
endl输出换行标示,并清空缓冲区X
ends输出空字符X
fixed启用fixed标志X
flush清空流X
hex启用 hex 标志XX
internal启用 internal 标志X
left启用 left 标志X
noboolalpha关闭boolalpha 标志XX
noshowbase关闭showbase 标志X
noshowpoint关闭showpoint 标志X
noshowpos关闭showpos 标志X
noskipws关闭skipws 标志X
nounitbuf关闭unitbuf 标志X
nouppercase关闭uppercase 标志X
oct启用 oct 标志XX
right启用 right 标志X
scientific启用 scientific 标志X
showbase启用 showbase 标志X
showpoint启用 showpoint 标志X
showpos启用 showpos 标志X
skipws启用 skipws 标志X
unitbuf启用 unitbuf 标志X
uppercase启用 uppercase 标志X
ws跳过所有前导空白字符X
#include <iostream>
using namespace std;
​
int main(int argc,const char* argv[])
{
    /*
    printf("|%4d|\n",1);    
    cout << "|";
    cout.width(4);
    cout << 1 << "|" <<  endl;
    */
    int num = 0x01020304;
    printf("%x\n",num);
    cout << hex << num << endl;
    bool flags = false;
    cout << boolalpha << flags << endl;
    return 0;
}

二、 异常处理

什么是异常处理:

从宏观角度来说,异常处理就是当程序执行过程中出现了错误,以及对错误的处理方案。

C语言的异常处理:

C语言一般通过函数返回值、信号,来表示程序在运行过程中出现的错误。

例如:

文件打开失败,fopen、open函数的返回值来判断文件打开是否成功。

缺点:返回的类型单一,返回的数据很难跨作用域,还需要考虑它们的成功情况,必须使用if、switch对返回值进行判断。

断错误、非法硬件指令、总线错误、浮点异常等代码执行过程中出现错误信息。

缺点:错误信息过于简单,捕获处理完后进行依然需要结束。

C++语言的异常处理:
1、如何抛异常

throw 数据;

类似return语句返回一个数据,但不同时它可以返回任何类型的数据,并且可以不需要预告声明。

注意:throw与return最大区别是,throw返回的数据,上层必须处理,否则程序会立即结束(核心已转储)。

2、声明异常

1、所谓的异常声明,就是函数的实现者对调用者的一种承诺,我会抛哪些类型的导常

返回值类型 函数名(参数列表) throw(类型,...)
{
​
}

2、如果函数不进行异常声明,则表示可能会抛出任何类型的异常。

3、如果抛出了声明以外的异常,编译不会出错,但无法捕获,即使你写的准确捕获语句,也无法捕获,也就是说如果函数的实现者不遵守承诺,调用它的程序只有死路一条。

4、throw() 表示不会抛出任何异常,请放心调用。

size_t file_size(const char* filename)
{
    throw 1234;
    ifstream ifs(filename);
    if(!ifs)
    {
        throw string("文件打开失败!");
    }
​
    ifs.seekg(0,ios::end);
    return ifs.tellg();
}
​
int main(int argc,const char* argv[])
{
    try{
        cout << file_size("eheheheheh") << endl;
    }
    catch (int num)
    {
        cout << "我就知道你不靠谱" << num << endl;
    }
    catch (string str)
    {
        cout << str << endl;
    }
    return 0;
}

5、类成员函数的异常声明列表如果不同,会影响函数覆盖,如果其它条件都符,只有异常声明列表不同,编译会出错误。

#include <iostream>
using namespace std;
​
class A
{
public:
    virtual void func(void) throw()
    {   
        cout << "我是A类的func函数" << endl;
    }   
};
​
class B : public A
{
public:
    void func(void) throw()
    {   
        cout << "我是B类的func函数" << endl;
    }   
};
​
int main(int argc,const char* argv[])
{
    A* a = new B;
    a->func();
    return 0;
}
3、捕获异常
try{
    可能产生异常的函数调用、代码。
}
catch(类型1 变量名){
    1、处理异常
    2、继续往上抛
}
catch(类型2 变量名){
    1、处理异常
    2、继续往上抛
}
...
int main(int argc,const char* argv[])
{ 
    int* p;
    try{
        p = new int[0xffffffff];
    }
    catch(bad_array_new_length error){
        cout << "申请内在失败" << endl;
        cout << error.what() << endl;
    }
}

注意:如果继续往上抛的异常,没有被处理,那么程序将停止执行(我个人习惯,只在main函数内进行异常捕获)。

4、抛异常和捕获异常要注意的问题

1、捕获异常时要先尝试捕获子类异常变量,再捕获父类异常变量,因为catch不会挑选最合适的,而从上到下选择一个可以捕获的类型,或者只写捕获父类异常变量,这样返回父类异常和子类异常都可以兼容。

2、不要在异常类的构造函数的析构函数中抛出异常,如果该类对象就是异常数据,那么会在抛异常的过程中产生新的异常(指的设计异常类,暂时不需要掌握)。

3、不要抛指针类型的异常,因为我们的异常是跨作用域的,当捕获者获得异常后,指针指向的内存可能已经释放,那么捕获的指针就可能是野指针(异常会一层层往上抛,要么被捕获,要么是抛到main函数中,程序死掉)。

4、尽量使用类名创建临时的类对象进行抛异常,使用引用来捕获异常,因为这样既避免调用拷贝构造函数,也避免对象出了作用域后被释放产生悬空引用。

5、不需要抛基本类型的异常数据,如果想抛自定义的异常,建议封装成异常类,并且该类继承exception类,这样我们只需要在main函数中写一份异常捕获即可。

C++标准异常:

所谓的C++标准异常就是在使用C++标准库中的函数、类、类对象、new、delete时可能抛出的异常,简称C++标准异常。

异常描述
std::exception该异常是所有标准 C++ 异常的父类。
std::bad_alloc该异常可以通过 new 抛出。
std::bad_cast该异常可以通过 dynamic_cast 抛出。
std::bad_exception这在处理 C++ 程序中无法预期的异常时非常有用。
std::bad_typeid该异常可以通过 typeid 抛出。
std::logic_error理论上可以通过读取代码来检测到的异常。
std::domain_error当使用了一个无效的数学域时,会抛出该异常。
std::invalid_argument当使用了无效的参数时,会抛出该异常。
std::length_error当创建了太长的 std::string 时,会抛出该异常。
std::out_of_range该异常可以通过方法抛出,例如 std::vector 和 std::bitset<>::operator。
std::runtime_error理论上不可以通过读取代码来检测到的异常。
std::overflow_error当发生数学上溢时,会抛出该异常。
std::range_error当尝试存储超出范围的值时,会抛出该异常。
std::underflow_error当发生数学下溢时,会抛出该异常。
#include <exception>
using namespace std;
​
int main(int argc,const char* argv[])
{
    // 只要是C++标准异常,该方法都可以捕获
    try{
        // int* p = new int[0xffffffff];
        // string str(0xffffffff,'x');
        int a = 1234 , b = 0;
        int c = a / b;
    }   
    catch (exception& ex) 
    {   
        cout << ex.what() << endl;
    }   
    
    return 0;
} 
自定义的通用异常类
#ifndef MY_ERROR_H
#define MY_ERROR_H
#include <iostream>
using namespace std;
​
class MyError:public exception
{
    string whatInfo;
public:
    MyError(const char* file,const char* func,size_t line,const char* info)
    {
        whatInfo = file;
        whatInfo += " ";
        whatInfo += func;
        whatInfo += " ";
        
        char buf[21];
        sprintf(buf,"%u",line);
        whatInfo += buf;
​
        whatInfo += ":";
        whatInfo += info;
    }
​
    ~MyError(void) throw() {}
​
    const char* what(void)const throw()
    {
        return whatInfo.c_str();
    }
};
​
#define Error(info) MyError(__FILE__,__func__,__LINE__,info)
​
#endif//MY_ERROR_H

C++中的异常处理与C语言的错误处理的区别?

throw是在return语句的基础上实现了,都是向调用返回一个数据。

1、throw可以返回多种类型数据,而return只能返回一种。

2、return返回的数据可以不处理,throw返回的数据必须处理,否则程序停止运行。

3、return返回的数据给调用者,throw返回的数据可以一层一层向上返回,直到被捕获处理。


http://www.kler.cn/a/310078.html

相关文章:

  • Java Stream 流常用操作大全
  • VSCode可以安装最新版,并且可以对应Node 12和npm 6
  • 【Xrdp联机Ubuntu20.04实用知识点补充】
  • Golang常见编码
  • [SWPUCTF 2022 新生赛]Power! 反序列化详细题解
  • 云防护单节点2T抗攻击能力意味着什么?
  • nanoGPT用红楼梦数据从头训练babyGPT-12.32M实现任意问答
  • Redis 5.0.4 安装教程
  • NFT Insider #147:Sandbox 人物化身九月奖励上线;Catizen 付费用户突破百万
  • PDF转图片的思路思考
  • Leetcode—环形链表||
  • 脚本基本规则
  • C++:日期类的实现
  • java 递归读取前10个匹配的文件所在的全路径
  • 松散绑定是什么?
  • 切换淘宝最新镜像源:优化NPM包管理的极致体验
  • windows C++ 并行编程-异步消息块(一)
  • 【系统架构设计师-2016年真题】案例分析-答案及详解
  • Java从入门到精通学习框架(三)
  • Mybatis+Druid+MybatisPlus多数据源配置
  • 闲鱼网页版开放,爬虫的难度指数级降低。
  • LDD学习启程(TODO)
  • 【React】React18新特性 - startTransition
  • vue-ts-demo
  • 【C-项目】网盘(一期,无限进程版)
  • 什么是数据治理?如何保障数据质量安全