C++ 输入输出操作

基础知识

        C++语言并不能直接处理IO操作,它是依赖标准库中不同的IO类来实现从设备中读取数据和向设备中写入数据。

1)IO类对象

        执行输入操作时,数据流(stream)从输入设备(键盘、磁盘文件等)流向内存,执行输出操作时,数据流从内存流向输出设备(控制台窗口、打印机、磁盘文件等)。IO操作通过与设备关联的IO对象来实现数据的读写操作。常用的IO类如下所示:

头文件 类名 功能
iostream ios 抽象基类。由它派生出istream类和ostream类
iostream istream 通用输入流和其他输入流的基类。cin便是istream类的对象
ostream 通用输出流和其他输出流的基类。cout便是ostream类的对象
iostream 通用输入输出流和其他输入输出流的基类。
fstream ifstream 输入文件流。它继承自istream
ofstream 输出文件流。它继承自ostream
fstream 输入输出文件流。它继承自iostream
sstream istringstream 输入字符串流。它继承自istream
ostringstream 输出字符串流。它继承自ostream
stringstream 输入输出字符串流。

       在输入输出过程中,程序会在内存中为每一个数据流开辟一个内存缓冲区,用来存放流(stream)中的数据。在输入操作过程中,从键盘输入的数据先放在键盘自身的缓冲区中,当按回车键后,键盘缓冲区数据流到程序中的输入缓冲区,形成cin流,然后用输入运算符>>从输入缓冲区中提取数据并将它们保存到与对象关联的内存中。当用cout <<向控制台窗口输出数据时,先将这些数据送到程序中的输出缓冲区保存,直到缓冲区执行刷新操作,就将缓冲区的数据送到控制台窗口显式出来。总之流是与内存缓冲区对应的,或者说,缓冲区就是数据中的流。

        和普通对象不同,IO对象不支持赋值操作,另外IO对象也不支持复制操作。正是因为不支持复制操作,因此不能把它们设置为非引用类型形参或非引用返回类型;IO类型通常以引用的方式传递或返回,且传递或返回的引用不能是const类型。

ostream &print(ostream &, const Fraction &);

2)条件状态

        确定一个流对象是否有效的最简单办法是测试它的值。例如cin处于出错状态时,他的值为false,如果cin处于正常状态,则它的值为true。

while(cin >> x)
/*如果cin是有效的(没有遇到输入错误或输入结束符),则while循环将继续执行。如果cin状态
是有效的,则可以使用cin的成员函数clear来使其恢复为有效状态*/
if(! cin) {
    cin.clear()    //恢复到有效状态。
    /*ignore成员用来清除以回车符结束的输入缓冲区中的无效输入,消除对下一次输入的影响,进而
    可以继续执行后续的输入操作,第一个实参利用numeric_limits类获取streamsize的最大值(在头
    文件limits中定义*/
    cin.ignore(numeric_limits::max(), '\n');    //忽略无效输入
}

3)刷新缓冲区

        cout负责从程序的输出缓冲区向控制台窗口输出数据,只有当缓冲区刷新时,数据才会真正送到输出设备。缓冲区刷新完成后,原来的数据被清空。导致缓冲区刷新的原因有很多,比如缓冲区满、程序正常结束、遇到endl等。除使用endl强制完成缓冲区刷新外,还可以使用flush或ends强制刷新缓冲区:

cout << "endl" << endl;    //输出endl和一个换行,然后刷新缓冲区
cout << "flush" << flush;    //输入flush(无额外字符),然后刷新缓冲区
cout << "ends" << ends;    //输出ends和一个空字符('\0'),然后刷新缓冲区

..................................................................

字数数据的输入

1)cin.get()函数

        利用cin>>输入字符时,数据的输入一般以空白字符(空格符、制表符和回车符) 结束(如果遇到空白字符,则忽略空白字符并停止字符的读入)。如果想要获取这些空白字符,则必须使用cin的成员函数get,该函数的功能是从输入流中提取一个字符,并将其返回。

/*逐个获取从控制台窗口输入的字符,知道遇到'\n'为止,并将它们输出*/
for(char c; (c = cin.get()) != '\n')
    cout << c;
