C++输入输出和文件

文章目录

  • 一. 流, 缓冲区和iostream文件
  • 二. 使用cout进行输出
    • 1. 用cout进行格式化
    • 2. 刷新输出缓冲区
  • 三. 使用cin进行输入
    • 1. cin>>如何检查输入
    • 2. 流状态
    • 3. 其他istream类方法
  • 四. 文件输入和输出
    • 1. 简单的文件I/O
    • 2. 文件模式
    • 3. 随机存取
    • 4. 内核格式化
  • To be continue...


一. 流, 缓冲区和iostream文件

  • C++输入输出和文件_第1张图片

  • C++输入输出和文件_第2张图片

  • C++输入输出和文件_第3张图片

  • streambuf 类为缓冲区提供了内存,并提供了用于填充缓冲区、访问缓冲区内容、刷新缓冲区和管理缓冲区内存的类方法;

  • ios_base 类表示流的一般特征,如是否可读取、是二进制流还是文本流等;

  • ios 类基于ios_base,其中包括了一个指向streambuf对象的指针成员;

  • ostream 类是从ios类派生而来的,提供了输出方法;

  • istream 类也是从ios类派生而来的,提供了输入方法;

  • iostream 类是基于istream和ostream类的,因此继承了输入方法和输出方法。

  • cin对象对应于标准输入流。在默认情况下,这个流被关联到标准输入设备(通常为键盘)。wcin对象与此类似,但处理的是wchar_t类型。

  • cout对象与标准输出流相对应。在默认情况下,这个流被关联到标准输出设备(通常为显示器)。wcout对象与此类似,但处理的是wchar_t类型。

  • cerr对象与标准错误流相对应,可用于显示错误消息。在默认情况下,这个流被关联到标准输出设备(通常为显示器)。这个流没有被缓冲,这意味着信息将被直接发送给屏幕,而不会等到缓冲区填满或新的换行符。wcerr对象与此类似,但处理的是wchar_t类型。

  • clog对象也对应着标准错误流。在默认情况下,这个流被关联到标准输出设备

  • C++的iostream类库管理了很多细节。例如,在程序中包含iostream文件将自动创建8个流对象(4个用于窄字符流,4个用于宽字符)
  • ios_base类中的成员和方法以前位于ios类中。现在,ios_base是ios的基类。在新系统中,ios是包含char和wchar_t实体化的模块板,而ios_base包含了非模板特性。

二. 使用cout进行输出

1. 用cout进行格式化

[1].修改显示时使用的计数系统

  • hex(cout);
    //或者
    cout << hex;
    //示例:
    cout << hex <<10; //以16进制格式输出十进制10

    完成上述设置后,程序将以十六进制形式打印整数值,直到将格式状态设置为其他选项为止。
    >.hex不是成员函数
    oct dec 对应八进制和十进制 用法同上

[2].调整字段宽度

  • int width();
    int width(int i);

    第一种格式返回字段宽度的当前设置;第二种格式将字段宽度设置为 i 个空格,并返回以前的字段宽度值。这使得能够保存以前的值,以便以后恢复宽度值时使用

  • width()方法只影响显示的下一个项目,然后字段宽度将恢复为默认值。

  • 由于width()是成员函数, 因此必须使用对象来调用它(这里是cout) .

    cout << '#';
    cout.width(12);
    cout << 12 << "#" << 24 << "#\n";
    

    输出:

    #     12#24#
    
  • 默认右对齐

  • C++永远不会截短数据,如果在字段宽度为2的时候打印7个字段的值, C++将增宽字段, 以容纳该数据. C++视内容重于形式.

[3].填充字符

  • cout.fill(‘*’); //用 * 填充空白字符

  • 示例:

    cout << '#';
    cout.width(12);
    cout.fill('*');
    cout << 12 << "#" << 24 << "#\n";
    cout.width(5);
    cout << "%%%" << endl;
    

    输出:

    #*****12#24#
    **%%%
    
  • 将一直有效, 直到更改 .

