输入输出流
- I/O (输入/输出) 流类库提供对象之间的交互服务。
- 流类库预定义了一批流对象,连接常用的外部设备。
- 使用者可以定义所需的I/O流对象,使用流库提供的工作方式实现数据传输。
一:流类和流对象
- 在程序中,对数据的输入/输出是以字节流实现的。
- 应用程序对字节序列做出各种数据解释。
- I/O 系统的任务就是在内存和外部设备之间稳定可靠的传输数据和解释数据。
- 流对象可以建立和删除,可以从流中获取数据,也可以向流中添加数据。
流类库
- 流库(stream library)使用继承方法建立的输入输出类库。
- 流库具有两个平行的基类:streambuf 和 ios 类,所有流类均以两者之一作为基类。
- streambuf 提供了对缓冲区的低级操作:设置缓冲区 ; 对缓冲区指针操作 ; 向缓冲区存/取字符。
- ios 类及其派生类提供用户使用流类的接口,支持对 streambuf 的缓冲区输入/输出的格式化或非格式化转换。
streambuf 对缓冲区的低级操作:
ios 提供的用户接口:
头文件
- iostream 包含所有输入输出流所需的基本信息 含有 cin , cout, cerr, clog 对象,提供无格式和格式化的I/O。
- iomanip 包含格式化I/O 操纵算子,用于指定数据输入输出的格式。
- fstream 处理文件信息,包括建立文件,读写文件的各种操作接口。
每种C++版本还包含其他一些与I/O 相关的库,提供特定系统的某些功能。
二:标准流和流操作
- 标准流是C++ 预定义的对象,提供内存与外部设备进行数据交互功能。
- 流的操作是流类的共有成员函数。
标准流为用户常用的外部设备提供与内存之间的通信通道,对数据进行解释和传输,提供必要的数据缓冲。
标准流
- cin istream 类的对象,通常连接键盘,可以重定向。
- cout ostream 类的对象,通常连向显示器,可以重定向。
- cerr ostream 类的对象,连向显示器,不可以重定向。
- clog ostream 类的对象,也用于向标准错误设备(默认设备为显示器)输出有关错误信息,不可以被重定向。但它可以被缓冲,当缓冲区被填满或遇 endl 时向显示器输出信息。
重定向的演示:
//重定向的演示:
#include <iostream>
//#include<iomanip>
//#include<fstream>
using namespace std;
void fun(int a,int b)
{
if (b == 0) {
cerr << "除数遇到0,这个错误消息不能被重定向。 \n";
}
else
{
cout << a << '/' << b << '=' << a / b << endl;
}
}
int main()
{
std::cout << "Hello World!\n";
int n1, n2;
cout << "one:\n";
cin >> n1 >> n2;
fun(n1, n2);
cerr << "tow:\n";
fun(10, 0);
}
在控制台是可以输出 cerr 的信息。
下面利用VS的命令行进行对代码编译,执行,重定向输入和输出。新建一个 txt 文本,把代码拷贝进去,(代码中的中改为英文)更改后缀为.cpp
如下图:
打开开发人员命令行:
打开后切换到源代码目录,使用 cl 命令编译 .cpp 源文件:
执行编译后的 exe文件:在窗口中可以正常输出。
利用重定向符号 > (输出) 和 < (输入) 进行重定向到文件的测试:
新建两个文本文件,num作为输入,out作为输出。
测试结果:cerr 输出不能进行重定向。
输入/输出 流操作:
istream 类的公有成员函数:
函数 | 功能 |
---|---|
read | 无格式输入指定字节数 |
get | 从流中提取字符,包括空格 |
getline | 从流中提取一行字符 |
ignore | 提取并丢弃流中指定字符 |
peek | 返回流中下一个字符,但不从流中删除 |
gcount | 统计最后输入的字符个数 |
eatwhite | 忽略前导空格 |
seekg | 移动输入流指针 |
tellg | 返回输入流中指定位置的指针 |
operstor >> | 提取运算符 |
ostream 类的公有成员函数
函数 | 功能 |
---|---|
put | 无格式插入一个字符 |
write | 从无格式,插入一个字节序列 |
flush | 刷新输出流 |
seekp | 移动输出流指针 |
tellp | 返回输出流中指定位置的指针 |
operstor<< | 插入运算符 |
演示:
#include <iostream>
#include<iomanip>
//#include<fstream>
using namespace std;
//用get函数从键盘输入字符
int main()
{
{
const char* str = "12345";
cout << str << endl;
cout << (void*)str << endl;
int n = strlen(str);
for (size_t i = 0; i < n; i++)
{
cout << *((char*)((void*)str) + i);
}
cout << endl;
}
{
cout.put('h').put('e').put('l').put('l').put('o').put('\n');//等价于 C语言 putchar,但可以链式编程。
char str[20] = "Hello World!";
cout.write(str, strlen(str)).put('\n').put('A').write(str+5,strlen(str+5)).put('\n');//write输出字符串,参数:首地址,长度;.可以链式编程。
cout << "\n\n\n";
//格式化对齐字符,右对齐
cout.width(10);
cout << "123456" << endl;
cout.width(20);
cout << "ABCDEFG" << endl;
cout << "字段填充" << endl;
cout.width(20);
cout.fill('*');
cout << "ABCDEFG" << endl;//输出字符串与填充字符个数共20
cout << "\n\n\n";
}
{
cout << "设置浮点数输出精度:" << endl;
double d = 1.123456789;
cout << d << endl;
cout.precision(9);
cout << d << endl;
float f = 1.00;
cout << f << endl;
cout.precision(3);
cout.setf(ios::showpoint);//设置显示小数点
cout << f << endl;
cout << "\n\n\n";
}
{
int num = 256;
cout <<"十进制:" << dec << num << endl;
cout << "十六进制:" << hex << num << endl;
cout << "八进制:" << oct << num << endl;
}
return 0;
}
#include <iostream>
#include<iomanip>
//#include<fstream>
using namespace std;
void main()
{
{
char c = cin.get(); //get函数 【有返回值且无参数时】不可链式。只接受一个字符。等价于 C getchar函数
cout.put(c).put(':').put(c).put('\n');//put 函数可以链式编程
char c1, c2, c3, c4;
cin.get(c1).get(c2).get(c3).get(c4);
cout.put(c1).put(':').put(c2).put(':').put(c3).put(':').put(c4).put('\n');
}
{
char ch1[10]{0};
char ch2[10]{ 0 };
cin.get(ch1, 10);
cout << ch1 << endl;
cin.ignore();//忽略
cin.get(ch2, 10);
cout << ch2 << endl;
}
cin.get();
}
#include <iostream>
#include<iomanip>
//#include<fstream>
using namespace std;
void main()
{
char str1[20]{ 0 };
cin.getline(str1, 20,'#');//读取一行数据, 制定 # 为结束符。
cout << str1 << endl;
char str2[20]{ 0 };
cin.getline(str2, 20, '#');//读取一行数据, 制定 # 为结束符。
cout << str2 << endl;
cin.get();
}
流的错误状态:
标识常量 | 值 | 含义 |
---|---|---|
goodbit | 0x00 | 状态正常 |
eofbit | 0x01 | 文件结束符 |
failbit | 0x02 | I/O 操作失败,数据未丢失,可以恢复 |
badbit | 0x04 | 非法操作,数据丢失,不可恢复 |
三: 串流
- 串流类是 ios 中派生类。
- C++ 的串流对象可以连接string对象或字符串。
- 串流提取数据时对字符串按变量类型进行解释,插入数据时把类型数据转成字符串。
- 串流I/O具有格式化功能。
演示:istringstream:从输入串流对象提取数据
#include <iostream>
#include <sstream>
#include <cstdlib>
#include <string>
using namespace std;
struct DataInfo
{
string s1;
string s2;
int num;
char c;
double db;
void show()
{
cout << "s1= " << this->s1 << endl;
cout << "s2= " << this->s2 << endl;
cout << "num= " << this->num << endl;
cout << "c= " << this->c << endl;
cout << "db= " << this->db << endl;
}
};
int main()
{
std::cout << "Hello World!\n";
stringstream sstr;//字符串流
sstr.put('A').put(':').put('B').put(':');
sstr << "你好!C++ " << 3.1415926 << ':' << 100;
cout << sstr.str() << endl; //相当于 C语言 sprintf 函数功能。
{
string str("Google Microsoft 125 A 3.14");
DataInfo info;
istringstream iput(str);//建立输入流
iput >> info.s1 >> info.s2 >> info.num >> info.c >> info.db;
info.show();
}
cout << "\n\n\n";
{
char str[] = "Google,Microsoft,125,A,3.14";
for (char* ptr = str; *ptr != '\0'; ptr++) {
if (*ptr == ',') { *ptr = ' '; }
}
DataInfo info;
istringstream iput(str);//建立输入流
iput >> info.s1 >> info.s2 >> info.num >> info.c >> info.db;
info.show();
}
return 0;
}
演示:ostringstream:向输出串流对象插入数据
#include <iostream>
#include <sstream>
#include <cstdlib>
#include <string>
using namespace std;
void main()
{
char str[100]{ 0 };
ostringstream osput(str,sizeof(str));
osput << "abcdefg" << ',' << 12345 << ',' << 12.345 << ',' << 'A';
cout << osput.str() << endl;
cout << str << endl;//str没有数据, 只是起到缓冲区作用
system("pause");
}
演示:ostrstream:向输出串流对象插入数据
#include <iostream>
#include <sstream>
#include <strstream>
#include <cstdlib>
#include <string>
using namespace std;
void main()
{
char buf[80]{ 0 };
ostrstream ossOutput(buf,sizeof(buf));
double x, y;
cout << "Input X:";
cin >> x;
cout << "Input Y:";
cin >> y;
//endl表示输出并换行,ends表示输出加一个空格,flush表示什么都不加,直接输出
ossOutput << x << "*" << y << "=" << x * y << ends; //输出到字符串
cout << buf << endl;
system("pause");
}
四:文件处理
- C++ 把文件当作无结构的字节流,编码方式:文本文件,二进制文件。存取方式:顺序文件,随机文件。
- ifstream , ofstream , fstream 类,用于文件与内存之间的数据传输。
文件和流
文件可以映射到内存,当作内存数据使用文件指针进行操作。我们操作文件,往往都是这种方式来实现的。
打开文件:
包括建立文件流对象;与外部文件进行关联;指定文件的打开方式。
打开文件有两种方式:
- 首先建立流对象,然会调用 fstream::open() 函数连接外部文件: 流对象 对象名; 对象名. open(文件全名,方式);
- 调用流类带参数的构造函数,建立流对象的同时连接外部文件:流类 对象名 (文件全名,方式);
open() 函数原型:
void open(const char* _Filename, ios_base::open_mode _Mode) ;
void open(const string& _Str, ios_base::openmode _Mode = ios_base::in | ios_base::out,
int _Prot = ios_base::_Default_open_prot);
void open(const char* _Filename, ios_base::openmode _Mode = ios_base::in | ios_base::out,
int _Prot = ios_base::_Default_open_prot)
filebuf , ifstream , ofstream , fstream 的构造函数具有相同的参数和缺省值,文件流的构造函数和open() 函数用于打开文件,析构函数在文件流被删除之前关闭文件。
参数 | _Filename | mode | prot |
---|---|---|---|
含义 | 操作文件名 | 打开文件方式 | 打开文件属性和保护方式(应用少,一般用省却值) |
model 参数文件打开方式:
标识常量 | 值 | 含义 |
---|---|---|
ios::in | 0x01 | 读方式打开文件 |
ios::out | 0x02 | 写方式打开文件 |
ios::ate | 0x04 | 打开文件时,指针指向文件尾部 |
ios::app | 0x08 | 追加方式 |
ios::trunc | 0x10 | 删除文件现有内容 |
ios::_Nocreate | 0x40 | 如果文件不存在,则打开操作失败 |
ios::_Noreplace | 0x80 | 如果文件存在,则打开操作失败 |
ios::binary | 0x20 | 二进制方式打开,默认为文本方式 |
演示:
#include <iostream>
#include<fstream>
#include<cstdlib>
using namespace std;
int main()
{
std::cout << "Hello World!\n";
const char* filepath = "D:\\test.txt";
{
ofstream outfile;//创建输出文件流
outfile.open(filepath);
outfile << "C++,使用ofstream类写入文件测试。";
outfile.close();//切记,关闭生效
}
{
char str[256]{ 0 };
ifstream infile;//创建文件输入流
infile.open(filepath);
infile >> str; //如果字符串有空格会被截断
infile.close();
cout << str << endl;
}
system(filepath);//打开创建的文件
}
#include <iostream>
#include<fstream>
#include<cstdlib>
using namespace std;
void main()
{
const char* filepath = "D:\\test.txt";
char str[256]{ 0 };
ifstream infile;//创建文件输入流
infile.open(filepath);
//infile >> str; //如果字符串有空格会被截断
infile.getline(str, sizeof(str)); //读取包含空格等特殊字符
infile.close();
cout << str << endl;
}
#include <iostream>
#include<fstream>
#include<cstdlib>
using namespace std;
void main()
{
std::cout << "Hello World!\n";
const char* filepath = "D:\\test.txt";
{
ofstream outfile;//创建输出文件流
outfile.open(filepath,ios::app);//指定追加模式
outfile << "\r\n 这是追加的一行字符串文本,测试。";
outfile.close();//切记,关闭生效
system(filepath);
}
}
二进文件制读写:
#include <iostream>
#include<fstream>
#include<cstdlib>
#include<string>
#include<sstream>
#include <limits>
using namespace std;
std::string DoubleToString(const double value, unsigned int precisionAfterPoint)
{
ostringstream sout;
// 清除默认精度
sout.precision(std::numeric_limits<double>::digits10);
sout << value;
std::string res = std::move(sout.str());
auto pos = res.find('.');
if (pos == std::string::npos)
return res;
auto splitLen = pos + 1 + precisionAfterPoint;
if (res.size() <= splitLen)
return res;
return res.substr(0, splitLen);
}
struct Info
{
char name[10];
char pwd[10];
int id;
double price;
};
void main()
{
const char* file = "D:\\bit.bin";
cout << "写入二进制文件:" << endl;
Info infos[3] = { {"AAAA","a1a2a3",1,100.32},{"BBBB","b1b2b3",2,120.32} ,{"CCCC","c1c2c3",3,140.32} };
ofstream ofs(file,ios::binary);//指定二进制方式
ofs.write((char*)infos, sizeof(infos));//写入
ofs.close();
cout << "读取二进制文件:" << endl;
Info tmpInfo[3] = { 0 };
ifstream ifs(file, ios::binary);
ifs.read((char*)tmpInfo, sizeof(tmpInfo));
ifs.close();
for (auto& i : tmpInfo)
{
cout << i.name << ' ' << i.pwd << ' ' << i.id << ' ' << i.price << endl;
}
cout << "写入到文本文件中:" << endl;
const char* filetxt = "D:\\info.txt";
ofstream fout(filetxt);
for (auto& i : tmpInfo)
{
string str;
str.append(i.name).append(" ").append(i.pwd).append(" ").append(std::to_string(i.id)).append(" ").append(DoubleToString(i.price,2)).append("\n");
fout << str;
}
fout.close();
system(filetxt);//打开文本文件
cin.get();
}
一个文件要读写同时执行则使用 fstream 流类。
文件指针演示:
#include <iostream>
#include<fstream>
#include<cstdlib>
using namespace std;
void main()
{
const char* file = "D:\\filePtr.txt";
{
ofstream ofs(file);
if (!ofs)
{
cout << "文件操作失败!" << endl;
}
ofs << "123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
ofs.close();
}
{
ifstream ifs(file);
if (ifs.fail())
{
cout << "文件操作失败!" << endl;
}
ifs.seekg(9, ios::beg);//从文件开始,移动文件的可读指针,移动步长为9
char c;
while (ifs.get(c))//读取剩下的每一个字符
{
cout << c;
}
cout << endl;
ifs.close();
}
{
cout << "在指定位置插入文本!" << endl;
//ofstream ofs(file,ios::app); //追加模式对于移动文件指针无效。
//ofstream ofs(file,ios::in|ios::out);//覆盖指针后面数据
ofstream ofs(file);
if (ofs.fail())
{
cout << "文件操作失败!" << endl;
}
ofs.seekp(9, ios::beg);
ofs << "******";
ofs.close();
system(file);
}
cin.get();
}