cout << endl;

2)getline函数

        如果想读取空白字符,并且获取一整行字符序列,可以使用getline函数。该函数以回车符作为输入结束的标志符,把从输入流cin中提取的字符序列(不包括回车符)放到string类对象s中,并返回cin的引用。如果输入流中只有回车符,s将会是一个空string对象。

sring s;
getline(cin, s);        //当输入"hello C++"时(注意里面的空格),s的内容为"hello C++"

..................................................................

输入输出的格式化控制

        这里需要注意,使用操纵符setprecision、setw、setfill时需要包含iomanip文件。

1)整型值的进制(默认为10进制)

/*showbase设置数据输出时显式其进制信息,uppercase设置十六进制数以大写形式输出,包括前导
字符X*/
cout << showbase << uppercase;
cout << "default:" << 26 << endl;    //default:26
cout << "octal:" << oct << 26 << endl;    //cotal:032
cout << "decimal:" << dec << 26 << endl;    //decimal:26
cout << "hex:" << hex << 26 << endl;    //hex:0X1A
/*将输出形式恢复到默认设置*/
cout << noshowbase << nouppercase << dec;

/*在输入的时候,也可以利用进制说明符来控制输入数据的进制,例如当输入032和0x1a并按回车键时,
i和j的值均为26*/
int i, j;
cin >> oct >> i;
cin >> hex >> j;    //注意,这条语句执行完成后,可以利用dec将进制形式恢复到默认的十进制

2)浮点数的格式控制

        默认情况下,浮点数按6位数字精度(有效数字)打印,并以舍入方式而不是截断方式打印。可以利用setprecision函数或IO对象的precision函数来指定打印精度。其中procesion函数有两个版本,一个版本接受一个整型值用来设置当前精度,并返回原来的精度;另一个版本没有参数,返回当前精度。

double x = 1.2125;
int default_precision = cout.precision(3);
cout << "precision:" << cout.precision() << ", x=" << x << endl;//procesion:3, x=1.22
cout << setprecision(4);
cout << "precision:" << cout.precision() << ", x=" << x << endl;//procesion:4, x=1.215
cout.precision(default_precision);    //恢复默认精度

        还可以指定输出的格式,如科学计数法、定点十进制或默认格式:

/*注意,在执行scientific或者fixed操纵符后,精度控制的是小数点后面的数值位数,而不是默认的
数值总位数。defaultfloat是C++11新特性,它将流恢复到默认格式(不能控制精度)*/
cout << "default format:" << 10*exp(1.0) << endl; //default format:27.1828
cout << "scientific:" << scientific << 10*exp(1.0) << endl; //scientific:2.718282e+01
cout << "fixed decimal:" << fixed << 10*exp(1.0) << endl; //fixed decimal:27.182818
cout << "default float:" << defaultfloat << 10*exp(1.0) << endl; //default float:27.1828

        默认情况下,如果一个浮点值的小数部分为0,则不显示小数部分。可使用showpoint强制打印小数点:

cout << 1.0 << endl;    //打印1
cout << showpoint << 1.0 << noshowpoint << endl;    //打印1.00000
//操纵符noshowpoint恢复默认设置,后续输出将采用默认设置。

3)宽度控制

        当按列打印数据时,可以利用操纵符setw指定输入输出数据占用的宽度。setw接受一个int值(数值或者字符等占用的位置数目)。如果数据的实际宽度大于指定宽度,则按实际宽度输出;如果数据的宽度小于指定宽度,则采用默认的右对齐左边补空的方式输出。也可以使用setfill指定字符来填充空白。

int i = -10;
double x = 1.2152;
cout << "i:" << setw(10) << i << endl;               //i:       -10
cout << "x:" << setw(10) << x << endl;               //x:    1.2152
cout << setfill('*') << "x:" << setw(10) << endl;    //x:****1.2152

 ..................................................................

文件输入输出

        前面介绍的IO对象都是与键盘设备(cin对象)和控制台窗口(cout对象)相关联的。如果需要从硬盘读取数据或向硬盘写入数据,则需要文件流。IO文件流定义在头文件fstream中,包括ifstream类型(从一个指定文件读取数据)、ostream类型(向一个指定文件写入数据)和fstream类型(可以读写文件)。

