本专栏目的
- 更新C/C++的基础语法,包括C++的一些新特性
前言
- 流思想,我认为在计算机中是一个很重要的思想,因为计算机、编程无非就是获取数据,然后对数据进行操作;
- C++给主要给我们提供了3种流,输入输出流、文件流、字符串流,这篇就是小编的学习笔记,希望大家能够批评指正。
文章目录
IO流
咱已经学过了 C 语言,所以都知道,C语言有有一整套完成数据读写(I/O)的解决方案:
- 使用 scanf()、gets() 等函数从键盘读取数据,使用 printf()、puts() 等函数向屏幕上输出数据;
- 使用 fscanf()、fgets() 等函数读取文件中的数据,使用 fprintf()、fputs() 等函数向文件中写入数据。
- 使用sscanf() 读取格式化的字符串中的数据,使用sprintf()把格式化的数据写入到字符串中
要知道,C 语言的这套 I/O 解决方案也适用于 C++ 程序,但 C++ 自己独立开发了一套全新的 I/O 解决方案,其中就包含大家一直使用的 cin 和 cout,但是要注意,cin、cout是类,printf、scanf是函数调用。
C++标准流,提供了一下三个方面的内容:
- 对
系统指定的标准设备
的输入和输出。即从键盘输入数据,输出到显示器屏幕,简称标准I/O。 - 以
外存磁盘文件为对象
进行输入和输出,简称文件I/O。 - 对
内存中指定的空间
进行输入和输出,,简称串I/O。
流的概念:数据传输,输入、输出像水一样,故将这个过程定义为流。
标准输入输出流
类和流对象
重点关注前两个即可。
流类 | 预定义流对象 | 描述 |
---|---|---|
istream | cin(常用) | 标准输入流 |
ostream | cout(常用) | 标准输出流 |
ostream | cerr | 标准错误流(不缓冲数据,直接显示) |
ostream | clog | 标准日志流(缓冲数据) |
标准库定义了 4 个 IO 对象。
-
处理输入时使用命名为cin的 istream 类型对象,称为标准输入。
-
处理输出时使用命名为cout的 ostream 类型对象,称为标准输出。
-
标准库还定义了另外两个 ostream 对象,分别命名为 cerr 和 clog,cerr 对象又叫作标准错误,通常用来输出警告和错误信息给程序的使用者。
-
而 clog 对象用于产生程序执行的一般信息。
标准输入流
cin本身是会略过所有空格、回车、tab的,然后开始读入,遇到空格、回车、tabl停止读入,光标会停留在这些字符之前,意思就是下一次读入也会从空格后开始读取。
>>读取
C++给我们提供了方便的输入方法,直接使用>>即可,但是遇到空格会被截断。
cin.get()
读取一个字符,也可以连续读取
char c;
char buf[20],buf1[20];
//读取一个字符
c = cin.get(); //<==> cin.get(c);
//读取字符串
cin.get(buf, 20);
//读取字符串,遇到指定的字符终止读取
cin.get(buf1, 20, '#');
cout << c << " " << buf<<" "<< buf1;
cin.getine()
读取一行
char buf[20];
cin.getline(buf, 20);
cin.getline(buf, 20, '#');
cin.ignore
忽略
std::string str;
//忽略所有\n
while (cin.peek() == '\n')
{
cin.ignore(1);
}
std::getline(cin, str,'#');
// 输出
cout << str << endl;
/*
输入:
\n
\n
123#
输出:
123
*/
标准输出流
C++格式化输出3种使用方式:
- 通过流成员函数进行输入输出格式控制
// 设置输出内容宽度
cout.width(30);
// 设置填充内容
cout.fill('*');
// 设置对其方式
cout.setf(ios::left);
// 用科学计数法展示
cout.setf(ios::scientific);
// 八进制格式输出
cout << oct << 0xA << endl;
// 十进制格式输出
cout << dec << OxA << endl;
// 大写形势展示十六进制中字母
cout.self(ios::uppercase);
// 十六进制格式显示
cout << hex << OxA << endl;
// 取消科学计数法使用
cout.unself(ios::scientific);
// 设置浮点数显示前N位
cout.precision(3);
cout << 3.1415 << endl; // 输出 3.142,最后一位四舍五入
// ………………………… 还有很多,现用现查即可。
这里有些设置会全局生效,所以有还需要关闭,时候很不方便。
- 通过#include 提供的控制符,进行输入输出格式控制,如下:
控制符 | 作用 |
---|---|
setbase(n) | 设置整数为n进制 |
setfill(n) | 设置字符填充 |
setprecision(n) | 设置浮点数有效位 |
setw() | 设置字段宽度 |
setiosflags(ios::fixed) | 设置浮点数固定小数位输出 |
setiosflags(ios::scientific) | 浮点数以科学计数法表示 |
setiosflags(ios::left) | 左对齐 |
setiosflags(ios::right) | 右对齐 |
当然还有很多,先用现查即可。
案例
cout << setw(30) << setfill('*') << setiosflags(ios::left) << 1.2222233 << endl;
// 输出: 1.22222***********************
而且这个具有临时性,不会影响后面输出。
- 运用C语言的API
可以用C语言的scanf进行格式化输出。
文件流
简介
文件:字节流,即如何从文件读取流和向文件写入流。
C++ 给操作文件提供了一个标准库 fstream,它定义了三个新的数据类型:
使用
其实任何语言文件操作都差不多,打开、关闭、读取与写入(字节、一行、二进制等)。
打开文件
fstream file("maye.txt");
可以通过构造函数指定文件名,并打开;默认就是以可读可写的方式打开的,但是文件不存在会打开失败!
file.open("maye.txt", ios::in | ios::out | ios::trunc);
也可以使用open函数打开文件,使用open必须传递打开模式ios::in
为可读,ios::out
为可写,ios::trunc
为文件不存在时创建,存在则清空所有内容(ios::trunc
必须与ios::out
搭配使用)。
if (!file) //if (!file.is_open()) //两种判断方式都可以
{
cout << "open error:" << strerror(file.rdstate());
}
else
{
cout << "open successed";
}
file.close();
注意: 使用完毕之后并不需要手动关闭文件,但是如果想提前关闭可以使用close()函数。
读取文件
文件打开之后,就可以进行读写了,注意打开模式,ios::in
为可读,ios::out
为可写。
创建stu.txt文件,并写入以下内容,并测试读取!
123 wy 男
456 lt 男
789 wgs 男
110 lyp 男
如果乱码,则将文件存为ansi(win11中记事本)
使用>>读取
std::string buf;
while (!file.eof()) // 判断是否读到了文件最后
{
file >> buf; //读成功返回true
cout << buf << " ";
if (file.peek() == '\n')
{
cout << endl;
}
}
注意,使用>>读取,遇到空格会截断,所以需要循环的去读取,而且还需要自己判断是不是\n。
使用read()读取
char buf[200] ={0};
file.read(buf, 200);
cout << buf;
注意:我们并不知道文件有多大,所以并不能很好的指定读取的大小,从而一次性把文件读取完成。可以先获取文件大小然后再读取。
file.seekp(0,ios::end);
int64_t len = file.tellp(); // 获取文件大小
file.seekp(0,ios::beg);
char* buf = new char[len + 1] {0}; //及其重要
file.read(buf, len);
cout << buf;
delete[] buf;
使用std::getline读取
如果想直接读取到std::string里面,则需要使用std::getline。
std::string buf;
std::getline(file, buf);
cout << buf;
这样读取只能读取一行,如果想要读取多行,则需要使用循环。
std::string buf;
while (!file.eof())
{
std::getline(file, buf);
cout << buf << endl;
}
写入文件
-
如果想要追加写入文件,请添加打开模式
ios::app
-
如果想要二进制读写,请添加打开模式
ios::binary
使用<<写入
使用write写入
打开模式
模式标记 | 适用对象 | 作用 |
---|---|---|
ios::in | ifstream fstream | 打开文件用于读取数据。如果文件不存在,则打开出错。 |
ios::out | ofstream fstream | 打开文件用于写入数据。如果文件不存在,则新建该文件;如果文件原来就存在,则打开时清除原来的内容。 |
ios::app | ofstream fstream | 打开文件,用于在其尾部添加数据。如果文件不存在,则新建该文件。 |
ios::ate | ifstream | 打开一个已有的文件,并将文件读指针指向文件末尾(读写指 的概念后面解释)。如果文件不存在,则打开出错。 |
ios:: trunc | ofstream | 打开文件时会清空内部存储的所有数据,单独使用时与 ios::out 相同。 |
ios::binary | ifstream ofstream fstream | 以二进制方式打开文件。若不指定此模式,则以文本模式打开。 |
ios::in | ios::out | fstream | 打开已存在的文件,既可读取其内容,也可向其写入数据。文件刚打开时,原有内容保持不变。如果文件不存在,则打开出错。 |
ios::in | ios::out | ofstream | 打开已存在的文件,可以向其写入数据。文件刚打开时,原有内容保持不变。如果文件不存在,则打开出错。 |
ios::in | ios::out | ios::trunc | fstream | 打开文件,既可读取其内容,也可向其写入数据。如果文件本来就存在,则打开时清除原来的内容;如果文件不存在,则新建该文件。 |
文件位置指针
istream 和 ostream 都提供了用于重新定位文件位置指针的成员函数。这些成员函数包括关于 istream 的 seekg(“seek get”)和关于 ostream 的 seekp(“seek put”)。
seekg 和 seekp 的参数通常是一个长整型。第二个参数可以用于指定查找方向。
查找方向:
- ios::beg 默认的,从流的开头开始定位
- ios::cur 从流的当前位置开始定位
- ios::end 从流的末尾开始定位
案例
#include <iostream>
#include <fstream>
using namespace std;
struct Gril {
int age;
string name;
};
int main()
{
Girl mygirl[3] = {18, "w", 19, "y", 10, "l"};
// 打开文件
fstream file("./test.txt", ios::in | ios::out | ios::binary | ios::trunc);
if(!file) {
cerr << "file open failed" << endl;
}
// 写入文件
file.write((char*)mygirl, sizeof(mygirl));
// 文件指针移动到开头
file.seekg(ios::beg);
//读
Girl temp[3];
file.read((char*)temp, sizeof(temp));
for(auto& it : temp) {
cout << it.age << " " << it.name << endl;
}
file.close();
return 0;
}
文件操作的一些函数
这张图是当时我学《C++ prime》这本书的时候在博客找的,我感觉还是很清晰的讲解了文件操作相关的函数。
字符串流
在C语言中,我们使用sscanf和sprintf来对字符串进行格式化,格式控制非常不方便,在C++中有了更方便的操作。
使用头文件添加对字符串流的支持。
我感觉,这个最大的作用就是,格式化与反格式化。
打开字符串流
stringstream stream(ios::out | ios::in);
如果支持格式化和反格式化,则需要指定打开模式ios:in
和ios::out
。
stream << 1 <<" " << "234"s <<" "<< 5.20;
使用stream对象,把需要的数据全部格式化,这里每个数据之间用空格隔开,方便等会反格式化。(反格式化以空格为分割字符)
cout << stream.str()<<endl;
当然在反格式化之前,我们使用stream.str()查看已经格式化的数据。
int a;
float b;
string s;
首先我们定义三个对象,来接受从stream中反格式化的数据。
stream >> a >> s >> b;
cout << a <<" " << s << " " << b << endl; // 反格式化的意思就是,输出的时候按照不同的数据类型自动识别,不受输入影响
从string反格式化
stringstream对象还可以绑定一个string对象,直接对string进行操作。
string buf = "1 234 5.20";
stringstream stream(buf);
先准备一个string对象,然后构造stringstream,默认以可读可写方式打开。
int a;
float b;
string s;
stream >> a >> s >> b;
cout << a << " " << s << " " << b << endl; // 反格式化
如果在反格式化之后需要写入内容,是写入不了的,需要调用stream.clear()对格式进行清除,代码如下:
// 清除操作
stream.clear();
stream << "@@" << " ##";
cout << stream.str();
如果这个时候像着追加继续写,则只需要在构造stringstream对象的时候,添加上ios::app
以追加的模式写入即可,如下:
stringstream stream(buf,ios::app | ios::in | ios::out);
重定向流
重定向流:我们一般输入输出一般都是在屏幕、键盘上操作,键盘输入,屏幕输出,重定向流就是可以指定输出位置,比如说输出到文件,不输出到屏幕
代码实重定向
C语言方式
C语言方式可以通过freopen函数实现:
freopen("in.txt","r",stdin); //stdin重定向到in.txt
//...
freopen("CON","r",stdin); //恢复重定向
freopen("out.txt","w",stdout);
freopen("CON","w",stdout);
C++方式
C语言方式可以通过cin.rdbuf函数实现:
fstream fin("in.txt");
streambuf* oldbuf = cin.rdbuf();
cin.rdbuf(fin.rdbuf());
//...
cin.rdbuf(oldbuf);