目录
- 1、输入输出类介绍
- 1.C/C++文件操作对比
- 2.什么是流?
- 3.C++ I/O流类层次
- 4.带缓冲的输入输出
- 5.gcc编译器cin.in_avail()
- 2、向文件写入数据
- 1.写文件小练习
- 2.如何将信息同时输出到文件和屏幕?
- 3、从文件读数据
- 1.检测文件是否成功打开
- 2.检测是否已到文件末尾
- 3.读取文件小练习
- 4.bad()函数能否用来判断文件流是否成功打开?
1、输入输出类介绍
1.C/C++文件操作对比
2.什么是流?
流是一个数据序列。
一个流是有两个端点的,一边是data source(数据源),一边是程序。
一个I / O流表示输入源或输出目的地。流可以表示许多不同种类的源和目标,包括磁盘文件,设备,其他程序和内存阵列。
流支持许多不同类型的数据,包括简单字节,原始数据类型,本地化字符和对象。一些流只是传递数据;其他则以有用的方式操纵和转换数据。
无论它们在内部如何工作,所有流都向使用它们的程序提供相同的简单模型:流是数据序列。
程序使用输入流从源中一次读取一项数据:
程序使用输出流将数据写入目的地,一次写入一项:
摘自:I / O流
3.C++ I/O流类层次
C++的流类主要有五类:
流基类(ios_base和ios)
标准输入输出流类(istream/ostream/iostream)
字符串流类(istringstream/ostringstream)
文件流类(ifstream/ofstream/fstream)
缓冲区类(streambuf/stringbuf/filebuf)
标准输入输出流对象 cin 和 cout 分别是类 istream 和 ostream 的实例
字符串流:将各种不同的数据格式化输出到一个字符串中,可以使用I/O操纵器控制格式;反之也可以从字符串中读入各种不同的数据。
4.带缓冲的输入输出
C++的I/O流是有内部缓冲区的。
c = cin.get(void)每次读取一个字符并把由Enter键生成的换行符留在输入队列中
#include<iostream>using namespace std;int main() {char c;int i = 0;do {c = cin.get();cout << ++i << " : " <<static_cast<int>(c) << endl;} while (c != 'q');return 0;}
本部分要展示的内容如下:
1、键盘输入一个字母后回车,实际进入缓冲区的是两个字符
2、使用cin输入信息后,有换行字符残留在缓冲区,从而导致return语句前的cin.get()不起作用
#include <iostream>int main()
{//拿到cin对象的缓冲区指针auto p = std::cin.rdbuf();//从键盘读入字符到缓冲区,保留所有字符在缓冲区auto x = std::cin.peek();std::cout << "x= " << x << std::endl;//显示缓冲区中的字符数量//由于每次读取返回值都不一样,所以先将初始值保存auto count = p->in_avail();std::cout << "There are " << count << "characters in the buffer." << std::endl;//把缓冲区的字符都取出来并显示for (int i = 0; i < count; i++){std::cout << i + 1 << ":" << std::cin.get() << std::endl;}std::cin.get();return 0;
}
5.gcc编译器cin.in_avail()
在使用gcc编译器的时候,存在这样一种意外的情况:
无论输入多少个字符, cin.in_avail()函数返回值永远是0。
这是由于GCC编译器中配套的libstdc++实现中的问题(可以看作是标准库gcc实现的一个bug,或者一个特性)。
解决办法是,在使用cin之前,插入下面一行代码
cin.sync_with_stdio(false);
简单解释原因:GCC的libstdc++默认是保证C++的cin与C的stdin同步的。
2、向文件写入数据
ofstrem可向文本文件中写数据.
输出数据的流程:
文件已存在,则直接清除内容。
Writing Data to a File – Auto type recognition (自动类型识别)
1.写文件小练习
本部分要展示的内容如下:
1、创建文件输出流
2、向文件写数据
output << “Lilei” << " " << 90.5 << endl;
output << “HanMeimei” << " " << 85 << endl;
3、关闭文件
4、用文本编辑器打开文件,对比代码语句检查结果
//std::c++latest
#include <fstream>
#include <iostream>
#include <filesystem>using std::ifstream;
using std::ofstream;
using std::cout;
using std::endl;
namespace fs = std::filesystem;int main()
{//第一步,关联文件fs::path p{ "scores.txt" };//第二步,创建一个流输出对象ofstream output{ p };double lileiScore{ 90.5 };int hanmeimeiScore{ 84 };output << "Lilei " << lileiScore << endl;output << "HanMeimei " << hanmeimeiScore << endl;output.close();cout << "size of " << p << " is " << fs::file_size(p) << endl;std::cin.get();return 0;
}
打开一个输出文件流的方法包括:
2.如何将信息同时输出到文件和屏幕?
在软件的调试技术中,很重要的一个技术是将软件运行过程中的一些信息写入到“日志文件”中。但是同时还要将信息显示到屏幕上,以方便程序员实时查看这些信息。
最简单的一种办法是这样的:
std::ofstream output("debug.log", ios::out);
output << __FILE__ << ":" << __LINE__ << "\t" << "Variable x = " << x;
cout << __FILE__ << ":" << __LINE__ << "\t" << "Variable x = " << x;
下面使用streambuf构造一个自己的类,来实现这个功能
#include <streambuf>
#include <iostream>
#include <fstream>//Linux tee命令用于读取标准输入的数据,并将其内容输出成文件。
//tee指令会从标准输入设备读取数据,将其内容输出到标准输出设备,同时保存成文件。
class teebuf : public std::streambuf
{
public:// Construct a streambuf which tees output to both input// streambufs.teebuf(std::streambuf* sb1, std::streambuf* sb2): sb1(sb1), sb2(sb2){}
private:// This tee buffer has no buffer. So every character "overflows"// and can be put directly into the teed buffers.virtual int overflow(int c){if (c == EOF){return !EOF;}else{int const r1 = sb1->sputc(c);int const r2 = sb2->sputc(c);return r1 == EOF || r2 == EOF ? EOF : c;}}// Sync both teed buffers.virtual int sync(){int const r1 = sb1->pubsync();int const r2 = sb2->pubsync();return r1 == 0 && r2 == 0 ? 0 : -1;}
private:std::streambuf* sb1;std::streambuf* sb2;
};class teestream : public std::ostream
{
public:// Construct an ostream which tees output to the supplied// ostreams.teestream(std::ostream& o1, std::ostream& o2);
private:teebuf tbuf;
};teestream::teestream(std::ostream& o1, std::ostream& o2): std::ostream(&tbuf), tbuf(o1.rdbuf(), o2.rdbuf())
{
}int main()
{std::ofstream output("debug.log");//1、创建文件/屏幕输出流对象teeteestream tee(std::cout, output);auto x = 1.1;tee << __FILE__ << ":" << __LINE__ << "\t" << "Variable x = " << x;return 0;
}
效果:
3、从文件读数据
ifstrem可从文本文件中读数据,并检测文件是否成功打开。
了解数据格式
若想正确读出数据,必须确切了解数据的存储格式。
用流提取运算符从文件流中读数据,所读入的信息的长度与流提取运算符右侧的变量的类型有关。
1.检测文件是否成功打开
可能出现错误:
1、读文件时文件不存在
2、写文件时介质只读
检测文件是否正确打开的方法:
1、open()之后马上调用fail()函数
2、fail()返回true, 文件未打开
ofstream output("scores.txt");
if (output.fail()) {cout << R"(Can't open file "scores.txt"!)";}
2.检测是否已到文件末尾
若你不知道文件有多少行,还想把他们全读出来,用eof()函数检查是否是文件末尾
由于get函数返回值是int,所以需要强制类型转换再进行屏幕输出。
ifstream in("scores.txt");
while (in.eof() == false) {cout << static_cast<char>(in.get());
}
3.读取文件小练习
本部分要展示的内容如下:
1、创建文件输入流,打开文件score.txt
2、用>>从文件读取数据
3、使用fail()函数检测文件是否打开
4、将读文件语句放入循环中,使用eof()作为循环条件
5、关闭文件
//std::c++latest
#include <fstream>
#include <iostream>
#include <filesystem>
#include <string>
using std::ifstream;
using std::ofstream;
using std::cout;
using std::endl;
using std::string;
namespace fs = std::filesystem;int main()
{//第一步,关联文件fs::path p{ "scores.txt" };//第二步,创建一个流输入对象ifstream input{ p };//第三步使用fail判断流是否正常打开if (input.fail()){cout << "Can't open file " << p << endl;std::cin.get();return 0;}//第四步,定义一些变量去存这些数据string name{ "" };double score{ 0.0 };//读取文件中的姓名和分数//input >> name >> score;//cout << name << " " << score << endl;//input >> name >> score;//cout << name << " " << score << endl;while (input.eof() == false){cout << static_cast<char>(input.get());}std::cin.get();return 0;
}
4.bad()函数能否用来判断文件流是否成功打开?
在ifstream和ofstream类中,除了fail()函数之外,还有bad()函数。
那么我们能否用bad()函数取代fail()函数判断流是否成功打开?
不能,bad() 如果出现意外的问题,如文件受损或硬件故障,最后一次读取数据的时候发生了这样的问题,方法bad()将返回true.