C++ :输入/输出流

输入输出流

  • I/O (输入/输出) 流类库提供对象之间的交互服务。
  • 流类库预定义了一批流对象,连接常用的外部设备。
  • 使用者可以定义所需的I/O流对象,使用流库提供的工作方式实现数据传输。

一:流类和流对象

  1. 在程序中,对数据的输入/输出是以字节流实现的。
  2. 应用程序对字节序列做出各种数据解释。
  3. I/O 系统的任务就是在内存和外部设备之间稳定可靠的传输数据和解释数据。
  4. 流对象可以建立和删除,可以从流中获取数据,也可以向流中添加数据。
流类库
  • 流库(stream library)使用继承方法建立的输入输出类库。
  • 流库具有两个平行的基类:streambuf 和 ios 类,所有流类均以两者之一作为基类。
  • streambuf 提供了对缓冲区的低级操作:设置缓冲区 ; 对缓冲区指针操作 ; 向缓冲区存/取字符。
  • ios 类及其派生类提供用户使用流类的接口,支持对 streambuf 的缓冲区输入/输出的格式化或非格式化转换。

streambuf 对缓冲区的低级操作:

文件缓冲区管理
字符串缓冲区管理
标准I/O缓冲区管理
streambuf
filebuf
strstreambuf
stdiobuf

ios 提供的用户接口:

输入流
输出流
输入文件流
输入串流
重载运算符 = 号
输入输出流
输出文件流
输出串流
重载运算符 = 号
输入输出流
输入/输出文件流
输入/输出串流
输入/输出标准流
ios
istream
ostream
ifstream
istrstream
istream_withassign
iostream
ofstream
ostrstream
ostream_withassign
fstream
strstream
stdiostream
头文件
  • iostream 包含所有输入输出流所需的基本信息 含有 cin , cout, cerr, clog 对象,提供无格式和格式化的I/O。
  • iomanip 包含格式化I/O 操纵算子,用于指定数据输入输出的格式。
  • fstream 处理文件信息,包括建立文件,读写文件的各种操作接口。

每种C++版本还包含其他一些与I/O 相关的库,提供特定系统的某些功能。

二:标准流和流操作

  • 标准流是C++ 预定义的对象,提供内存与外部设备进行数据交互功能。
  • 流的操作是流类的共有成员函数。

在这里插入图片描述标准流为用户常用的外部设备提供与内存之间的通信通道,对数据进行解释和传输,提供必要的数据缓冲。

标准流
  1. cin istream 类的对象,通常连接键盘,可以重定向。
  2. cout ostream 类的对象,通常连向显示器,可以重定向。
  3. cerr ostream 类的对象,连向显示器,不可以重定向。
  4. 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();
}

流的错误状态:

标识常量含义
goodbit0x00状态正常
eofbit0x01文件结束符
failbit0x02I/O 操作失败,数据未丢失,可以恢复
badbit0x04非法操作,数据丢失,不可恢复

三: 串流

  • 串流类是 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 类,用于文件与内存之间的数据传输。
文件和流

文件可以映射到内存,当作内存数据使用文件指针进行操作。我们操作文件,往往都是这种方式来实现的。
在这里插入图片描述

打开文件:

包括建立文件流对象;与外部文件进行关联;指定文件的打开方式。
打开文件有两种方式:

  1. 首先建立流对象,然会调用 fstream::open() 函数连接外部文件: 流对象 对象名; 对象名. open(文件全名,方式);
  2. 调用流类带参数的构造函数,建立流对象的同时连接外部文件:流类 对象名 (文件全名,方式);

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() 函数用于打开文件,析构函数在文件流被删除之前关闭文件。

参数_Filenamemodeprot
含义操作文件名打开文件方式打开文件属性和保护方式(应用少,一般用省却值)

model 参数文件打开方式:

标识常量含义
ios::in0x01读方式打开文件
ios::out0x02写方式打开文件
ios::ate0x04打开文件时,指针指向文件尾部
ios::app0x08追加方式
ios::trunc0x10删除文件现有内容
ios::_Nocreate0x40如果文件不存在,则打开操作失败
ios::_Noreplace0x80如果文件存在,则打开操作失败
ios::binary0x20二进制方式打开,默认为文本方式

演示:

#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();
}

在这里插入图片描述

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值