C++标准流

流的来源和目的

  • 在C++中,流可使用三个公共的来源与目的:控制台文件字符串

控制台流

流式输出

  • std::endl会刷新目标缓冲区,在循环中应该谨慎使用它。

  • put()接受单个字符,write()接受一个字符数组。传给这两个方法的数据将按照原本的形式输出。

    const char* str{"hello world!\n"};
    cout.write(str,strlen(str));
    cout.put('c');
    
  • 在以下任意一种条件下,流将刷新积累的数据:

    1. 遇到endl操作算子;
    2. 流离开作用域被析构;
    3. 流缓冲区满;
    4. 显式地刷新缓冲区,cout.flush();
    5. 从对应的输入流(cin)输入数据时。
  • good()方法获得流的基本验证信息,bad()方法返回true时说明发生了致命错误,fail()方法在最近一次操作失败时返回true,eof()方法在流到达文件尾部时返回true。

  • 流具有可转换为bool类型的转换运算符,转换运算符与!fail()返回的结果相同。即!cout == cout.fail()

  • 遇到文件结束标记时,good()fail()都会返回false。关系如下:good() == (!fail()) && !eof()

  • clear()方法可以重置流的错误状态。

  • 可以要求流在发生故障时抛出异常(ios_base::failure)。

    cout.exceptions(ios::failbit | ios::badbit | ios::eofbit);
    try{
        cout<<"hello world!"<<endl;
    }catch(const ios_base::failure& ex){
        cerr<<"Caught exception: "<<ex.what()<<",error code: "<<ex.code()<<endl;
    }
    
  • 输出操作算子

    1. boolalphanoboolalpha,将布尔值输出为true和false(boolalpha)或者1和0(noboolalpha),默认是noboolalpha;
    2. hexoctdec,分别以十六进制、八进制和十进制输出数字;
    3. setprecision,接收一个参数,设置输出小数时的小数位数;
    4. setw,接收一个参数,设置输出的数值数据的字段宽度;
    5. setfill,接收一个字符,设置为流的填充字符;
    6. showpointnoshowpoint,对于没有小数部分的浮点数,设置显示或者不显示小数点;
    7. put_money,参数化的操作算子,向流写入一个格式化的货币值;
    8. put_time,参数化的操作算子,向流写入一个格式化的时间值;
    9. quoted,参数化的操作算子,把给定的字符串放到引号中,并转义嵌入的引号。

流式输入

  • >>运算符会根据空白字符进行标志化,包括' ''\f''\n''\r''\t''\v'

  • 读取数据后应该检查流状态,这样可以从异常输入中恢复。

    int sum{0};
    if(!cin.good()){
        cerr<<"Standard input is in a bad state!"<<endl;
        return 1;
    }
    while(!cin.bad()){//ctrl+Z退出循环(windows)或者ctrl+D退出循环(linux)
    	int num;
        cin>>num;
        if(cin.good())sum+=num;
        else if(cin.eof())break;
        else if(cin.fail()){//如果输入的不是int而是字符或字符串
            cin.clear();//重置错误状态
            string badToken;
            cin>>badToken;//将字符或字符串写入badToken
            cerr<<"WARNING:Bad input encountered: "<<badToken<<endl;
    	}
    }
    cout<<"sum = "<<sum<<endl;
    
  • get()方法从流中读入原始输入数据,返回流中的下一个字符(最简单版本)。可用于避免>>运算符的自动标志化。

    string readName(istream& stream){//参数不能是const
        string name;
        while(stream){//或者:while(!stream.fail()){
            int next{stream.get()};//由于get()会返回一些特殊的非字符值,例如文件结束std::char_traits<char>::eof(),因此用int
            if(!stream||next==std::char_traits<char>::eof())break;
            name+=static_cast<char>(next);
        }
        return name;
    }
    //可以使用另一个版本的get()使代码更简洁,这个版本的get()接收一个字符的引用,返回一个流的引用。
    string readName(istream& stream){
        string name;
        char next;
        while(stream.get(next)){ name += next; }
        return name;
    }
    
  • unget()可以将读入的前一个字符放回流中。如果当前位置就是流的起始位置,那么调用unget()就会失败,可通过fail()方法查看。

    int main() {
        string name;
        string left;
        char ch;
        cin >> noskipws;//不跳过空白字符
        while (cin >> ch) {
            if (isdigit(ch)) {//如果是数字则放回流中并退出循环
                cin.unget();
                if (cin.fail()) cout << "unget() failed" << endl;
                break;
            }
            name += ch;
        }
        if (cin) cin >> left;//将剩余部分写入left
        if (!cin) cerr << "error getting size" << endl; 
        cout << "name: " << name << "\nleft: " << left << endl;
    }
    
  • putback()方法使用一个字符作为参数,将其放入cin中。

  • peek()方法可以预览流中的下一个字符。

  • getline()方法用一行数据填充字符缓冲区,数据量最多至指定大小,指定大小包括\0字符。

    const int buffersize{10};
    char buffer[buffersize] {0};
    cin.getline(buffer, buffersize, '@');//读取9个字符,或者读到行尾,或者遇到@符号,第三个参数是可选的分割符,默认是\n
    string str;
    std::getline(cin,str,'#');//读到行尾或遇到#符号,这个函数不需要指定缓冲区大小
    
  • 输入操作算子

    1. boolalphanoboolalpha:如果设置了boolalpha,字符串false会被解析为布尔值false,其他任何字符串都会被解析为布尔值true;如果设置了noboolalpha,0会被解析为false,其他任何值都被解析为true。默认为noboolalpha。
    2. dechexoct:分别读取十进制数、十六进制数和八进制数。
    3. skipwsnoskipws:读入空白字符作为标记,或者在标记化时跳过空白字符。
    4. ws:跳过流中当前位置的一串空白字符。
    5. get_money():从流中读入一个格式化的货币值。
    6. get_time():从流中读入一个格式化的时间值。
    7. quote():读取引号中的字符串,并转义嵌入的引号。