[4].设置浮点数的显示精度

  • cout.precision(2);

  • 在默认模式下, 它指的是总位数; 在定点模式和科学计数模式下,指的是小数点后面的位数(稍后讨论) C++的默认精度为6位 (但末尾的0不显示)

  • 一直有效, 直到更改.

  • 示例:

    #include 
    int main()
    {
        using std::cout;
        float price1 = 20.40;
        float price2 = 1.9 + 8.0 / 9.0;
        cout << "\"Furry Friends\" is $" << price1 << "!\n";
        cout << "\"Fiery Fiends\" is $" << price2 << "!\n";
    
        cout.precision(2);
        cout << "\"Furry Friends\" is $" << price1 << "!\n";
        cout << "\"Fiery Fiends\" is $" << price2 << "!\n";
        return 0;
    }
    

    输出:

    "Furry Friends" is $20.4!
    "Fiery Fiends" is $2.78889!
    "Furry Friends" is $20!
    "Fiery Fiends" is $2.8!
    

[5].打印末尾的0和小数点

  • 有时候呢保留末尾的0将更加美观, 比如 $20.40 比 $20.4 更加美观
    ios_base类提供了一个函数setf (用于set 标记) 这个类还定义了多个静态常量,可以作为该函数的参数

    cout.setf(ios_base::showpoint);
    
  • 上述语句将导致末尾的0被显示出来; 如果使用默认精度为6, 那么2.00 将被显示为2.00000

[6].再谈setf

  • setf()函数有两个原形. 第一个为:

    fmtflags
    setf(fmtflags __fmtfl)
    {
      fmtflags __old = _M_flags; // _M_flags 作为ios_base的成员变量,用来记录当前的标记
      _M_flags |= __fmtfl;
      return __old;   //返回以前的标记设置;
    }
    

    fmtflags是这样被定义的类型: typedef _Ios_Fmtflags fmtflags;
    _Ios_Fmtflags 是这样被定义的:

    enum _Ios_Fmtflags 
      { 
        _S_boolalpha 	= 1L << 0,
        _S_dec 		= 1L << 1,
        _S_fixed 		= 1L << 2,
        _S_hex 		= 1L << 3,
        _S_internal 	= 1L << 4,
        _S_left 		= 1L << 5,
        _S_oct 		= 1L << 6,
        _S_right 		= 1L << 7,
        _S_scientific 	= 1L << 8,
        _S_showbase 	= 1L << 9,
        _S_showpoint 	= 1L << 10,
        _S_showpos 	= 1L << 11,
        _S_skipws 	= 1L << 12,
        _S_unitbuf 	= 1L << 13,
        _S_uppercase 	= 1L << 14,
        _S_adjustfield 	= _S_left | _S_right | _S_internal,
        _S_basefield 	= _S_dec | _S_oct | _S_hex,
        _S_floatfield 	= _S_scientific | _S_fixed,
        _S_ios_fmtflags_end = 1L << 16,
        _S_ios_fmtflags_max = __INT_MAX__,
        _S_ios_fmtflags_min = ~__INT_MAX__
      };
    

    对位进行跟踪好像很乏味, 然而我不必做这项工作, ios_base类提供了代表位值的常量:

    常 量

    含 义

    ios_base ::boolalpha

    输入和输出bool值,可以为true或false

    ios_base ::showbase

    对于输出,使用C++基数前缀(0,0x)

    ios_base ::showpoint

    显示末尾的小数点

    ios_base ::uppercase

    对于16进制输出,使用大写字母,E表示法

    ios_base ::showpos

    在正数前面加上+

  • 第二个seft()函数原形接受两个参数,并返回以前的设置:

    fmtflags
    setf(fmtflags __fmtfl, fmtflags __mask)
    {//__mask称为掩码 通常这块位域全部填充1;
      fmtflags __old = _M_flags;   //_M_flags中记录着上次的标记情况
      _M_flags &= ~__mask;         //把_M_flags中的关于这块位域的标志位清零
      _M_flags |= (__fmtfl & __mask);  //两个为1结果才为1 在这块标志位域内去除其他标志 最后添加到总标记_M_flags里
      return __old;
    }
    

    第一参数和以前一样,也是一个包含了所需设置的fmtflags值。第二参数指出要清除第一个参数中的哪些位。假定以将第3位设置为1表示以10为基数,将第4位设置为1表示以8为基数,将第5位设置为1表示以16为基数。假设输出是以10为基数的,而要将它设置为以16为基数,则不仅需要将第5位设置为1,还需要将第3位设置为0一一这叫作清除位(清除位)。
    下面的函数调用与使用16进制控制符作用相同:

    cout.setf(ios_base::hex, ios_base::basefield);
    

    对位进行跟踪好像很乏味, 然而我不必做这项工作, ios_base类提供了一些掩码的常量:

    第二个参数

    第一个参数

    含 义

    ios_base ::basefield

    ios_base ::dec

    使用基数10

    ios_base ::oct

    使用基数8

    ios_base ::hex

    使用基数16

    ios_base ::floatfield

    ios_base ::fixed

    使用定点计数法

    ios_base ::scientific

    使用科学计数法

    ios_base ::adjustfield

    ios_base ::left

    使用左对齐

    ios_base ::right

    使用右对齐

    ios_base ::internal

    符号或基数前缀左对齐,值右对齐

    在C++标准中,定点表示法和科学表示法都有下面两个特征:

    • 精度表示的是小数位数, 而不是总位数;
    • 显示末尾的0;
  • 调用setf()的效果可以通过 unsetf()消除

    void
    unsetf(fmtflags __mask)
    { _M_flags &= ~__mask; }//不管是掩码还是单个标志位的 都适用
    

    示例:

    cout.unsetf(ios_base::floatfield); // go to default mode
    

    如果已知cout处于定点状态,则可以使用参数ios_base::fxed调用函数unsetf()来切换到默认模式。而无论cout的当前状态如何,使用参数ios_base::floatfield调用函数unsetf()都将切换到默认模式,是一种更好的选择
    其他方法:

    cout.setf(0, ios_base::floatfield); // go to default mode
    

