C++学习笔记七-流

北战南征 提交于 2020-02-13 09:09:09

C++ 的输入/输出(input/output)由标准库提供。标准库定义了一族类型,支持对文件和控制窗口等设备的读写(IO)。还定义了其他一些类型,使 string 对象能够像文件一样操作,从而使我们无须 IO 就能实现数据与字符之间的转换。这些 IO 类型都定义了如何读写内置数据类型的值。此外,一般来说,类的设计者还可以很方便地使用 IO 标准库设施读写自定义类的对象。类类型通常使用 IO 标准库为内置类型定义的操作符和规则来进行读写。

一、面向对象的标准库:

iostream

istream 从流中读取

ostream 写到流中去

iostream 对流进行读写;从 istreamostream 派生而来

fstream

ifstream 从文件中读取;由 istream 派生而来

ofstream 写到文件中去;由 ostream 派生而来

fstream 读写文件;由 iostream 派生而来

sstream

istringstreamstring 对象中读取;由 istream 派生而来

ostringstream 写到 string 对象中去;由 ostream 派生而来

stringstreamstring 对象进行读写;由 iostream 派生而来

 

二、国际字符的支持:

每一个 IO 头文件都定义了 charwchar_t 类型的类和标准输入/输出对象。基于流的 wchar_t 类型的类和对象在 iostream 中定义,宽字符文件流类型在 fstream 中定义,而宽字符 stringstream 则在 sstream 头文件中定义。

 

三、IO 对象不可复制或赋值:

      1.只有支持复制的元素类型可以存储在 vector 或其他容器类型里。由于流对象不能复制,因此不能存储在 vector(或其他)容器中(即不存在存储流对象的 vector 或其他容器)。

 

      2.形参或返回类型也不能为流类型。如果需要传递或返回 IO 对象,则必须传递或返回指向该对象的指针或引用:

  ofstream &print(ofstream&);              // ok: takes a reference, no copy
    while (print(out2)) { /* ... */ } // ok: pass reference to out2
 
四、条件状态:流必须处于无错误状态,才能用于输入或输出.

检测流是否可用的最简单的方法是检查其真值。

if(cin) // ok to use cin, it is in a valid state

while(cin >> word) // ok: read operation successful…

常量

含义

failbit标记位的值

eofbit标记位的值

badbit标记位的值

转化为10进制

ios::failbit

输入(输出)流出现非致命错误,可挽回

1

0

0

4

ios::badbit

输入(输出)流出现致命错误,不可挽回

0

0

1

2

ios::eofbit

已经到达文件尾

0

1

0

1

ios::goodbit

流状态完全正常

0

0

0

0

分别对应cin.fail(),cin.bad(),cin.eof(),cin.good()

---------------------------------------------------------------------------------

1.cin.sync():    清除缓冲区
2.cin.clear() :   清除cin错误状态
     cin.clear()用法如果输入发生错误发生,那么流状态既被标记为错误,你 必须清除这些错误状态,以使你的程序能正确适当地继续运行。要清除错误状态,需使用clear()函数。
3.cin.ignore(int n,char c)  :

cin.ignore(   5,   'c'   )   的是从输入流(cin)中提取字符,提取的字符被忽略(ignore),不被使用。每抛弃一个字符,它都要计数和比较字符:如果计数值达到5或者被抛弃的字符是'c',则cin.ignore()   函数执行终止;否则,它继续等待。  它的一个常用功能就是用来清除以回车结束的输入缓冲区的内容,消除上一次输入对下一次输入的影响。比如可以这么用:cin.ignore(   1024,   '\n'   );,通常把第一个参数设置得足够大,这样实际上总是只有第二个参数   '\n'   起作用,所以这一句就是把回车(包括回车)之前的所以字符从输入缓冲(流)中清除出去。

4.clear(flag):将流 s 中的某个指定条件状态设置为有效。flag 的类型是 strm::iostate

5.setstate(flag):给流 s 添加指定条件。flag 的类型是 strm::iostate

6.rdstate():返回流 s 的当前条件,返回值类型为 strm::iostate

 

五、多种状态的处理:常常会出现需要设置或清除多个状态二进制位的情况。此时,可以通过多次调用 setstate 或者 clear 函数实现。另外一种方法则是使用按位或(OR)操作符在一次调用中生成“传递两个或更多状态位”的值。按位或操作使用其操作数的二进制位模式产生一个整型数值。对于结果中的每一个二进制位,如果其值为 1,则该操作的两个操作数中至少有一个的对应二进制位是 1。例如:

// sets both the badbit and the failbit
   is.setstate(ifstream::badbit | ifstream::failbit);

 

六、输出缓冲区的管理:每个 IO 对象管理一个缓冲区,用于存储程序读写的数据。

如有下面语句:

  os << "please enter a value: ";//系统将字符串字面值存储在与流 os 关联的缓冲区中。

