C++ IO流_数据的旅行之路
1. 前言
程序中的数据总是在流动着,既然是流动就会有方向。数据从程序的外部流到程序内部,称为输入;数据从程序内部流到外部称为输出。
C++
提供有相应的API
实现程序和外部数据之间的交互,统称这类API
为 IO
流API
。
流
是一个形象概念,数据从一端传递到另一端时,类似于水一样在流动,只是流动的不是水,而是数据
。
概括而言,流对象可连接 2
端,并在两者之间搭建起一个通道 ,让数据通过此通道流过来、流过去。
2. 标准输入输出流
初学C++
时,会接触 cout
和cin
两个流对象。
2.1 简介
cout
称为标准输出流对象,其一端连接程序,一端连接标准输出设备(标准输出设备一般指显示器
),cout
的作用是把程序中的数据显示在显示器上。
除了cout
,还有cerr
,其作用和 cout
相似。两者区别:
cout
带有数据缓存功能,cerr
不带缓存功能。缓存
类似于蓄水池
,输出时,先缓存数据,然后再从缓存中输出到显示器上。cout
输出程序通用数据(测试,逻辑结果……),cerr
输出错误信息。
另还有一个
clog
对象,和cerr
类似,与cerr
不同之处,带有缓存功能。
cin
称为标准输入流对象,一端连接程序,一端连接标准输入设备(标准输入设备一般指键盘),cin
用来把标准输入设备上的数据输入到程序中。
使用 cout
和cin
时需要包含 iostream
头文件。
#include <iostream>
打开 iostream
源代码,可以看到 iostream
文件中包含了另外 2
个头文件:
#include <ostream>
#include <istream>
且在 iostream
头文件中可以查找到如下代码:
extern istream cin; /// Linked to standard input
extern ostream cout; /// Linked to standard output
extern ostream cerr; /// Linked to standard error (unbuffered)
extern ostream clog; /// Linked to standard error (buffered)
cout
、cerr
、clog
是 ostream
类的实例化对象,cin
是 istream
类的实例化对象。
2.2 使用
ostream
类重载了<<
运算符,istream
类重载了>>
运算符,可以使用这 2
个运算符方便、快速地完成输入、输出各种类型数据。打开源代码,可以查看到 <<
运算符返回调用者本身。意味着使用 cout<<数据
时,返回 cout
本身,可以以链式方式
进行数据输出。
#include <iostream>
using namespace std;
int main(int argc, char** argv) {
string name="果壳";
int age=12;
//链式输出格式
cout<<"姓名:"<<name<<"年龄:"<<age;
return 0;
}
istream
类重载了 >>
运算符,返回调用者(即 istream
对象)本身,也可以使用链式方式进行输入。
#include <iostream>
using namespace std;
int main(int argc, char** argv) {
char sex;
int age;
//链式输入
cin>>sex>>age;
return 0;
}
cout
、cin
流对象的其它函数暂不介绍,继续本文的重点文件流。
3. 文件流
文件流 API
完成程序中的数据和文件中的数据的输入与输出,使用时,需要包含 fstream
头文件。
#include <fstream>
3.1 文件输入流
ifstream
从 istream
类派生,用来实现把文件中的数据l输入(读)到程序中。
输入操作对程序而言,也称为
读
操作。
文件输入流对象的使用流程:
3.1.1 建立流通道
使用 ifstream
流对象的 open
函数建立起程序
和外部存储设备
中的文件资源之间的流通道。
文件类型分文本文件和二进制文件。
使用之前,了解一下 open
函数的原型说明。打开ifstream
头文件,可查看到 ifstream
类中有如下的信息说明:
template<typename _CharT, typename _Traits>
class basic_ifstream : public basic_istream<_CharT, _Traits>
{
/**
* @brief Opens an external file.
* @param __s The name of the file.
* @param __mode The open mode flags.
*
* Calls @c std::basic_filebuf::open(s,__mode|in). If that function
* fails, @c failbit is set in the stream's error state.
*
* Tip: When using std::string to hold the filename, you must use
* .c_str() before passing it to this constructor.
*/
void open(const char* __s, ios_base::openmode __mode = ios_base::in)
{
if (!_M_filebuf.open(__s, __mode | ios_base::in))
this->setstate(ios_base::failbit);
else
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 409. Closing an fstream should clear error state
this->clear();
}
#if __cplusplus >= 201103L
/**
* @brief Opens an external file.
* @param __s The name of the file.
* @param __mode The open mode flags.
*
* Calls @c std::basic_filebuf::open(__s,__mode|in). If that function
* fails, @c failbit is set in the stream's error state.
*/
void open(const std::string& __s, ios_base::openmode __mode = ios_base::in)
{
if (!_M_filebuf.open(__s, __mode | ios_base::in))
this->setstate(ios_base::failbit);
else
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 409. Closing an fstream should clear error state
this->clear();
}
#endif
}
ifstream
重载了 open
函数,2
个函数参数数量一致,但第一个参数的类型不相同。调用时需要传递 2
个参数:
- 第一个参数,指定文件的路径。第一个
open
函数通过const char* __s
类型(字符串指针)接受,第二个open
函数通过const std::string& __s
类型(字符串对象)接受。 - 第二个参数,指定文件的打开方式。打开方式是一个枚举类型,默认是
ios_base::in(输入)
模式。打开模式如下所示:
enum _Ios_Openmode
{
_S_app = 1L << 0,
_S_ate = 1L << 1,
_S_bin = 1L << 2,
_S_in = 1L << 3,
_S_out = 1L << 4,
_S_trunc = 1L << 5,
_S_ios_openmode_end = 1L << 16
};
typedef _Ios_Openmode openmode;
/// 以写的方式打开文件,写入的数据追加到文件末尾
static const openmode app = _S_app;
/// 打开一个已有的文件,文件指针指向文件末尾
static const openmode ate = _S_ate;
/// 以二进制方式打开一个文件,如不指定,默认为文本文件方式
static const openmode binary = _S_bin;
/// 以输入(读)方式打开文件
static const openmode in = _S_in;
/// 以输出(写)方式打开文件,如果没有此文件,则创建,如有此文件,此清除原文件中数据
static const openmode out = _S_out;
/// 打开文件的时候丢弃现有文件里边的内容
static const openmode trunc = _S_trunc;
打开文件实现:
#include <iostream>
#include <fstream>
using namespace std;
int main(int argc, char** argv) {
ifstream inFile;
//文件路径保存在字符数组中
char fileName[50]="d:\\guoke.txt";
inFile.open(fileName,ios_base::in);
//文件路径保存在字符串对象中
string fileName_="d:\\guoke.txt" ;
inFile.open(fileName_,ios_base::in);
return 0;
}
除了直接调用 open
函数外,还可以使用 ifstream
的构造函数,如下代码,本质还是调用 open
函数。
char fileName[50]="d:\\guoke.txt";
//构造函数
ifstream inFile(fileName,ios_base::in);
或者:
string fileName_="d:\\guoke.txt" ;
ifstream inFile(fileName_,ios_base::in);
可以使用ifstream
的 is_open
函数检查文件是否打开成功。
3.1.2 读数据
打开文件后,意味着输入流通道
建立起来,默认情况下,文件指针指向文件的首位置,等待读取操作。
读或写都是通过移动文件指针实现的。
读取数据的方式:
- 使用
>>
运算符。
ifstream
是istream
的派生类,继承了父类中的所有公共函数,如同 cin
一样可以使用 >>
运算符实现对文件的读取操作。
cin
使用>>
把标准输入设备上的数据输入至程序。
ifstream
使用>>
把文件中的数据输入至程序。两者的数据源不一样,目的地一样。
提前在 guoke.txt
文件中写入如下内容,也可以用空白隔开数字。
#include <iostream>
#include <fstream>
using namespace std;
int main(int argc, char** argv) {
//用来存储文件中的数据
int nums[10];
//文件输入流对象
ifstream inFile;
//文件路径
char fileName[50]="d:\\guoke.txt";
//打开文件
inFile.open(fileName,ios_base::in);
if(inFile.is_open()) {
//检查文件是否正确打开
cout<<"文件打开成功"<<endl;
//读取文件中的内容
for(int i=0; i<5; i++){
//读取
inFile>>nums[i];
//输出到显示器
cout<<nums[i]<<endl;
}
}
return 0;
}
如上代码,把文件中的 5
个数字读取到 nums
数组中。
用
>>
运算符读取时,以换行符、空白等符号作为结束符。
- 使用
get
、getline
函数。
ifstream
类提供有 get
、getline
函数,可用来读取文件中数据。get
函数有多个重载,本文使用如下的 2
个。getline
函数和get
函数功能相似,其差异之处后文再述。
//以字符为单位读取
istream &get( char &ch );
//以字符串为单位读取
istream &get( char *buffer, streamsize num );
先在 D
盘使用记事本创建 guoke.txt
文件,并在文件中输入以下 2
行信息:
this is a test
hello wellcome
编写如下代码,使用 get
函数以字符类型
逐个读取文件中的内容。
#include <iostream>
#include <fstream>
using namespace std;
int main(int argc, char** argv) {
//用来存储文件中的数据
int nums[10];
ifstream inFile;
char fileName[50]="d:\\guoke.txt";
inFile.open(fileName,ios_base::in);
char myChar;
if(inFile.is_open()) {
cout<<"文件打开成功"<<endl;
//以字符为单位读取数据
while(inFile.get(myChar)){
cout<<myChar;
}
}
return 0;
}
//输出结果
this is a test
hello wellcome
读取时,需要知道是否已经达到了文件的未尾,或者说如何知道文件中已经没有数据。
如上使用
get
函数读取时,如果没有数据了,会返回false
。使用
eof
函数。eof
的全称是end of file
, 当文件指针移动到文件无数据处时,eof
函数返回true
。建议使用此函数。while(!inFile.eof()){
inFile.get(myChar);
cout<<myChar;
}
使用 get
的重载函数以字符串
类型读取。
#include <iostream>
#include <fstream>
using namespace std;
int main(int argc, char** argv) {
//用来存储文件中的数据
int nums[10];
ifstream inFile;
char fileName[50]="d:\\guoke.txt";
inFile.open(fileName,ios_base::in);
char myChar[100];
if(inFile.is_open()) {
cout<<"文件打开成功"<<endl;
while(!inFile.eof() ) {
//以字符串为单位读取
inFile.get(myChar,100);
cout<<myChar<<endl;
//为什么要调用无参的 get 函数?
inFile.get();
}
}
return 0;
}
输出结果:
上述 get
函数以字符串
为单位进行数据读取,会把读出来的数据保存在第一个参数 myChar
数组中,第二个参数限制每次最多读 num-1
个字符。
如果把上述的
inFile.get(myChar,100);
改成
inFile.get(myChar,10);
则程序运行结果如下:
第一次读了 9
个字符后结束 ,第二次遇到到换行符后结束,第三行读了 9
个字符后结束,第四行遇到文件结束后结束 。
为什么在代码要调用无参 get
函数?
因为get
读数据时会把换行符
保留在缓存器中,在读到第二行之前,需要调用无参的 get
函数提前清除(读出)缓存器。否则后续数据读不出来。
getline
和 get
函数一样,可以以字符串
为单位读数据,但不会缓存换行符(结束符)。如下同样可以读取到文件中的所有内容。
while(inFile.eof()){
inFile.getline(myChar,100)
cout<<myChar<<endl;
}
- 使用
read
函数。
除了get
和getline
函数还可以使用 read
函数。函数原型如下:
istream &read( char *buffer, streamsize num );
#include <iostream>
#include <fstream>
using namespace std;
int main(int argc, char** argv) {
//用来存储文件中的数据
int nums[10];
ifstream inFile;
char fileName[50]="d:\\myinfo.txt";
inFile.open(fileName,ios_base::in);
char myChar[100];
if(inFile.is_open()) {
cout<<"文件打开成功"<<endl;
inFile.read(myChar,100);
cout<<myChar;
}
return 0;
}
read
一次性读取到num
个字节或者遇到 eof(文件结束符)
停止读操作。这点和 get
和getline
不同,后者以换行符为结束符号。
3.1.3 关闭文件
读操作结束后,需要关闭文件对象。
inFile.close();
3.2 文件输出流
ofstream
称为文件输出流,其派生于ostream
,用于把程序中的数据输出(写)到文件中。和使用 ifstream
的流程一样,分 3
步走:
- 打开文件。
使用 ofstream
流对象的 open
函数(和 ifstream
的 open
函数参数说明一样)打开文件,因为是写操作,打开的模式默认是ios_stream::out
,当然,可以指定其它的如ios_stream::app
模式。
#include <iostream>
#include <fstream>
using namespace std;
int main(int argc, char** argv) {
//输出流对象
ofstream outFile;
char fileName[50]="d:\\guoke.txt";
outFile.open(fileName,ios_base::out);
if (outFile.is_open()){
cout<<"打开文件成功"<<endl;
}
return 0;
}
- 写操作和读操作一样,有如下几种方案:
- 使用
<<
运算符。
#include <iostream>
#include <fstream>
using namespace std;
int main(int argc, char** argv) {
//输出流对象
ofstream outFile;
char fileName[50]="d:\\guoke.txt";
outFile.open(fileName,ios_base::out);
if (outFile.is_open()){
cout<<"打开文件成功"<<endl;
for(int i=0;i<10;i++){
//向文件中写入 10 个数字
outFile<<i;
}
}
return 0;
}
输出结果:
- 使用
put
、write
函数。
put
函数以字符为单位向文件中写入数据,put
函数原型如下:
ostream &put( char ch );
#include <iostream>
#include <fstream>
using namespace std;
int main(int argc, char** argv) {
//输出流对象
ofstream outFile;
char fileName[50]="d:\\guoke.txt";
outFile.open(fileName,ios_base::out);
if (outFile.is_open()){
cout<<"打开文件成功"<<endl;
for(int i=0;i<10;i++){
//写入 10 个大写字母
outFile.put(char(i+65) );
}
}
return 0;
}
write
可以把字符串
写入文件中,如下为write
函数原型:
ostream &write( const char *buffer, streamsize num );
参数说明:
- 第一个参数:
char
类型指针。 - 第二个参数:限制每次写入的数据大小。
#include <iostream>
#include <fstream>
using namespace std;
int main(int argc, char** argv) {
//输出流对象
ofstream outFile;
char fileName[50]="d:\\guoke.txt";
outFile.open(fileName,ios_base::out);
char infos[50]="thisisatest";
if (outFile.is_open()){
cout<<"打开文件成功"<<endl;
outFile.write(infos,50);
}
return 0;
}
文件中内容:
thisisatest
如果把
outFile.write(infos,50);
改成
outFile.write(infos,5);
则文件中内容为
thisi
- 关闭资源。
操作完成后,需要调用close
函数关闭文件。
outFile.close();
4. 随机访问文件
随机访问指可以根据需要移动二进制文件
中的文件指针,随机读或写二进制文件中的内容。
随机访问要求打开文件时,指定文件打开模式为
ios_base::binary
。
随机读写分 2
步:
- 移动文件指针到读写位置。
- 然后读或写。
随机访问的关键是使用文件指针
的定位函数进行位置定位:
gcount() 返回最后一次输入所读入的字节数
tellg() 返回输入文件指针的当前位置
seekg(文件中的位置) 将输入文件中指针移到指定的位置
seekg(位移量,参照位置) 以参照位置为基础移动若干字节
tellp() 返回输出文件指针当前的位置
seekp(文件中的位置) 将输出文件中指针移到指定的位置
seekp(位移量,参照位置) 以参照位置为基础移动若干字节
如下代码,使用文件输出流向文件中写入数据,然后随机定位文件指针位置,再进行读操作。
#include<fstream>
#include<iostream>
using namespace std;
int main() {
int i,x;
// 以写的模式打开文件
ofstream outfile("d:\\guoke.txt",ios_base::out | ios_base::binary);
if(!outfile.is_open()) {
cout << "open error!";
exit(1);
}
for(i=1; i<100; i+=2)
//向文件中写入数据
outfile.write((char*)&i,sizeof(int));
outfile.close();
//输入流
ifstream infile("d:\\guoke.txt",ios_base::in|ios_base::binary);
if(!infile.is_open()) {
cout <<"open error!\n";
exit(1);
}
//定位
infile.seekg(30*sizeof(int));
for(i=0; i<4 &&!infile.eof(); i++) {
//读数据
infile.read((char*)&x,sizeof(int));
cout<<x<<'\t';
}
cout <<endl;
infile.close();
return 0;
}
原文件中内容:
代码执行后的运行结果,并没有输入文件中的所有内容。
5. 总结
本文讲述了标准输入、输出流和文件流对象。
C++ IO流_数据的旅行之路的更多相关文章
- 八: IO流,数据的读写传输
IO流概括图: IO流的分类: 按流: 输入流(InputStream和Reader):从硬盘或者别的地方读入内存 输出流(OutputStream和Writer):从内存里向硬盘或别的地方输出 按 ...
- java ->IO流_打印流
打印流的概述 打印流添加输出数据的功能,使它们能够方便地打印各种数据值表示形式. 打印流根据流的分类: 永远不会抛出IO异常 l 字节打印流 PrintStream l 字符打印流 Print ...
- java - >IO流_缓冲流(高效流)
缓冲流(高效流) 在我们学习字节流与字符流的时候,大家都进行过读取文件中数据的操作,读取数据量大的文件时,读取的速度会很慢,很影响我们程序的效率,那么,我想提高速度,怎么办? Java中提高了一套缓冲 ...
- Java_初入IO流_字符流_Write-Read_小笔记
package IO; import java.io.FileWriter; import java.io.IOException; class File_Writer { public static ...
- IO流_文件切割与合并(带配置信息)
在切割文件的时候应该生成一个记录文件信息的文件,以便在以后合并文件的时候知道这个文件原来的文件名和记录文件切割完后生成了多少个切割文件 import java.io.File; import java ...
- IO流_演示键盘录入
读取一个键盘录入的数据,打印到控制台上 键盘本身就是一个标准的输入设备,对于java而言,对于这种输入设备都有相应的对象在System类中 import java.io.IOException; im ...
- java ->IO流_序列化流与反序列化流
序列化流与反序列化流 用于从流中读取对象的操作流 ObjectInputStream 称为 反序列化流 用于向流中写入对象的操作流 ObjectOutputStream 称为 序列化流(对象 ...
- java ->IO流_字节流
字节流 在前面的学习过程中,我们一直都是在操作文件或者文件夹,并没有给文件中写任何数据.现在我们就要开始给文件中写数据,或者读取文件中的数据. 字节输出流OutputStream OutputStre ...
- IO流_文件切割与合并
切割可以分两种方式:按文件个数切,按文件大小来切(建议用这种方式,因为按个数的话,有可能文件非常大) import java.io.File; import java.io.FileInputStre ...
随机推荐
- 我的 Java 学习&面试网站又又又升级了!
晚上好,我是 Guide. 距离上次介绍 JavaGuide 新版在线阅读网站已经过去 7 个多月了(相关阅读:官宣!我升级了!!!),这 7 个多月里不论是 JavaGuide 的内容,还是 Jav ...
- C语言学习之我见-strcmp()字符串比较函数
strcmp()函数,用于两个字符串的比较. (1)函数原型 int strcmp(const char *_Str1,const char *_Str2); (2)头文件 string.h (3)功 ...
- Vmware-Centos7-NAT 网络配置
首先一句话总结 NAT模式下,将VMware Network Adapter VMnet8的IP改为与虚拟机IP同一网段即可. 操作步骤 1. 打开虚拟网络编辑器 2. 配置NAT 选择NAT模式,取 ...
- SAP Smartforms 参数配置
DATA : sf_name TYPE rs38l_fnam. DATA : sf_output_options TYPE ssfcompop. DATA : sf_control_parameter ...
- java请求登录接口代码示例
前言 近期研究如何利用java代码如何获取其他系统中所需的数据,自己总结的方法如下: 1.工具类代码 /** * <pre> * 方法体说明:向远程接口发起请求,返回字符串类型结果 * @ ...
- Python:27行代码实现将多个Excel表格内容批量汇总合并到一个表格
序言 (https://jq.qq.com/?_wv=1027&k=GmeRhIX0) 老板最近越来越过分了,快下班了发给我几百个表格让我把内容合并到一个表格内去.还好我会Python,分分钟 ...
- 写了个 Markdown 命令行小工具,希望能提高园友们发文的效率!
写了个 Markdown 命令行小工具,希望能提高园友们发文的效率! 前言 笔者使用 Typora 来编写 Markdown 格式的博文,图片采用的是本地相对路径存储(太懒了不想折腾图床). 时间久了 ...
- 等待唤醒机制代码实现_包子类&包子铺类和等待唤醒机制代码实现_吃货类&测试类
资源类:包子类 设置包子的属性 皮 陷 包子的状态:有 true 没有 false public class BaoZi { //皮 String pi; //陷 String xian; //包子的 ...
- 2019 CSP-S 初赛解析
因为我不会设置用博客园显示Markdown语法,所以在洛谷也写了一份:传送门 一起讨论的这套卷.题干 然后还有一些可以借鉴一下的解析 选择: T1. 注意运算顺序: a%3=1 --> (int ...
- esp8266模拟输入(ADC)检测问题
今天使用esp12f读取A0数据时一直出现错误; Serial.println(analogRead(A0));读取值一直为1024 因为前段时间一直用的是开发板,读取电压值正常 而从昨天换为了esp ...