[7].标准控制符

  • C++提供了多个控制符,能够调用setf(),并自动提供正确的参数。
    前面介绍过hex oct dec 用法都类似
    例如,下面的语句使用左对齐和定点表示法:

    cout << left << fixed;
    

    下面罗列出一些标准控制符

    控 制 符

    调 用

    boolalpha

    setf(ios_base::boolalpha)

    noboolalpha

    unsetf(ios_base::boolalpha)

    showbase

    setf(ios_base::showbase)

    noshowbase

    unsetf(ios_base::showbase)

    showpoint

    setf(ios_base::showpoint)

    noshowpoint

    unsetf(ios_base::showpoint)

    showpos

    setf(ios_base::showpos)

    noshowpos

    unsetf(ios_base::showpos)

    uppercase

    setf(ios_base::uppercase)

    nouppercase

    unsetf(ios_base::uppercase)

    internal

    setf(ios_base::internal,ios_base::adjustfield)

    left

    setf(ios_base::left,ios_base::adjustfield)

    right

    setf(ios_base::right,ios_base::adjustfield)

    dec

    setf(ios_base::dec,ios_base::basefield)

    hex

    setf(ios_base::hex,ios_base::basefield)

    oct

    setf(ios_base::oct,ios_base::basefield)

    fixed

    setf(ios_base::fixed,ios_base::floatfield)

    scientific

    setf(ios_base::scientific,ios_base::floatfield)

    如果你的系统不支持, 仍然可以使用 setf();

[8]. 头文件iomanip

  • 有时候使用cout.fill(‘*’); cout.precision(5); cout.width(3); 这样使用不太方便
    C++在iomanip中提供了一些其他的控制符, 如第 [7] 小结那样, 表示起来更方便, 只是这些控制符带参数
    3个最常用的控制符分别是setprecision() setfill() setw() 对参数的用法如同上面三个
    示例:

    #include 
    #include 
    #include 
    int main()
    {
        using namespace std;
        // use new standard manipulators
        cout << fixed << right;
    
        // use iomanip manipulators
        cout << setw(6) << "N" << setw(14) << "square root"
             << setw(15) << "fourth root\n";
    
        double root;
        for (int n = 10; n <= 100; n += 10)
        {
            root = sqrt(double(n));
            cout << setw(6) << setfill('.') << n << setfill(' ')
                 << setw(12) << setprecision(3) << root
                 << setw(14) << setprecision(4) << sqrt(root)
                 << endl;
        }
        system("pause");
        return 0;
    }
    

    输出:

         N   square root   fourth root
    ....10       3.162        1.7783
    ....20       4.472        2.1147
    ....30       5.477        2.3403
    ....40       6.325        2.5149
    ....50       7.071        2.6591
    ....60       7.746        2.7832
    ....70       8.367        2.8925
    ....80       8.944        2.9907
    ....90       9.487        3.0801
    ...100      10.000        3.1623
    Press any key to continue . . .
    

2. 刷新输出缓冲区

  • 如果实现不能在所希望时刷新输出,可以使用两个控制符中的一个来强行进行刷新。控制符flush刷新缓冲区,而控制符endl刷新缓冲区并插入一个换行符.

    cout << "Hello, good-looking! " << flush; //or flush(cout);
    cout << "Wait just a moment, please." << endl;
    