下面几种情况将导致缓冲区的内容被刷新,即写入到真实的输出设备或者文件

      A.程序正常结束。作为 main 返回工作的一部分,将清空所有输出缓冲区。

      B.在一些不确定的时候,缓冲区可能已经满了,在这种情况下,缓冲区将会在写下一个值之前刷新。

      C.用操纵符显式地刷新缓冲区,例如行结束符 endl。

      D.在每次输出操作执行完后,用 unitbuf 操作符设置流的内部状态,从而清空缓冲区。 可将输出流与输入流关联(tie)起来。在这种情况下,在读输入流时将刷新其关联的输出缓冲区。

 

1.输出缓冲区的刷新:

我们的程序已经使用过 endl 操纵符,用于输出一个换行符并刷新缓冲区。除此之外,C++ 语言还提供了另外两个类似的操纵符。第一个经常使用的 flush,用于刷新流,但不在输出中添加任何字符。第二个则是比较少用的 ends,这个操纵符在缓冲区中插入空字符 null,然后后刷新它:

    cout << "hi!" << flush;      // flushes the buffer; adds no data
    cout << "hi!" << ends;       // inserts a null, then flushes the buffer
    cout << "hi!" << endl;       // inserts a newline, then flushes the buffer

2.unitbuf 操纵符

如果需要刷新所有输出,最好使用 unitbuf 操纵符。这个操纵符在每次执行完写操作后都刷新流:

    cout << unitbuf << "first" << " second" << nounitbuf;

nounitbuf 操纵符将流恢复为使用正常的、由系统管理的缓冲区刷新方式。

3.如果程序崩溃了,则不会刷新缓冲区.为了确保用户看到程序实际上处理的所有输出,最好的方法是保证所有的输出操作都显式地调用了 flushendl

4.将输入和输出绑在一起

当输入流与输出流绑在一起时,任何读输入流的尝试都将首先刷新其输出流关联的缓冲区。标准库将 coutcin 绑在一起,因此语句:

cin >> ival;//导致 cout 关联的缓冲区被刷新.

tie 函数可用 istreamostream 对象调用,使用一个指向 ostream 对象的指针形参。调用 tie 函数时,将实参流绑在调用该函数的对象上。如果一个流调用 tie 函数将其本身绑在传递给 tieostream 实参对象上,则该流上的任何 IO 操作都会刷新实参所关联的缓冲区。

一个 ostream 对象每次只能与一个 istream 对象绑在一起。如果在调用 tie 函数时传递实参 0,则打破该流上已存在的捆绑。

 

七、文件的输入和输出:

1.fstream 头文件定义了三种支持文件 IO 的类型:

A.ifstream,由 istream 派生而来,提供读文件的功能。

B.ofstream,由 ostream 派生而来,提供写文件的功能。

C.fstream,由 iostream 派生而来,提供读写同一个文件的功能。

fstream 类型除了继承下来的行为外,还定义了两个自己的新操作—— openclose,以及形参为要打开的文件名的构造函数。fstreamifstreamofstream 对象可调用这些操作,而其他的 IO 类型则不能调用。

 

2.文件流对象的使用:

要读写文件时,则必须定义自己的对象,并将它们绑定在需要的文件上。假设 ifileofile 是存储希望读写的文件名的 strings 对象,可如下编写代码:

    // construct an ifstream and bind it to the file named ifile
    ifstream infile(ifile.c_str());
    // ofstream output file object to write file named ofile
    ofstream outfile(ofile.c_str());
或者
    ifstream infile;    // unbound input file stream
    ofstream outfile;   // unbound output file stream
    infile.open("in");   // open file named "in" in the current directory
    outfile.open("out"); // open file named "out" in the current directory

调用 open 成员函数将已存在的 fstream 对象与特定文件绑定。为了实现读写,需要将指定的文件打开并定位,open 函数完成系统指定的所有需要的操作。

 

3.C++ 中的文件名:由于历史原因,IO 标准库使用 C 风格字符串而不是 C++ strings 类型的字符串作为文件名。假设要使用的文件名保存在 string 对象中,则可调用 c_str 成员获取 C 风格字符串。

 

4.检查文件打开是否成功:打开文件后,通常要检验打开是否成功,这是一个好习惯:

    // check that the open succeeded
    if (!infile) {
        cerr << "error: unable to open input file: "
             << ifile << endl;
        return -1;
    }
 
5.将文件流与新文件重新捆绑:

fstream 对象一旦打开,就保持与指定的文件相关联。如果要把 fstream 对象与另一个不同的文件关联,则必须先关闭(close)现在的文件,然后打开(open)另一个文件:要点是在尝试打开新文件之前,必须先关闭当前的文件流。open 函数会检查流是否已经打开。如果已经打开,则设置内部状态,以指出发生了错误。接下来使用文件流的任何尝试都会失败。