1)使用文件流对象

        对一个文件进行读写操作,需要创建一个文件流对象,并使之与该文件相关联。如果在创建文件流对象时提供了文件名,则会自动创建open操作。

/*定义了一个输入流对象in,用来从指定文件读取数据。文件名为ifname*/
ifstream in(ifname);    //创建输入文件流对象,提供文件名
ofstream out(ofname, ios::app);    //创建输出文件流对象,提供文件名

        如果在创建一个文件流对象时没有指定文件名,则可以调用open函数,使之与一个文件关联。


/*定义了一个输出流对象out, 用来向指定文件输出数据。*/
ofstream out;    //创建输出文件流对象,没有提供文件名
out.open(ofname);

        文件的open操作不一定成功,因此在执行read/write数据之前检查open是否成功。当读写完成之后需要断开流对象与文件的关联,否则与文件流对象关联的文件不能被其他文件流对象访问,该文件流对象也不能访问其他文件。

if(out)    //检查open操作是否成功
/*read/write操作*/
out.close();    //关闭文件

2)文件模式

        每个文件都有一些关联的文件模式,用来指定如何使用文件,例如读操作、写操作或者将数据附加到文件末尾等。常用的打开模式如下:

ios::in 以读方式打开文件。
ios::out 以写方式打开文件(默认方式)。如果已有此文件,则将其原有内容全部擦除;如果文件不存在,则建立新文件。
ios::app 以写方式打开文件,写入的数据追加到文件末尾。
ios:ate 打开一个已有的文件,并定位到文件末尾。
ios::trunc 如果文件存在先删除,再创建
ios:binary 以二进制方式打开一个文件,如不指定此方式默认为ASCII方式。

        每一个文件流类型都设置了一个默认的文件模式,如果没有指定具体的文件模式,则以默认模式打开。ifstream流的默认模式是ios::in;ofstream流的默认模式是ios::out (默认情况下使用ofstream对象打开一个已有文件时文件原有内容会被清空,如果要保留文件中原有数据,则应使用ios::app模式);fstream流的默认模式是ios::in和ios::out。

 ..................................................................

string流

        string流可以向string类对象写入数据,也可以向string类对象读取数据。string流对象定义在sstream头文件中,其中的istringstream从string对象读取数据,ostringstream向string对象写入数据,stringstream既可以向string对象写入数据,也可以从string对象读数据。

1)istringstream流

        当从设备(如键盘或硬盘文件)读取一行文本时,往往需要对整行文本中的单个单词进行处理,这时可以使用istringstream流对象。比如,需要获取一行文本中的所有单词,并把它存放到一个vector里,为后续的单词处理工作做准备。

vector  wds;    //保存读取的单词
string line, word;
/*利用getline获取一行文本,如果获取成功(getline函数返回有效的cin),则将整行文本的副本保存到
istringstream对象iss中。然后通过调用输入运算符号>>逐个获取iss中的每一个单词,并把获得的单词
放到vector类型对象wds中。
需要注意的是,在读完line中的所有单词后,会触发文件结束信息,使iss变为无效状态,内层while循环
会结束。*/
while(getline(cin, line)) {
    istringstream iss(line);    //创建输入string流对象,保存line的一个副本
    while(iss >> word) //读取每个单词
        wds.push_back(word); //将读取的单词尾插到wds中
}

2)ostringstream流

        当需要一次打印不同数据类型的数据时,使用ostreamstream流可以很容易实现。比如,在上面的例子中,在获取所有的单词后,一次性输出每个单词和它们的长度:

ostringstring out;    //创建string流对象
for (auto &i : wds)    //处理每一个单词
    out << i << ":" << i.length() << '\n';    //将数据写入输出流对象中
/*对象out的成员函数str返回存储的string类型数据。注意,ostringstream的另一个版本的成员函数
str接受一个string类型的参数,用来覆盖原有的数据,例如out.str('')调用后,out里面的数据将被
清空*/
cout << out.str();    //打印输出

   

你可能感兴趣的:(编程语言,C++11)