三. 使用cin进行输入

1. cin>>如何检查输入

  • cin>>查看输入流时跳过并丢弃开头的空白(空格,制表符和换行符), 直到遇到非空白字符.

  • 非单字符情况下 它读取 从非空白字符开始 到 与目标类型不匹配(边界符不算)的第一个字符之间 的 全部内容

  • 后面的空格被视为边界符并留在输入流, 下一个cin>> 又遵循第一条规则

  • 所以连续使用>>时,像是:以空格为分隔符 分别存储到各变量 (抽取并丢弃空格)

  • 使用示例:

    #include 
    #include 
    #include 
    int main()
    {
        using namespace std;
        cout << "Enter numbers: ";
        int input;
        int residual;
        cin >> input;
        cout << "input = " << input << endl;
        cin >> residual;
        cout << "residual = " <<  residual << endl;
        system("pause");
        return 0;
    }
    

    正常输入时的情况:

    Enter numbers: 210 10
    input = 210
    residual = 10
    Press any key to continue . . .

    • 第二个cin不会等待用户输入, 而是抽取上次残留在输入流中的10

    非适当输入的情况:

    Enter numbers: 1230a
    input = 1230
    residual = 0
    Press any key to continue . . .

    • 最后一个可接受的字符是0, a将留在输入流中, 下一个cin语句将从这里读取.
    • 读取时发现不符合要求, 返回0 并设置流状态;

2. 流状态

  • cin或cout对象包含一个描述流状态(流状态)的数据成员(从ios_base类那里继承的)。流状态(被定义为iostate类型,而iostate是一种bitmask类型)。3个ios_base元素组成:eofbitbadbitfailbit,其中每个元素都是一位,可以是1或0。当cin操作到达文件未尾时,它将设置eofbit,当cin操作未能读取到预期的字符时(像前一个例子那样),它将设置failbit。I/O失败(如试图读取不可访问的文件或试图写入开启写保护的磁盘),也可能将failbit设置为1。在一些无法诊断的失败破坏流时,badbit元素将被设置(实现没有必要就哪些情况下设置failbit,哪些情况下设置badbit达成一致)。当全部3个状态位都设置为0时,说明一切顺利。程序可以检查流状态的。这种信息来决定下一步做什么。

  • 这个表述流状态的成员在ios_base.h被定义为 iostate _M_streambuf_state;

  • iostate 类型被定义为typedef _Ios_Iostate iostate

    enum _Ios_Iostate
      { 
        _S_goodbit 		= 0,
        _S_badbit 		= 1L << 0,
        _S_eofbit 		= 1L << 1,
        _S_failbit		= 1L << 2,
        _S_ios_iostate_end = 1L << 16,
        _S_ios_iostate_max = __INT_MAX__,
        _S_ios_iostate_min = ~__INT_MAX__
      };
    
  • cin或cout对象包含描述各种流状态(流状态)的静态常量(从ios_base类那里继承的)

    static const iostate badbit =	_S_badbit;
    static const iostate eofbit =	_S_eofbit;
    static const iostate failbit =	_S_failbit;
    static const iostate goodbit =	_S_goodbit;
    
  • 下表列出了这些位和一些报告或改变流状态的ios_base方法。

    成 员 描 述
    eofbit 如果到达文件尾,则_M_streambuf_state 中表示该流状态的bit位设置为1 (下面同理简写)
    badbit 如果流被破坏,则设置为1;例如,文件读取错误
    failbit 如果输入操作未能读取预期的字符或输出操作没有写入预期的字符,则设置为1
    goodbit 另一种表示0的方法
    good() 如果流可以使用(所有的位都被清除),则返回true
    eof() 如果eofbit被设置,则返回true
    bad() 如果badbit被设置,则返回true
    fail() 如果badbit或failbit被设置,则返回true
    rdstate() 返回流状态 (_M_streambuf_state)
    exceptions() 返回一个位掩码,指出哪些标记导致异常被引发
    exceptions(isostate ex) 设置哪些状态将导致clear()引发异常;例如,如果ex是eofbit,则如果eofbit被设置,clear()将引发异常
    clear(iostate s) 将流状态设置为s;s的默认值为0(goodbit);如果(rdstate()& exceptions())为true,则引发异常basic_ios::failure
    setstate(iostate s) 调用clear(rdstate()|s)。这将设置与s中设置的位对应的流状态位,其他流状态位保持不变
  • 设置流状态位有一个非常重要的后果:流将对后面的输入或输出关闭
    如果希望程序在流状态位被设置后能够读取后面的输入,就必须将流状态重置为良好。这可以通过调用clear()方法来实现