如果程序员需要重用文件流读写多个文件,必须在读另一个文件之前调用 clear 清除该流的状态。

 

八、文件模式:

   1. 在打开文件时,无论是调用 open 还是以文件名作为流初始化的一部分,都需指定文件模式(file mode)。每个 fstream 类都定义了一组表示不同模式的值,用于指定流打开的不同模式。与条件状态标志一样,文件模式也是整型常量,在打开指定文件时,可用位操作符设置一个或多个模式。文件流构造函数和 open 函数都提供了默认实参设置文件模式。默认值因流类型的不同而不同。此外,还可以显式地以模式打开文件。

in       打开文件做读操作

out      打开文件做写操作

app      在每次写之前找到文件尾

ate      打开文件后立即将文件定位在文件尾

trunc    打开文件时清空已存在的文件流

binary   以二进制模式进行 IO 操作

 

outtruncapp 模式只能用于指定与 ofstreamfstream 对象关联的文件;in 模式只能用于指定与 ifstreamfstream 对象关联的文件。所有的文件都可以用 atebinary 模式打开。ate 模式只在打开时有效:文件打开后将定位在文件尾。以 binary 模式打开的流则将文件以字节序列的形式处理,而不解释流中的字符。

默认时,与 ifstream 流对象关联的文件将以 in 模式打开,该模式允许文件做读的操作:与 ofstream 关联的文件则以 out 模式打开,使文件可写。以 out 模式打开的文件会被清空:丢弃该文件存储的所有数据。从效果来看,为 ofstream 对象指定 out 模式等效于同时指定了 outtrunc 模式。

对于用 ofstream 打开的文件,要保存文件中存在的数据,唯一方法是显式地指定 app 模式打开

 

九、对同一个文件作输入和输出运算:

         1.fstream 对象既可以读也可以写它所关联的文件。fstream 如何使用它的文件取决于打开文件时指定的模式。默认情况下,fstream 对象以 inout 模式同时打开。当文件同时以 inout 打开时不清空。如果打开 fstream 所关联的文件时,只使用 out 模式,而不指定 in 模式,则文件会清空已存在的数据。如果打开文件时指定了 trunc 模式,则无论是否同时指定了 in 模式,文件同样会被清空。

 

        2.模式是文件的属性而不是流的属性:只要调用 open 函数,就要设置文件模式,其模式的设置可以是显式的也可以是隐式的。如果没有指定文件模式,将使用默认值。

 

        3.打开模式的有效组合:

out                打开文件做写操作,删除文件中已有的数据

out | app          打开文件做写操作,在文件尾写入

out | trunc        out 模式相同

in                 打开文件做读操作

in | out           打开文件做读、写操作,并定位于文件开头处

in | out | trunc   打开文件做读、写操作,删除文件中已有的数据

上述所有的打开模式组合还可以添加 ate 模式。对这些模式添加 ate 只会改变文件打开时的初始化定位,在第一次读或写之前,将文件定位于文件末尾处。 用法如下:

//  output mode by default; truncates file named "file1"
    ofstream outfile("file1");
    // equivalent effect: "file1" is explicitly truncated
    ofstream outfile2("file1", ofstream::out | ofstream::trunc);
    //  append mode; adds new data at end of existing file named "file2"
    ofstream appfile("file2", ofstream::app);

 

十、字符串流

    1.iostream 标准库支持内存中的输入/输出,只要将流与存储在程序内存中的 string 对象捆绑起来即可。此时,可使用 iostream 输入和输出操作符读写这个 string 对象。标准库定义了三种类型的字符串流:

  • istringstream,由 istream 派生而来,提供读 string 的功能。

  • ostringstream,由 ostream 派生而来,提供写 string 的功能。

  • stringstream, 由 iostream 派生而来,提供读写 string 的功能。

要使用上述类,必须包含 sstream 头文件。

个人理解:ostringstream相当于把字符串存进去  istringstream相当于从里面把字符取出来

stringstream 的读写操作实际上读写的就是该对象中的 string 对象。这些类还定义了名为 str 的成员,用来读取或设置 stringstream 对象所操纵的 string 值。

注意到尽管 fstreamsstream 共享相同的基类,但它们没有其他相互关系。特别是,stringstream 对象不使用 openclose 函数,而 fstream 对象则不允许使用 str

 

       2.stringstream 对象的一个常见用法是,需要在多种数据类型之间实现自动格式化时使用该类类型。例如,有一个数值型数据集合,要获取它们的 string 表示形式,或反之。sstream 输入和输出操作可自动地把算术类型转化为相应的 string 表示形式,反过来也可以。

    int val1 = 512, val2 = 1024;
    ostringstream format_message;
    // ok: converts values to a string representation
    format_message << "val1: " << val1 << "\n"
                   << "val2: " << val2 << "\n";
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!