22 double balance; 23
24 cout << setiosflags( ios::left ) << setw( 10 ) << \25 << setw( 13 ) << \26
27 while ( fin >> account >> name >> balance ) 28 outputLine( account, name, balance ); 29
30 return 0; // ifstream destructor closes the file 31 } 32
33 void outputLine( int acct, const char *name, double bal ) 34 {
35 cout << setiosflags( ios::left ) << setw( 10 ) << acct
36 << setw( 13 ) << name << setw( 7 ) << setprecision( 2 ) 37 << resetiosflags( ios::left )
38 << setiosflags( ios::fixed | ios::showpoint ) 39 << bal << '\\n'; 40 }
输出结果:
Account Name Balance i00 Jones 24.98 200 Doe 345.67 300 White 0.00 400 Stone -42.16 500 Rich 224.62
图4.7 读取并打印一个顺序文件
为了按顺序检索文件中的数据,程序通常要从文件的起始位置开始读取数据,然后连续地读取所有的数据,直到找到所需要的数据为止。程序执行中可能需要按顺序从文件开始位置处理文件中的数据好几次。istreatrl类和ostream类都提供成员函数,使程序把“文件位置指针”(file position pointer,指示读写操作所在的下一个字节号)重新定位。这些成员函数是istream类的seekg(“seekget”)和ostream类的seekp(“seek put”)。每个istream对象有个get指针,表示文件中下一个输入
相距的字节数,每个ostream对象有一个put指针,表示文件中下一个输出相距的字节数。下列语句:
fin.seekg( 0 );
将文件位置指针移到文件开头(位置0),连接fin。seekg的参数通常为long类型的整数。 第二个参数可以指定寻找方向,ios::beg(默认)相对于流的开头定位,ios::cur相对于流当前位置定位,ios::end相对于流结尾定位。文件位置指针是个整数值,指定文件中离文件开头的相对位置(也称为离文件开头的偏移量)。下面是一些get文件位置指针的例子:
-66-
// position to the nth byte of fileObject // assumes ios::beg fileObject.seekg( n );
// position n bytes forward in fileObject fileObject.seekg( n, ios::cur );
// position y bytes back from end of fileObject fileObject.seekg( y, ios::end ); // position at end of fileObject fileObject.seekg( o, ios::end );
ostream成员函数seekp也可以进行类似的操作。成员函数tellg和tellp分别返回get和put指针的当前位置。下列语句将get文件位置指针值赋给long类型的变量location。
location = filObject.tellg();
4.5 更新访问文件
4. 4 节介绍了格式化和写入访问文件的数据修改时会有破坏文件中其他数据的危险。例如,如果要把名字\改为\,则不是简单地重定义旧的名字。White的记录是以如下形式写人文件中的:
300 White 0.00
如果用新的名字从文件中相同的起始位置重写该记录,记录的格式就成为: 300 Worthington 0.00
因为新的记录长度大于原始记录的长度,所以从“Worthington\的第二个“0”之后的字符将重定义文件中的下一条顺序记录。出现该问题的原因在于:在使用流插入运算符<<和流读取运算符>>的格式化输人,输出模型中,域的大小是不定的,因而记录的大小也是不定的。例如,7、14、-117、2047和27383都是int类型的值,虽然它们的内部存储占用相同的字节数,但是将它们以格式化文本打印到屏幕上或存储在磁盘上时要占用不同大小的域。因此,格式化输入,输出模型通常不用来更新已有的记录。
也可以修改上述名字,但比较危险。比如,在300 Whlte 0.00之前的记录要复制到一个新的文件中,然后写入新的记录并把300 White 0.00之后的记录复制到新文件中。这种方法要求在更新一条记录时处理文件中的每一条记录。如果文件中一次要更新许多记录,则可以用这种方法。
-67-
第5章 C++的字符串流
5.1 流的继承关系
在C++中,有一个stream这个类,所有的I/O都以这个“流”类为基础的,里面包括了所有的输入输出类,今天我们就来介绍一下sstream.h(字符串流)这个类:
C++引入了ostringstream、istringstream、stringstream这三个类,要使用他们创建对象就必须包含sstream.h头文件。
istringstream类用于执行C++风格的串流的输入操作。 ostringstream类用于执行C风格的串流的输出操作。
strstream类同时可以支持C风格的串流的输入输出操作。
istringstream类是从istream(输入流类)和stringstreambase(c++字符串流基类)派生而来,ostringstream是从ostream(输出流类)和stringstreambase(c++字符串流基类)派生而来,stringstream则是从iostream(输入输出流类)和和stringstreambase(c++字符串流基类)派生而来。 他们的继承关系如下图所示:
5.2 字串流的输入操作
istringstream是由一个string对象构造而来,istringstream类从一个string对象读取字符。
istringstream的构造函数原形如下:
istringstream::istringstream(string str); #include
istringstream istr; istr.str(\
-68-
//上述两个过程可以简单写成 istringstream istr(\ cout << istr.str()<
cout<>b;
cout<
上例中,构造字符串流的时候,空格会成为字符串参数的内部分界,例子中对a,b对象的输入\赋值\操作证明了这一点,字符串的空格成为了整型数据与浮点型数据的分解点,利用分界获取的方法我们事实上完成了字符串到整型对象与浮点型对象的拆分转换过程。
str()成员函数的使用可以让istringstream对象返回一个string字符串(例如本例中的输出操作(cout< 5.3 字串流的输出操作 ostringstream同样是由一个string对象构造而来,ostringstream类向一个string插入字符。 ostringstream的构造函数原形如下: ostringstream::ostringstream(string str); 示例代码如下: #include ostringstream ostr; ostr.str(\如果构造的时候设置了字符串参数,那么增长操作的时候不会从结尾开始增加,而是修改原有数据,超出的部分增长 ostr.put('d'); ostr.put('e'); ostr<<\ string gstr = ostr.str(); cout< system(\ } 在上例代码中,我们通过put()或者左移操作符可以不断向ostr插入单个字符或者是字符串,通过str()函数返回增长过后的完整字符串数据,但值得注意的一点是,当构造的时候对象内已经存在字符串数据的时候,那么增长操作的时候不会从结尾开始增加,而是修改原有数据,超出的部分增长。 对于stringstream了来说,不用我多说,大家也已经知道它是用于C++风格的字符串的输 -69- 入输出的。 stringstream的构造函数原形如下: stringstream::stringstream(string str); 示例代码如下: #include stringstream ostr(\ ostr.put('d'); ostr.put('e'); ostr<<\ string gstr = ostr.str(); cout< system(\ } 5.4 字串流在数据类型转换中的应用 除此而外,stringstream类的对象我们还常用它进行string与各种内置类型数据之间的转换。 示例代码如下: #include stringstream sstr; //--------int转string----------- int a=100; string str; sstr<>str; cout< //--------string转char[]-------- sstr.clear();//如果你想通过使用同一stringstream对象实现多种类型的转换,请注意在每一次转换之后都必须调用clear()成员函数。 string name = \ -70-