[1]. 设置流状态

  • 下面的调用将使用默认参数0,将三个状态位全部归0 (eofbit、badbit和failbit)

    clear();
    
  • 下面的调用将状态设置为eofbit 其他两个状态位被清除

    clear(ios_base::eofbit);
    
  • 你也可以这样调用:下面两个状态位被设置 其他被清除

    clear(ios_base::eofbit | ios_base::badbit);
    
  • 而setstate()方法只影响其参数中的位。因此,下面的调用将设置eofbit,而不会影响其他位:

    setstate(ios_base::eofbit);
    
  • setstate() 函数原形:

    void
    setstate(iostate __state)
    { this->clear(this->rdstate() | __state); }
    

[2]. I/O和异常

  • exceptions(isostate ex) 原形:

    void
    exceptions(iostate __except)
    {
      _M_exception = __except;
      this->clear(_M_streambuf_state); //当即就确认是否有相应标志位被设置
    }
    
  • exceptions() 原形:

    iostate
    exceptions() const
    { return _M_exception; }
    
  • clear(iostate s) 函数体:

    void
    clear(iostate __state)
    {
      if (this->rdbuf())    //结果通常为true
    _M_streambuf_state = __state;
      else
    _M_streambuf_state = __state | badbit;
      if (this->exceptions() & this->rdstate())
    __throw_ios_failure(__N("basic_ios::clear"));
    }
    
  • _M_exception 的默认设置为 goodbit, 也就是说默认不会抛出异常.
    但重载的 exceptions(iostate) 函数能够控制其行为
    如果 (_M_exception & _M_streambuf_state)为 true, clear()将引发ios_base::failure异常
    ios_base::failure 类是从 std::exceptions 类派生而来的, 因此包含一个 what() 方法

  • 示例:

    #include 
    #include 
    int main()
    {
        using namespace std;
        // have failbit cause an exception to be thrown
        cin.exceptions(ios_base::failbit);
        cout << "Enter numbers: ";
        int input;
        try {
            while (cin >> input); 
        } catch(ios_base::failure & bf)
        {
            cout << bf.what() << endl;
            cout << "O! the horror!\n";
        }
        return 0;
    }
    

    运行:

    Enter numbers: 10a
    basic_ios::clear: iostream error
    O! the horror!

[3]. 流状态的影响

  • 只有在流状态良好(所有的位都被清除)的情况下,下面的测试才返回true;

    while(cin>>input);
    
  • 如果测试失败,可以用 eof() fail() bad() 来判断可能的原因

  • 用 while(cin.get() != ‘\n’) 清除输入流残留的部分 (在你输入回车它才能恢复挂起状态时可用)

  • 用clear()来重新打开流

    int input, sum;
    while (cin >> input)
    {
        sum += input;
    }
    cout << "Last value entered = " << input << endl;
    cout << "Sum = " << sum << endl;
    
    if (cin.fail() && !cin.eof() ) // failed because of mismatched input
    {
        cout << "cin.fail():true" << endl;
        cin.clear(); // reset stream state
        while (cin.get() != '\n')
        {
            continue;    // get rid of bad input
        }
    }
    else // else bail out
    {
        cout << "I cannot go on!\n";
        system("pause");
        exit(1);
    }
    cout << "Now enter a new number: ";
    cin >> input; // will work now
    system("pause");
    

3. 其他istream类方法

[1]. 单字符输入

  • 特 征

    cin.get(ch)

    ch = cin.get()

    传输输入字符的方法

    赋给参数ch

    将函数返回值赋给ch

    字符输入时函数的返回值

    指向istream对象的引用

    字符编码(int值)

    达到文件尾时函数的返回值

    转换为false

    EOF

  • get(char&)get(void) 会抽取空白字符

