跳转至

Chapter8 流和文件


8.1 IO流和流类库

$~$

流(stream):

流指的是数据从一个设备流向另一个设备。

流本质上是一个对象,在使用前被建立,在使用后被删除。数据的输入/输出就是从流中提取数据/向流中插入数据。

$~$

预定义流对象:

对象定义 说明
istream cin 与标准输入设备相关联
ostream cout 与标准输出设备相关联
ostream cerr 与标准错误输出设备相关联(非缓冲方式)
ostream clog 与标准错误输出设备相关联(缓冲方式)

$~$


8.2 IO流类的成员函数

$~$

istream流中的常用成员函数:

class istream: virtual public ios
{
    public:
        istream& operator>>(double &);
        int get();
        istream& get(char* c,int n,char='\n');
        istream& read(char* c,int n);
        istream& ignore(int=1,int=EOF);
        istream& getline(char* c,int n,char='\n');
};

Example

int main()
{
    char c,a[50],s[100];
    cout<<"use get() input char: ";
    while((c=cin.get())!='\n') cout<<c;         //L1
    cout<<endl;
    cout<<"use get(a,10) input char: ";
    cin.get(a,10);                              //L2
    cout<<a<<endl;
    cin.ignore(1);                              //L3
    cout<<"use getline(s,10) input char: ";
    cin.getline(s,10);                         //L4
    cout<<s<<endl;
}

输入输出示例:

use get() input char: abcd 
abcd
use get(a,10) input char: 12345678 
12345678
use getline(s,10) input char: this is a str 
this is a

L1处的cin.get()函数理论上每输入一个字符就应该输出一个字符,但由于输入缓冲区的存在,实际输出被延迟。

L2处的cin.get(a,10)函数从输入流中读取最多9个字符(最后一个字符留给\0),并将其存储在数组a中。如果输入超过9个字符,多余字符会留在缓冲区。如果遇到\n则停止,\n并不会被存储到a上,但是会留在缓冲区中。

L3处的cin.ignore(1)函数会清除缓冲区的第一个字符,这里是\n

L4处的cin.getline(s,10)函数同样从输入流中读取最多9个字符(最后一个字符留给\0),并将其存储在数组s中,直到遇到\n为止。但与cin.get()不同的是,cin.getline()并不会将\n留在缓冲区中。

$~$

ostream流中的常用成员函数:

1
2
3
4
5
6
7
8
class ostream: virtual public ios
{
    public:
        ostream& operator<<(...);
        ostream& operator<<(long);
        ostream& put(char);
        ostream& write(const char*,int);
};

Example

int main()
{
    char c;
    char a[50]="this is a string...";
    cout<<"use get() input char: ";
    while((c=cin.get())!='\n') cout.put(c);     //L1
    cout.put('\n');
    cout.put('t').put('h').put('i').put('s').put('\n');
    cout.write(a,sizeof(a)-1).put('\n');
}

输入输出示例:

use get() input char: how are you!
how are you!
this
this is a string...

$~$

ios类的格式化标志:

ios::skipws         //跳过输入流中的空白字符
ios::left           //左对齐输出
ios::right          //右对齐输出
ios::dec            //十进制输出
ios::oct            //八进制输出
ios::hex            //十六进制输出
ios::showbase       //在输出数据前面显示基数(八进制为0,十六进制为0x)
ios::showpoint      //在输出浮点数时显示小数点和完整小数位,不足补0
ios::uppercase      //在输出十六进制数时使用大写字母
ios::showpos        //在输出正数时显示正号
ios::scientific     //用科学计数法输出浮点数,如[2.122E2]
ios::fixed          //用定点数形式输出浮点数,如[212.2]
ios::unitbuf        //操作完成后立即刷新缓冲区

$~$

ios类的格式化成员函数:

1
2
3
setf(flags)         //设置指定格式
unsetf(flags)       //取消指定格式
setf(flags,filed)   //先清除指定格式,再设置指定格式

其中,flags即为上面提到的ios类的格式化标志。

$~$

ios类设置域宽、精度、填充字符的成员函数:

1
2
3
fill(c)             //设置填充字符为c(默认为空格)
precision(p)        //设置浮点数的精度(数字位数)为p
width(w)            //设置输出宽度为w

Example

int main()
{
    char c[30]="this is string";
    double d=-1234.8976;
    cout.width(30);   cout.fill('*');    cout.setf(ios::left);
    cout<<c<<"----L1"<<endl;
    cout.width(30);   cout.setf(ios::right);
    cout<<c<<"----L2"<<endl;
    cout.width(30);    cout.setf(ios::internal); //数值符号左对齐,数值本身右对齐
    cout<<d<<"----L3"<<endl;
    cout.setf(ios::dec|ios::showbase|ios::showpoint);
    cout.width(30);    
    cout<<d<<"----L4"<<"\n";
    cout.setf(ios::showpoint);    cout.precision(10);
    cout.width(30);
    cout<<d<<"----L5"<<"\n";
    cout.width(30);    cout.setf(ios::oct,ios::basefield);
    cout<<100<<"----L6"<<"\n";
}

输出为:

this is string****************----L1
****************this is string----L2
***********************-1234.9----L3
**********************-1234.90----L4
******************-1234.897600----L5
**************************0144----L6

值得注意的是,L3本来应该要输出:

-***********************1234.9----L3

也就是说,setf(ios::internal)没有发挥作用,这是因为其无法覆盖之前的setf(ios::right)。但是,setf(ios::left)setf(ios::right)是互斥的,因此后者可以覆盖前者。

此外,浮点数默认四舍五入到六位,如果末尾为0则默认不输出,因此L3本来四舍五入为-1234.90,输出变成-1234.9。

$~$

操纵符(manipulator):

操纵符与格式状态类似,但写法更加简洁。(需要头文件<iomanip>

例如,上面的例子中,用操纵符的写法可以写成:

1
2
3
4
5
cout<<setw(30)<<left<<setfill('*')<<c<<"----L1"<<endl;
cout<<setw(30)<<right<<setfill('*')<<c<<"----L2"<<endl;
cout<<dec<<showbase<<showpoint<<setw(30)<<d<<"----L3"<<endl;
cout<<setw(30)<<showpoint<<setprecision(10)<<d<<"----L4"<<endl;
cout<<setw(30)<<setbase(8)<<100<<"----L5"<<endl;

输出结果一样。

$~$


8.3 文件操作

$~$

文件与流:

用流操作文件的过程有以下四步:

  • 建立文件流:ifstream iFile,ofstream oFile
  • 打开文件:void open(const char* filename,int mode,int access)
  • 访问文件:iFile>>...,oFile<<...
  • 关闭文件:iFile.close(),oFile.close()

$~$

二进制文件:

二进制文件与文本文件在判定文件结束标志的方法上存在区别。二进制文件不能用EOF作为文件结束的判定值。

c++使用int xx::eof()判定文件是否结束,其中,xx表示输入流对象,到达文件末尾时,返回一个非零值,否则返回0。

$~$

二进制文件操作方法:

1
2
3
4
istream& get(char& ch);                 //按字节方式读文件,定义于istream类
ostream& put(char ch);                  //按字节方式写文件,定义于ostream类
istream& read(char* buf,int n);         //一次从输入流中读取n字节的内容,定义于istream类
ostream& write(const char* buf,int n);  //一次插入n字节的内容到输出流中

评论