字符串流

std::ostringstream

  • 通过str()方法将数据转化为string对象。

std::istringstream

文件流

文本模式与二进制模式

  • 默认情况下,文件流在文本模式中打开。在文本模式中会执行一些隐式转换,写入文件或从文件中读取的每一行都以\n结束(windows下是\r\n)。
  • 二进制模式中,要求把流处理的字节写入文件。读取时,完全按照文件中的形式返回字节。

ofstream

  • ofstream的构造函数第一个参数为要打开的文件名,第二个参数为打开文件的模式。默认包含ios_base::out模式。

    常量说明
    ios_base::app打开文件,在每一次写操作之前,移到文件末尾
    ios_base::ate打开文件,打开之后立即移到文件末尾
    ios_base::binary以二进制模式执行输入输出操作
    ios_base::in打开文件,从开头开始读取
    ios_base::out打开文件,从开头开始写入,覆盖已有的数据
    ios_base::trunc打开文件,并删除任何已有的数据
  • 可使用seekp()方法在输出流中移动到任何位置。seekp()有两个重载版本,一个版本接收一个绝对位置作为参数,调用之后定位到这个位置。另一个版本接收一个偏移量和一个绝对位置作为参数,调用之后定位到绝对位置加偏移量的位置。位置的类型为std::streampos,偏移量的类型为std::streamoff,整数可被隐式转换为streampos类型和streamoff类型。

  • 可使用tellp()方法查询流的当前位置。这个方法返回一个表示当前位置的streampos值。

    位置说明
    ios_base::beg表示流的开头
    ios_base::end表示流的结尾
    ios_base::cur表示流的当前位置
  • 析构函数会自动关闭底层文件,因此不需要显式调用close()

ifstream

  • 可使用seekg()方法在输出流中移动到任何位置。
  • 可使用tellg()方法查询流的当前位置

将流链接在一起

  • 任何输入流和输出流之间都可以建立链接,从而实现“访问时刷新”,即当输入流请求数据时,链接的输出流会自动刷新。
  • 对输入流调用tie()方法,并传入输出流的地址即可建立链接。传入nullptr即可解除链接。
  • 输出流可以链接至另一个输出流。cerr和cout之间存在链接,因此cerr的任何输出都会导致刷新cout。

双向I/O

  • fstream类提供双向文件流。
  • 可以通过stringstream类双向访问字符串流。
  • 双向流用不同的指针保存读位置和写位置,在读取和写入之间切换时需要定位到正确的位置。

文件系统支持库

  • 路径:path接口,append()方法可以添加组件到路径中,会自动插入平台相关的路径分割符。

  • 目录:如果要查询文件系统上的实际目录或文件,需要通过path构造一个directory_entry

  • copy()函数可以复制文件或目录。

  • create_directory()函数可以在文件系统上创建一个新目录。

  • file_size()函数可以获取文件大小。

  • last_write_time()函数可以获取文件最后的修改时间。

  • remove()函数可以删除文件。

  • temp_directory_path()函数可以获取适合存储临时文件的目录。

  • space()函数可用于查询文件系统上的可用空间。

  • 遍历目录

    #include <filesystem>
    void printDirectoryStructure(const std::filesystem::path& p) {
        if (!std::filesystem::exists(p))return;
        std::filesystem::recursive_directory_iterator begin{ p };//起始迭代器
        std::filesystem::recursive_directory_iterator end{};//结束迭代器,默认构造
        for (auto iter{ begin }; iter != end; ++iter) {
            const string spacer(iter.depth() * 2, ' ');
            auto& entry{ *iter };//访问迭代器所引用的directory_entry
            if (is_regular_file(entry)) {
                cout << std::format("{}File:{}({}bytes)", spacer, entry.path().string(), file_size(entry)) << endl;
            }
            else if (is_directory(entry)) {
                cout << std::format("{}Dir:{}", spacer, entry.path().string()) << endl;
            }
        }
    }
    void printDirectoryStructure(const std::filesystem::path& p,size_t level) {
        if (!std::filesystem::exists(p))return;
        const string spacer(level * 2, ' ');
        if (is_regular_file(p)) {
            cout << std::format("{}File:{}({}bytes)", spacer, p.string(), file_size(p)) << endl;
        }
        else if (is_directory(p)) {
            cout << std::format("{}Dir:{}", spacer, p.string()) << endl;
            for (auto& entry : std::filesystem::directory_iterator{ p }) {
                printDirectoryStructure(entry, level + 1);
            }
        }
    }
    int main() {
        std::filesystem::path p{ R"(C:\mingw64)"};
        printDirectoryStructure(p, 0);
    }
    
  • 18
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

平面海螺

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值