[2]. 字符串输入

  • istream & get(char *, int, char);
    istream & get(char *, int);
    istream & getline(char *, int, char);
    istream & getline(char *, int);
    
  • 第一个参数是用于放置输入字符串的内存单元的地址。第二个参数比实际要读取的最大字符数大1(另外的一个字符用于存储结尾的空字符,以便将输入存储为一个字符串) 。第三个参数指定使用边界符的字符,**只有两个参数的版本将换行符设置为边界符。**上述函数都读取最大数目的字符或遇到换行符后

  • 字符串 get() 和 getline() 的主要区别在于,
    字符串 get() 不会将 边界符 放入第一个参数内, 而是留在输入流中
    而 getline() 抽取并丢弃 边界符(如果到了边界)

  • 示例:

    #include 
    #include 
    int main()
    {
        using namespace std;
        int input, sum;
        char ch = -1;
        cout << "Now enter: ";
    
        char line[50];
        cin.getline(line, 50, '$'); //设置边界符为 $
        cin.get(ch);
        if (ch == '\n')
        {
            cout << "\\n" << endl;
        }
        else if (ch == ' ')
        {
            cout << "space" << endl;
        }
        else
        {
            cout << ch << endl;
        }
        cout << line << endl;
        system("pause");
        return 0;
    }
    

    运行:
    >.加粗的部分为用户输入的内容

    Now enter:abcde
    fgh$zsm

    z
    abcde
    fgh
    Press any key to continue . . .

    如果把上面代码中的 getline 换成 get 并输入同样的内容:

    Now enter:abcde
    fgh$zsm

    $
    abcde
    fgh
    Press any key to continue . . .

  • **ignore()**有两个参数, 第一个指定要读取的最大字符数, 第二个用作边界符;
    抽取输入流中的你指定的字符个数, 连同你指定的边界符一起丢弃(如果到了边界);

    istream & ignore(int = 1, int = EOF);
    

[3]. 意外字符串输入

  • 如果调用 字符串get() 时传入空值(直接输入回车 或边界符再回车) 它们使用setstate()设置failbit
  • 空值并不会导致 getline()设置 failbit

    方 法

    行 为

    getline(char *, int)

    如果没有读取任何字符(但换行符被视为读取了一个字符(尽管不会放入第一个参数中)),则设置failbit
    如果读取了最大数目的字符,且行中还有其他字符,则设置failbit

    get(char *, int)

    如果没有读取任何字符,则设置failbit

[4]. 其他方法

  • peek() gcount() putback()
  • peek() 返回输入流中的下一个字符, 但不抽取输入流中的字符
  • gcount() 返回最后一个非格式化抽取方法读取的字符数
    这意味着字符是由get() getline() ignore() read()方法读取的, 而不是抽取运算符>>读取的
  • putback() 在输入流前端插入一个字符

四. 文件输入和输出

