C++输入输出流
简介
数据输入和输出过程也是数据传输的过程。数据就像流水一样从一个地方流动到另一个地方,因此,在C++中将输入输出称为“流(stream)"。
C++的输入输出流是指由若干字节组成的字节序列,这些字节中的数据按顺序从一个对象传送到另一对象。流表示了信息从源到目的端的流动。在输入操作时,字节流从输入设备(如键盘、磁盘)流向内存,在输出操作时,字节流从内存流向输出设备(如屏幕、打印机、磁盘等)。流中的内容可以是ASCII字符、二进制形式的数据、图形图像、数字音频视频或其他形式的信息。
实际上,在内存中为每一个数据流开辟一个内存缓冲区,用来存放流中的数据。当用cout和插入运算符“<<”向显示器输出数据时,先将这些数据送到程序中的输出缓冲区保存,直到缓冲区满了或遇到endl,就将缓冲区中的全部数据送到显示器显示出来。在输入时,从键盘输入的数据先放在键盘缓冲区中,当按回车键时,键盘缓冲区中的数据输入到程序中的输入缓冲区,形成cin流,然后用提取运算符“>>”从输入缓冲区中提取数据送给程序中的有关变量。总之,流是与内存缓冲区相对应的,或者说,缓冲区中的数据就是流。
流类
在C++的标准库中,将用于进行数据输入输出的类统称为”流类“。cin是流类istream的对象,cout是流类ostream的对象。要使用流类,需要在程序中包含iostream头文件。
如上图所示,ios是抽象基类,它派生出istream和ostream。istream和ostream又共同派生出了iostream类。
为了避免多继承的二义性,从ios派生出istream和ostream时,均使用virtual关键字(虚继承)。
istream是用于输入的流类,cin就是该类的对象。
ostream是用于输出的流类,cout就是该类的对象。
ifstream是用于从文件读取数据的类。
ofstream是用于向文件写入数据的类。
iostream是既能用于输入,又能用于输出的类。
fstream是既能从文件读取数据,又能向文件写入数据的类。
流对象
标准流对象
iostream头文件中定义了四个标准流对象,它们是cin、cout、cerr和clog。
cin对应于标准输入流,用于从键盘读取数据,也可以被重定向为从文件中读取数据。
cout对应于标准输出流,用于向屏幕输出数据,也可以被重定向为向文件写入数据。
clog对应于标准错误输出流,用于向屏幕输出错误信息,不能被重定向。
cerr和clog的区别在于:cerr不适用缓冲区,直接向显示器输出信息;而输出到clog中的信息会先被存放到缓冲区,缓冲区满或者刷新时才输出到屏幕。
ostream类的无参构造函数和复制构造函数都是私有的,因此在程序中一般无法定义ostream类的对象,唯一能用的ostream类的对象就是cout。
cout可以被重定向,而cerr不能。所谓重定向,就是将输入的源或输出的目的地改变。例如,cout本来是输出到屏幕的,但是经过重定向,本该输出到屏幕上的东西就可以被输出到文件中。
例如:
#include <iostream>
using namespace std;
int main()
{
int x,y;
cin >> x >> y;
freopen("test.txt", "w", stdout); //将标准输出重定向到 test.txt文件
if( y == 0 ) //除数为0则输出错误信息
cerr << "error." << endl;
else
cout << x /y ;
return 0;
}
其中,freopen是个标准库函数,第二个参数w代表写模式,第三个参数代表标准输出。该语句的作用是将标准输出重定向为test.txt文件。重定向之后,所有对cout的输出都不再出现在屏幕上,而是在test.txt文件中。
重定义流对象
cin也可以被重定向,如果在程序中加入
freopen("input.dat", "r", stdin);
第二个参数r代表读入方式,第三个参数stdin代表输入。执行此语句后,cin就不再从键盘读入数据,而是从input.dat文件中读入数据。
流操作算子-格式化输出
C++中常用的输入流操纵算子如表1所示,它们都是在头文件iomanip中定义的,要使用这些流操纵算子,必须包含该头文件。
note:"流操纵算子"一栏中的*不是算子的一部分,星号表示在没有使用任何算子的情况下,就等效于使用了该算子。例如,在默认情况下,整数是十进制形式输出的,等效于使用了dec算子。
用法:
#include <iomanip>
cout << hex << 12 << "," << 24;
c, 18
控制符 | 描 述 |
---|---|
*dec | 以十进制形式输出整数 |
hex | 以十六进制形式输出整数 |
oct | 以八进制形式输出整数 |
fixed | 以普通小数形式输出浮点数 |
scientific | 以科学计数法形式输出浮点数 |
left | 左对齐,即在宽度不足时将填充字符添加到右边 |
*right | 右对齐,即在宽度不足时将填充字符添加到左边 |
setbase(b) | 设置输出整数时的进制,b=8、10 或 16 |
setw(w) | 指定输出宽度为 w 个字符,或输人字符串时读入 w 个字符 |
setfill(c) | 在指定输出宽度的情况下,输出的宽度不足时用字符 c 填充(默认情况是用空格填充) |
setprecision(n) | 设置输出浮点数的精度为 n。在使用非 fixed 且非 scientific 方式输出的情况下,n 即为有效数字最多的位数,如果有效数字位数超过 n,则小数部分四舍五人,或自动变为科学计 数法输出并保留一共 n 位有效数字。在使用 fixed 方式和 scientific 方式输出的情况下,n 是小数点后面应保留的位数。 |
setiosflags(flag) | 将某个输出格式标志置为 1 |
resetiosflags(flag) | 将某个输出格式标志置为 0 |
boolapha | 把 true 和 false 输出为字符串 |
*noboolalpha | 把 true 和 false 输出为 0、1 |
showbase | 输出表示数值的进制的前缀 |
*noshowbase | 不输出表示数值的进制.的前缀 |
showpoint | 总是输出小数点 |
*noshowpoint | 只有当小数部分存在时才显示小数点 |
showpos | 在非负数值中显示 + |
*noshowpos | 在非负数值中不显示 + |
*skipws | 输入时跳过空白字符 |
noskipws | 输入时不跳过空白字符 |
uppercase | 十六进制数中使用 A~E。若输出前缀,则前缀输出 0X,科学计数法中输出 E |
*nouppercase | 十六进制数中使用 a~e。若输出前缀,则前缀输出 0x,科学计数法中输出 e。 |
internal | 数值的符号(正负号)在指定宽度内左对齐,数值右对 齐,中间由填充字符填充。 |
读取一行
cin.get
istream& get ( char* s, streamsize n)
istream& get ( char* s, size_t n, streamsize delim)
二者的区别是前者默认以换行符结束,后者可指定结束符。n表示目标空间的大小。示例代码如下:
#include <iostream>
using namespace std;
int main()
{
char a;
char array[20]={NULL};
cin.get(array,20);
cin.get(a);
cout<<array<<" "<<(int)a<<endl;
return 0;
}
输入:
123456789[回车]
输出:
123456789
123456789 10
第一排显示的是输入信息,
第二排显示的是输出信息.
1)从结果可以看出,cin.get(array,20);读取一行时,遇到换行符时结束读取,但是不对换行符进行处理,换行符仍然残留在输入缓冲区。第二次由cin.get()将换行符读入变量a,打印输入换行符的ASCII码值为10。这也是cin.get()读取一行与使用getline读取一行的区别所在。getline读取一行字符时,默认遇到’ ’时终止,并且将’ ’直接从输入缓冲区中删除掉,不会影响下面的输入处理。
2)cin.get(str,size);读取一行时,只能将字符串读入C风格的字符串中,即char*,但是C++的getline函数可以将字符串读入C++风格的字符串中,即string类型。鉴于getline较cin.get()的这两种优点,建议使用getline进行行的读取。
cin.getline
函数作用:从标准输入设备键盘读取一串字符串,并以指定的结束符结束。
#include <iostream>
istream& getline(char* s, streamsize count); //默认以换行符结束
istream& getline(char* s, streamsize count, char delim); //指定换行结束符
#include <iostream>
using namespace std;
int main()
{
char array[20]={NULL};
cin.getline(array,20);
cout<<array<<endl;
return 0;
}
123456789
123456789
getline
#include <string>
istream& getline ( istream& is, string& str);//默认以换行符结束
istream& getline ( istream& is, string& str, char delim); //指定换行结束符
#include <string>
#include <iostream>
using namespace std;
int main()
{
string str;
getline(cin,str);
cout<<str<<endl;
system("pause");
return 0;
}
hello world[回车]
hello world
hello world
注意,getline遇到结束符时,会将结束符一并读入指定的string中,再将结束符替换为空字符。因此,进行从键盘读取一行字符时,建议使用getline,较为安全。但是,最好还是要进行标准输入的安全检查,提高程序容错能力。
cin.getline()类似,但是cin.getline()属于istream流,而getline()属于string流,是不一样的两个函数。
gets
char *gets( char *buffer );
#include <iostream>
using namespace std;
int main()
{
char array[20]={NULL};
gets(array);
cout<<array<<endl;
system("pause");
return 0;
}
但是c++中找不到gets函数...
123
123
文件流
头文件
由于文件设备并不像显示器屏幕与键盘那样是标准默认设备,不能像cout那样预先定义的全局对象,所以我们必须自己定义一个该类的对象。
- ifstream类,它是从istream类派生的,用来支持从磁盘文件的输入。
- ofstream类,它是从ostream类派生的,用来支持向磁盘文件的输出。
- fstream类,它是从iostream类派生的,用来支持对磁盘文件的输入输出。
文件原理
文件打开:
文件打开都有一个文件指针,该指针的初始位置由I/O方式指定,每次读写都从文件指针的当前位置开始。每读入一个字节,指针就后移一个字节。当文件指针移到最后,就会遇到文件结束EOF(文件结束符也占一个字节,其值为-1),此时流对象的成员函数eof的值为非0值(一般设为1),表示文件结束了。
文件关闭:
实际上是解除该磁盘文件与文件流的关联,原来设置的工作方式也失效,这样,就不能再通过文件流对该文件进行输入或输出
文件类型:
1、ASCII文件:文件的每一个字节中均以ASCII代码形式存放数据,即一个字节存放一个字符,这个文件就是ASCII文件(或称字符文件)。
2、二进制文件:文件中的信息不是字符数据,而是字节中的二进制形式的信息,因此它又称为字节文件.
打开文件
函数原型:
void open(const char *filename, ios::openmode mode);
open() 成员函数的第一参数指定要打开的文件的名称和位置,第二个参数定义文件被打开的模式。
模式标志 | 描述 |
---|---|
ios::app | 追加模式。所有写入都追加到文件末尾。 |
ios::ate | 文件打开后定位到文件末尾。 |
ios::in | 打开文件用于读取。 |
ios::out | 打开文件用于写入。 |
ios::trunc | 如果该文件已经存在,其内容将在打开文件之前被截断,即把文件长度设为 0。 |
以写入模式打开文件,并希望截断文件,以防文件已存在:
ofstream outfile;
outfile.open("file.dat", ios::out | ios::trunc );
以读写方式打开文件:
ifstream afile;
afile.open("file.dat", ios::out | ios::in );
关闭文件
当 C++ 程序终止时,它会自动关闭刷新所有流,释放所有分配的内存,并关闭所有打开的文件。但程序员应该养成一个好习惯,在程序终止前关闭所有打开的文件。
下面是 close() 函数的标准语法,close() 函数是 fstream、ifstream 和 ofstream 对象的一个成员。
void close();
写入文件-输出
在 C++ 编程中,我们使用流插入运算符( << )向文件写入信息,就像使用该运算符输出信息到屏幕上一样。唯一不同的是,在这里您使用的是 ofstream 或 fstream 对象,而不是 cout 对象。
读取文件-输入
在 C++ 编程中,我们使用流提取运算符( >> )从文件读取信息,就像使用该运算符从键盘输入信息一样。唯一不同的是,在这里您使用的是 ifstream 或 fstream 对象,而不是 cin 对象。
写入实例
下面的 C++ 程序以读写模式打开一个文件。在向文件 afile.dat 写入用户输入的信息之后,程序从文件读取信息,并将其输出到屏幕上:
#include <fstream>
#include <iostream>
using namespace std;
int main ()
{
char data[100];
// 以写模式打开文件
ofstream outfile;
outfile.open("afile.dat");
cout << "Writing to the file" << endl;
cout << "Enter your name: ";
cin.getline(data, 100);
// 向文件写入用户输入的数据
outfile << data << endl;
cout << "Enter your age: ";
cin >> data;
cin.ignore();
// 再次向文件写入用户输入的数据
outfile << data << endl;
// 关闭打开的文件
outfile.close();
// 以读模式打开文件
ifstream infile;
infile.open("afile.dat");
cout << "Reading from the file" << endl;
infile >> data;
// 在屏幕上写入数据
cout << data << endl;
// 再次从文件读取数据,并显示它
infile >> data;
cout << data << endl;
// 关闭打开的文件
infile.close();
return 0;
}
Writing to the file
Enter your name: Zara
Enter your age: 9
Reading from the file
Zara
9
上面的实例中使用了 cin 对象的附加函数,比如 getline()函数从外部读取一行,ignore() 函数会忽略掉之前读语句留下的多余字符。
文件位置指针
istream 和 ostream 都提供了用于重新定位文件位置指针的成员函数。这些成员函数包括关于 istream 的 seekg("seek get")和关于 ostream 的 seekp("seek put")。
seekg 和 seekp 的参数通常是一个长整型。第二个参数可以用于指定查找方向。查找方向可以是 ios::beg(默认的,从流的开头开始定位),也可以是 ios::cur(从流的当前位置开始定位),也可以是 ios::end(从流的末尾开始定位)。
文件位置指针是一个整数值,指定了从文件的起始位置到指针所在位置的字节数。下面是关于定位 "get" 文件位置指针的实例:
// 定位到 fileObject 的第 n 个字节(假设是 ios::beg)
fileObject.seekg( n );
// 把文件的读指针从 fileObject 当前位置向后移 n 个字节
fileObject.seekg( n, ios::cur );
// 把文件的读指针从 fileObject 末尾往回移 n 个字节
fileObject.seekg( n, ios::end );
// 定位到 fileObject 的末尾
fileObject.seekg( 0, ios::end );
参考链接:
https://blog.csdn.net/qq_41631051/article/details/91448649
https://blog.csdn.net/weixin_34194379/article/details/92288825?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase
https://www.runoob.com/cplusplus/cpp-files-streams.html