zoukankan      html  css  js  c++  java
  • C++课程学习笔记第七周(上):输入输出

    前言:本文主要是根据MOOC网北大课程——《程序设计与算法(三):C++面向对象程序设计》内容整理归纳而来,整理的课程大纲详见 https://www.cnblogs.com/inchbyinch/p/12398921.html

    本文介绍了常用的输入输出流、流操纵算子、文件读写,以及缓冲与缓存的区别。

    1 输入输出流

    1.1 基本IO类相关

    输入输出相关类:

    标准流对象:cin, cout, cerr, clog

    判断输入流结束:if(cin>>x), if(cin.get(..))等(估计本质上是调用了cin.good(),windows屏幕中输入流结束符为单独一行ctrl+z)

    istream类成员函数:

    • istream & cin.operator>>();
    • istream & cin.get()等;
    • istream & getline(char * buf, int bufSize)等;
    • bool eof(); 判断输入流是否结束
    • int peek(); 返回下一个字符,但不从流中去掉.
    • istream & putback(char c); 将字符ch放回输入流
    • istream & ignore(int nCount = 1, int delim = EOF);

    输出输入重定向:

    • freopen("test.txt", "w", stdout); //将标准输出重定向到 test.txt文件
    • freopen("t.txt", "r", stdin); //cin被改为从 t.txt中读取数据

    1.2 cin的详细用法:

    https://blog.csdn.net/bravedence/article/details/77282039

    cin读取数据方式常用三种:cin>> , cin.get() , cin.getline() .

    • 在屏幕上输入数据后,需要敲一个回车才能把数据送到缓冲区中,回车符会被替换成' ',作为一个字符存入缓冲区中;
    • cin读取数据也是从缓冲区中获取数据,缓冲区为空时,cin的成员函数会阻塞等待数据的到来,一旦缓冲区中有数据,就触发cin的成员函数去读取数据。
    • cin>> 读取数据时,会跳过并清除开头的空白字符,继续读取下一个字符;若读取成功,后面未读取成功的字符(包括换行符等空白字符)会残留在缓冲区中,cin不做处理。(推测:cin>>读取字符数组时会在末尾自动加上'',因为能正确cout)
    • cin.get常用的有四种,前两种读取一个字符,后两种可读取一行;开头不会跳过空白字符,读取一行字符时遇到' '停止,但' '仍残留在缓冲区中,不做处理。(故推荐用getline读取一行)
    • cin.getline不会跳过开头的空白字符,持续读取直至将' '读入char数组,并将最后一个字符' '换成''。(此时缓冲区中已经没有这个' '了。)
    • 若cin.get或cin.getline读入字符个数达到限定值,则读入失败,cin.fail()为1,(虽然此次已经读入,但下次cin不可用,)可用if(cin.getline(..))判断是否读入成功,可用cin.clear()重置。
    • cin.getline之后可以用cin.gcount()来查看成功读取的字符个数。
    • 注意cin.getline()和getline()不同,前者读进char数组中,后者读进string中。

    cin的条件状态函数:https://www.cnblogs.com/wangduo/p/5940884.html

    • 每一个IO对象都会维护一组条件状态属性,用来表示对象当前的条件状态,包括eofbit、failbit、badbit、goodbit等;
    • 获取、设置条件状态的函数有:s.eof()、s.fail()、s.bad()、s.good()、s.clear()、s.rdstate()等;
    • 最简单常用的判断方法是 if(cin>>c),if(cin.getline(..))

    cin清空输入缓冲区:

    • 为了避免上一次操作在缓冲区中残留数据导致下一次的输入受影响,可以手动清空缓冲区;
    • cin.ignore(numeric_limits < std::streamsize > ::max(), ' ')清除缓冲区的当前行(包括换行符),不指定delim时默认清除整个cin缓冲区.

    2 流操纵算子

    • 使用流操纵算子需要包含头文件iomanip
    • 整数流的基数:流操纵算子dec,oct,hex,setbase
    • 控制浮点数精度precision,setprecision
    • 设置浮点数小数点位置固定与非固定setiosflags(ios::fixed),resetiosflags(ios::fixed)
    • 设置域宽setw, width(宽度设置有效性是一次性的),以及宽度不足的填充方式setfill()
    • 也可以自定义流操纵算子

    3 文件读写

    文件打开后一定要及时关闭,读取文件之前,要把该文件的写入流关闭,否则出错。

    3.1 创建文件

    ifstream  //文件读取流
    ofstream  //文件写入流
    fstream   //文件读写流
    
    //创建流对象时直接关联文件
    ofstream fout("clients.dat", ios::out|ios::binary);
    //先创建对象,再打开文件
    ofstream fout;
    fout.open("clients.dat", ios::out|ios::binary);
    
    //判断打开是否成功
    if(!fout){
        cout << “File open error!”<<endl;
    }
    
    //第一个参数:(Windows下)文件的绝对路径和相对路径
    "c:\tmp\mydir\some.txt"   //绝对路径
    "\tmp\mydir\some.txt"   //当前盘符的根目录下的tmpdirsome.txt
    "tmp\mydir\some.txt"   //当前文件夹的tmp子文件夹里面的…
    "..\tmp\mydir\some.txt"   //当前文件夹的父文件夹下面的tmp子文件夹里面的…
    
    //第二个参数:文件打开方式
    ios::in  //只读
    ios::out  //只写
    ios::app  //追加模式,从文件末尾开始写,防止丢失文件中原来就有的内容
    ios::ate  //打开一个文件时,将位置移动到文件尾
    ios::binary  //二进制模式
    ios::nocreate  //打开一个文件时,如果文件不存在,不创建文件
    ios::noreplace  // 打开一个文件时,如果文件不存在,创建该文件
    ios::trunc   // 打开一个文件,然后清空内容
    

    3.2 文件的读写指针

    相关函数:

    • f.seekp 定位写指针
    • f.seekg 定位读指针
    • f.tellp 获取写指针的位置
    • f.tellg 获取读指针的位置

    文件指针位置在c++中的用法:

    • ios::beg // 文件头
    • ios::end // 文件尾
    • ios::cur // 当前位置
    //文件的读写指针
    ofstream fout("a1.out", ios::app); //以添加方式打开
    long location = fout.tellp();  //取得写指针的位置
    location = 10;
    fout.seekp(location);  // 将写指针移动到第10个字节处
    fout.seekp(location, ios::beg); //从头数location
    fout.seekp(location, ios::cur); //从当前位置数location
    fout.seekp(location, ios::end); //从尾部数location,location可以为负值
    
    ifstream fin(“a1.in”,ios::ate); //打开文件,定位文件指针到文件尾
    long location = fin.tellg(); //取得读指针的位置
    fin.seekg(10); // 将读指针移动到第10个字节处
    

    3.3 字符文件读写

    因为文件流也是流,所以流的成员函数和流操作算子也同样适用于文件流。

    //写一个程序,将文件 in.txt 里面的整数排序后,输出到out.txt
    //原in.txt的内容为:1 234 9 45 6 879
    //执行后out.txt内容为:1 6 9 45 234 879
    
    #include <iostream>
    #include <fstream>
    #include <vector>
    #include <algorithm>
    using namespace std;
    
    int main() {
        vector<int> v;
        ifstream srcFile("in.txt", ios::in);
        ofstream destFile("out.txt", ios::out);
        int x;
        while(srcFile >> x)
            v.push_back(x);
        sort(v.begin(),v.end());
        for(int i=0; i<v.size(); i++)
            destFile << v[i] << " ";
        destFile.close(); //文件要及时关闭
        srcFile.close();
        return 0;
    }
    

    3.4 二进制文件读写

    • 读取二进制文件用 istream& read(char* s, long n);
    • 写入二进制文件用 istream& write(const char* s, long n);
    • 前者为ifstream和fstream的成员函数,将文件内容读入地址s处的内存中;后者为ofstream和fstream的成员函数,将内存s处的内容写入文件中;读/写指针均随之移动。
    • 读写指针实际是一个指针,在fstream对象打开的文件中,尽量显式地说明指针的位置。
    //示例1:从键盘输入几个学生的姓名的成绩,并以二进制文件形式保存、读取、修改
    #include <iostream>
    #include <cstring>
    #include <fstream>
    using namespace std;
    
    struct Student {
        char name[20];
        int score;
    };
    int main(){
        Student s;
        //创建写入流文件,写入数据
        ofstream outFile( "students.dat", ios::out|ios::binary);
        if(!outFile){
            cerr << "error" << endl;
            return 0;
         }
        cout << "输入学生信息,格式示例:Jane 68" << endl;
        while(cin >> s.name >> s.score)
            outFile.write((char*)&s, sizeof(s));
        outFile.close(); //需要及时关闭
    
        //创建读取流文件,读取数据
        ifstream inFile("students.dat", ios::in|ios::binary );
        if(!inFile){
            cerr << "error" << endl;
            return 0;
         }
        cout << endl;
        while(inFile.read((char* )&s, sizeof(s))){
            int readedBytes = inFile.gcount(); //看刚才读了多少字节
            cout << s.name << " " << s.score << endl;
         }
        inFile.close();
    
        //将students.dat文件的第三个学生名字改成Mike
        fstream ioFile("students.dat", ios::in|ios::out|ios::binary);
        if(!ioFile){
            cout << "error";
            return 0;
         }
        ioFile.seekp(2 * sizeof(s), ios::beg); //定位写指针到第三个记录
        ioFile.write("Mike", strlen("Mike")+1);
        ioFile.seekg(0, ios::beg); //定位读指针到开头,需要显式地说明位置
        cout << endl;
        while(ioFile.read((char* )&s, sizeof(s)))
            cout << s.name << " " << s.score << endl;
        ioFile.close();
    
        return 0;
    }
    
    
    //示例2:文件拷贝程序mycopy,将src.dat拷贝到dest.dat
    //用法示例:mycopy src.dat dest.dat
    #include <iostream>
    #include <fstream>
    using namespace std;
    
    int main(int argc, char * argv[]){
        if(argc != 3){
            cout << "File name missing!" << endl;
            return 0;
         }
        ifstream inFile(argv[1], ios::binary|ios::in); //打开文件用于读
        if(!inFile ){
            cout << "Source file open error." << endl;
            return 0;
         }
        ofstream outFile(argv[2], ios::binary|ios::out); //打开文件用于写
        if(!outFile){
            cout << "New file open error." << endl;
            inFile.close(); //打开的文件一定要关闭
            return 0;
         }
        char c;
        while(inFile.get(c)) //每次读取一个字符
            outFile.put(c); //每次写入一个字符
        outFile.close();
        inFile.close();
        return 0;
    }
    

    3.5 二进制文件和文本文件的区别

    不同系统下换行符的表示不同:

    • Linux,Unix下的换行符号:‘ ’(ASCII码: 0x0a)
    • Windows 下的换行符号:‘ ’(ASCII码: 0x0d0a) endl 就是 ' '
    • Mac OS下的换行符号:‘ ’(ASCII码:0x0d)

    导致不同系统下二进制文件和文本文件的区别:

    • Linux, Mac OS文本文件在Windows记事本中打开时不换行。
    • Unix/Linux下打开文件,用不用 ios::binary 没区别。
    • Windows下打开文件,如果不用 ios::binary,则读取文件时,所有的 ' ’会被当做一个字符' '处理,即少读了一个字符' ';写入文件时,写入单独的' '时,系统自动在前面加一个' ',即多写了一个' '。

    4 其余相关:缓冲区和缓存

    https://www.cnblogs.com/mlgjb/p/7991903.html

    缓冲区(buffer)

    • 缓冲区就是一块内存区,它用在输入输出设备和CPU之间,用来存储数据。它使得低速的输入输出设备和高速的CPU能够协调工作,避免低速的输入输出设备占用CPU,解放出CPU,使其能够高效率工作。
    • 缓冲区的三种类型:全缓冲、行缓冲和不带缓冲。
    • 缓冲区的刷新:当缓冲区满或关闭文件时自动刷新,还可以显式刷新(endl、flush、ends)。

    缓存(cache):

    • 缓存是个很大的概念,有CPU的缓存、浏览器的缓存等。
    • CPU的Cache是用来解决CPU与内存之间速度不匹配的问题,避免内存与辅助内存频繁存取数据,提高系统的执行效率。

    缓冲区和缓存的区别

    • Buffer的核心作用是用来缓冲,缓和冲击。比如你每秒要写100次硬盘,对系统冲击很大,浪费了大量时间在忙着处理开始写和结束写这两件事。用个buffer暂存起来,变成每10秒写一次硬盘,对系统的冲击就很小。一方面提高效率,一方面减少对硬盘的访问。
    • Cache的核心作用是加快取用的速度。比如你一个很复杂的计算做完了,下次还要用结果,就把结果放手边一个好拿的地方存着,下次不用再算了。加快了数据取用的速度。
    • 简单来说就是buffer偏重于写,而cache偏重于读。
  • 相关阅读:
    centos8上添加sudoer用户
    centos平台scp通过密钥远程复制文件(免密登录)
    使用 TestNG 并发测试 ;
    maven 添加tomcat依赖
    maven Web项目中POM的配置信息
    找xpath好用的工具(比较少用,针对只能在IE上打开的网站)
    maven 实践 :管理依赖
    maven将依赖打入jar包
    maven scope含义的说明
    Maven编译打包出错:找不到符号
  • 原文地址:https://www.cnblogs.com/inchbyinch/p/12398454.html
Copyright © 2011-2022 走看看