1. 简单的文件I/O

  • 要让程序写入文件, 必须这样做:

    1. 创建 一个 ofstream 对像来管理输出流;
    2. 将该对象与特定的文件关联起来;
    3. 像使用cout那样使用该对象, 唯一的不同是输出将进入文件, 而不是屏幕;
    //two statement
    ofstream fout; // create an ofstream object named fout
    fout.open("jar.txt"); // associate fout with jar.txt
    //one statement
    ofstream fout("jar.txt"); // create fout object, associate it with jar.txt
    //以使用cout的方式使用fout
    fout << "Dull Data";
    

    warning :以默认模式打开文件进行输出将自动把文件的长度截短为零,这相当于删除已有内容;

  • 读取文件与写入文件类似:

    // two statements
    ifstream fin; // create ifstream object called fin
    fin.open("jellyjar.txt");    // open jellyjar.txt for reading
    // one statement
    ifstream fis("jamjar.txt"); // create fis and associate with jamjar.txt
    

    现在可以像使用 cin 那样使用 fin 或 fis

    char ch;
    fin >> ch;            // read a character from the jellyjar.txt file
    char buf[80];
    fin >> buf;           // read a word from the file
    fin.getline(buf, 80); // read a line from the file
    string line;
    getline(fin, line);   // read from a file to a string object
    

    使用close方法来显式的关闭到文件的连接:

    fout.close(); // close output connection to file
    fin.close();  // close input connection to file
    

    关闭这样的连接并不会删除流,而只是断开流到文件的连接。然而,流管理装置仍被保留。例如,fin对象与它管理的输入缓冲区;

  • 这里有一个简单的例子:

    #include  // not needed for many systems
    #include 
    #include 
    
    int main()
    {
        using namespace std;
        string filename;
    
        cout << "Enter name for new file: ";
        cin >> filename;
    
        // create output stream object for new file and call it fout
        ofstream fout(filename.c_str());
    
        fout << "For your eyes only!\n";       // write to file
        cout << "Enter your secret number: "; // write to screen
        float secret;
        cin >> secret;
        fout << "Your secret number is " << secret << endl;
        fout.close(); // close file
    
        // create input stream object for new file and call it fin
        ifstream fin(filename.c_str());
        cout << "Here are the contents of " << filename << ":\n";
        char ch;
        while (fin.get(ch)) // read character from file and
        {
            cout << ch;    // write it to screen
        }
        cout << "Done\n";
        fin.close();
        system("pause);
        return 0;
    }
    

2. 文件模式

  • 无论是使用文件名初始化流对象, 还是使用open方法, 都可以提供指定文件模式的第二个参数

    ifstream fin("banjo", mode1); // constructor with mode argument
    ofstream fout();
    fout.open("harp", mode2);     // open() with mode arguments
    

    常 量

    含 义

    ios_base::in

    打开文件,以便读取

    ios_base::out

    打开文件,以便写入

    ios_base::ate

    打开文件,并移到文件尾

    ios_base::app

    追加到文件尾

    ios_base::trunc

    如果文件存在,则截短文件

    ios_base::binary

    二进制文件

    C++和C的文件打开模式

    C++模式

    C模式

    含 义

    ios_base :: in

    "r"

    打开以读取

    ios_base :: out

    "w"

    等价于ios_base :: out | ios_base :: trunc

    ios_base :: out | ios_base :: trunc

    "w"

    打开以写入,如果已经存在,则截短文件

    ios_base :: out | ios_base :: app

    "a"

    打开以写入,只追加

    ios_base :: in | ios_base :: out

    "r+"

    打开以读写,在文件允许的位置写入

    ios_base :: in | ios_base :: out | ios_base::trunc

    "w+"

    打开以读写,如果已经存在,则首先截短文件

    c++mode | ios_base :: binary

    "cmodeb"

    以C++mode(或相应的cmode)和二进制模式打开;例如,ios_base :: in | ios_base :: binary成为“rb”

    c++mode | ios_base :: ate

    "cmode"

    以指定的模式打开,并移到文件尾。C使用一个独立的函数调用,而不是模式编码。例如,ios_base :: in | ios_base :: ate被转换为“r”模式和C函数调用fseek(file, 0, SEEK_END)

    没有列出的组合,如ios_base::in | ios_base::trunc,将禁止文件被打开。 is_open() 方法可检测这种故障

  • 流状态检查和 is_open()

    • 流状态前边已经将过了
    • 新的C++实现提供了一种更好的检查文件是否被打开的方法——is_open()方法;
  • 二进制文件

    • cout.write() 逐字节地复制数据, 而不进行任何转换
    • read()和write()成员函数的功能是相反的。请用read()来恢复用write()写入的数据

3. 随机存取

  • 随机存取指的是直接移动(不是依次移动)到文件的任何位置。

  • 接下来,需要一种在文件中移动的方式。fstream类继承了两个方法:seekg()和seekp(),​​首先将输出指针移到指定的文件位置,稍后将输出指针移到指定的位置的文件位置(实际上,由于fstream类使用缓冲区来存储中间数据 因此,指针指向的是缓冲区中的位置,而不是实际的文件)。也可以将seekg()ifstream对象,将seep()用于oftream对象。下面是seekg()的原型:

    basic_istream& seekg(off_type, ios_base::seekdir);
    basic_istream& seekg(pos_type);
    

    对于char类型的模板具体化, 上面两个原形等同于下面的代码:

    istream & seekg(streamoff, ios_base::seekdir);
    istream & seekg(streampos);
    

    第一个原型定位到离第二个参数指定的文件位置特定距离(单位为字节)的位置;第二个原型定位到离文件开头特定距离(单位为字节)的位置.
    ios_base::seekdir 有三个可能的值:
    ios_base::beg : 相对于文件开始处的偏移量
    ios_base::cur : 相对于当前位置
    ios_base::end : 相对于文件尾

    fin.seekg(30, ios_base::beg); // 30 bytes beyond the beginning
    fin.seekg(-1, ios_base::cur); // back up one byte
    fin.seekg(0, ios_base::end);  // go to the end of the file
    
  • 如果要检查文件指针的当前位置,则对于输入流,可以使用 tellg() 方法,对于输出流,可以使用 tellp() 查看。它们都返回一个表示当前位置的streampos值(以字节为单位,从文件开始处算起) 。创建fstream对象时,输入指针和输出指针将前一后地移动,因此tellg(0)和tellp()返回的值相同。但是,如果使用istream对象来管理输入流,而使用ostream对象来管理同一个文件的输出流,则输入指针和输出指针将各自独立地移动,因此tellg0和tellp()将返回不同的值。

  • 使用临时文件

    • 开发应用程序时,经常需要使用临时文件,这种文件的存在是短暂的,必须受程序控制。您是否考虑过,在C++中如何使用临时文件呢?创建临时文件、复制另一个文件的内容并删除文件其实很简单。首先,需要为临时文件制定一个命名方案,但如何确保每个文件都被指定了实验的文件名呢?cstdio中声明的tmpnam()标准函数可以帮助您

      char* tmpnam( char* pszName );
      
    • tmpnam()函数创建一个临时文件名,将它放在pszName指向的C风格字符串中。常量L_tmpnam和TMP_MAX(两者都是在cstdio中定义的)限制了文件名包含的字符数以及在确保当前目录中不生成重复文件名的情况下tmpnam()可被调用的最多次数。下面是生成10个临时文件名的代码。

      #include 
      #include 
      
      int main()
      {
          using namespace std;
          cout << "This system can generate up to " << TMP_MAX
               << " temporary names of up to " << L_tmpnam
               << " characters.\n";
          char pszName[ L_tmpnam ] = {'\0'};
          cout << "Here are ten names:\n";
          for( int i=0; 10 > i; i++ )
          {
              tmpnam( pszName );
              cout << pszName << endl;
          }
          return 0;
      }
      

      更具体地说,使用tmpnam()可以生成TMP_NAM个不同的文件名,其中每个文件名包含的字符不超过L_tmpnam个。

4. 内核格式化

  •     iostream族 (family) 支持程序与终端之间的I/O, 而fstream族使用相同的接口提供程序和文件之间的I/O. C++库还提供了 sstream 族, 他们使用相同的接口提供程序和string 对象之间的I/O.

  •     头文件sstream定义了一个从ostream类派生而来的ostringstream类 (还有一个基于wostream的wostringstream类, 这个类用于宽字符集). 如果创建了一个ostringstream对象, 则可以将信息写入其中, 它将存储这些信息. 可以将可用于cout的方法用于ostringstream对象. 也就是说,可以这样:

    ostringstream outstr;
    double price = 380.0;
    char * ps = " for a copy of the ISO/EIC C++ standard!";
    outstr.precision(2);
    outstr << fixed;
    outstr << "Pay only CHF " << price << ps << endl;
    

        格式化文本进入缓冲区,在需要的情况下,该对象将使用动态内存分配来增大缓冲区。ostringstream类有一个名为str()的成员函数, 该函数返回一个被初始化被缓冲区的string对象

    string mesg = outstr.str(); // returns string with formatted information
    

    示例:

    #include 
    #include 
    #include 
    int main()
    {
        using namespace std;
        ostringstream outstr; // manages a string stream
    
        string hdisk;
        cout << "What's the name of your hard disk? ";
        getline(cin, hdisk);
        int cap;
        cout << "What's its capacity in GB? ";
        cin >> cap;
        // write formatted information to string stream
        outstr << "The hard disk " << hdisk << " has a capacity of "
                << cap << " gigabytes.\n";
        string result = outstr.str(); // save result
        cout << result;                  // show contents
        system("pause");
        return 0;
    }
    
  •       istringstream类允许使用istream方法族读取 istringstream 对象中的数据,istringstream 对象可以使用 string 对象进行初始化。

    假设facts是一个string对象,则要创建与该字符串相关联的istringstream对象,可以这样做:

    istringstream instr(facts); // use facts to initialize stream
    

    这样,便可以使用istream方法读取instr中的数据。例如,如果instr包含大量字符格式的整数,则可以这样读取它们:

    int n;
    int sum = 0;
    while (instr >> n)
        sum += n;
    

    用法示例:

    #include 
    #include 
    #include 
    int main()
    {
        using namespace std;
        string lit = "It was a dark and stormy day, and "
                     " the full moon glowed brilliantly. ";
        istringstream instr(lit); // use buf for input
        string word;
        while (instr >> word)      // read a word a time
            cout << word << endl;
        system("pause");
        return 0;
    
    }
    

To be continue…

你可能感兴趣的:(C/C++,c++,笔记,windows)