C++ I/O 类

I/O 类

C++ 不直接处理输入输出, 而是通过一组定义在标准库中的类型来处理 I/O . 这些类型支持从设备读取数据, 向设备写入数据的 I/O 操作, 设备可以是文件, 控制台窗口等. 还有一些类型允许内存 I/O , 即, 从 string 读取数据, 向 string 写入数据

I/O 类主要包含了 iostream, sstream, fstream. iostream 定义了用于读写流的基本类型, fstream 定义了读写命名文件的类型, sstream 定义了读写内存 string 对象的类型

I/O 对象无法拷贝和赋值. 此外, 由于 I/O 操作的函数读写一个 I/O 对象会改变其状态, 因此 I/O 操作函数传递的参数和返回的引用不能为 const

I/O 操作与生俱来的问题是可能发生错误, 因此 I/O 库中定义了一些函数和标志. 即条件状态. 关于这些条件状态可以自行搜索

iostream

iostream 中包含了 istream 和 ostream, 即从流读取数据, 向流写入数据. 而 iostream 则继承了这两个库, 即读写流. 由于 iostream 继承于 istream 和 ostream , 因此在没有熟悉 I/O 中的函数及操纵符之前尽量使用 istream 和 ostream 声明和定义对象

每个 iostream 对象可以使用操纵符来修改流的格式状态, 在之前学习 C++ 中常见的 endl 就是一个操纵符, 其作用是输出一个换行并刷新缓冲区. 下面来介绍一些常用的格式化的 I/O 操纵符

格式化 I/O 操纵符

boolalpha / noboolalpha

将 true 和 false 输出为字符串 / 将 true 和 false 输出为 1, 0

#include <iostream>

using namespace std;

int main(void)
{
	cout << boolalpha << true << ' ' << false << endl;	//输出 true false
	cout << noboolalpha << true << ' ' << false << endl; //输出 1 0

	return 0;
}  

dec / hex / oct

输出十进制 / 十六进制 / 八进制

#include <iostream>

using namespace std;

int main(void)
{
	int x = 10;

	cout << dec << x << endl;	//输出 10
	cout << hex << x << endl;	//输出 a
	cout << oct << x << endl;	//输出 12

	return 0;
}  

showbase / noshowbase

对整型值输出表示进制的前缀 / 对不生成表示进制的前缀

#include <iostream>

using namespace std;

int main(void)
{
	int x = 10;

	cout << showbase << dec << x << endl;	//输出 10 (十进制无前缀)
	cout << showbase << hex << x << endl;	//输出 0xa
	cout << showbase << oct << x << endl;	//输出 012

	cout << noshowbase << dec << x << endl;	//输出 10
	cout << noshowbase << hex << x << endl;	//输出 a
	cout << noshowbase << oct << x << endl;	//输出 12

	return 0;
}

showpos / noshowpos

对非负数显示 + / 对非负数不显示 +

#include <iostream>

using namespace std;

int main(void)
{
	int x = 10;

	cout << showpos << x << endl;	//输出 +10
	cout << noshowpos << x << endl;	//输出 10

	return 0;
}

uppercase / noupppercase

在十六进制值中打印 0X, 在科学记数法中打印 E / 在十六进制值中打印 0x, 在科学记数法中打印 e

#include <iostream>

using namespace std;

int main(void)
{
	int x = 123;

	cout << hex << uppercase << showbase <<x << endl;	//输出 0X7B
	cout << hex << nouppercase << showbase << x << endl;	//输出 0x7b
    
	return 0;
}

scientific

浮点值显示为科学科学记数法

#include <iostream>

using namespace std;

int main(void)
{
	double x = 123;

	cout << scientific << x << endl;	// 输出 1.230000e+02

	return 0;
}

fixed

浮点值显示为定点十进制

#include <iostream>

using namespace std;

int main(void)
{
	double x = 123;

	cout << fixed << x << endl;	//输出 123.000

	return 0;
}

showpoint / noshowpoint

对浮点值总是显示小数点 / 只有当浮点值包含小数部分时才显示小数点

#include <iostream>

using namespace std;

int main(void)
{
    double x = 1.000;

    cout << showpoint << x << endl;		//输出 1.000000
    cout << noshowpoint << x << endl;	//输出 1

    return 0;
}

skipws / noskipws

输入运算符跳过空白符 / 输入运算符不跳过空白符

#include <iostream>

using namespace std;

int main(void)
{
    char x;

    cin >> noskipws;

    while(cin >> x)		//输入1 2 3	
        cout << x;		//输出1 2 3
    cout << endl;

    return 0;
}

hexfloat

浮点值显示为十六进制

#include <iostream>

using namespace std;

int main(void)
{
    double x = 0.125;

    cout << hexfloat << x << endl;	//输出0x8p-6

    return 0;
}

defaultfloat

重置浮点数格式为十进制

#include <iostream>

using namespace std;

int main(void)
{
    double x = 0.125;

    cout << hexfloat << x << endl;		//输出0x8p-6
    cout << defaultfloat << x << endl;	//输出0.125

    return 0;
}

setw

setw(w) 读或写值的宽度为 w 个字符


left / right / internal

在值的右侧添加填充字符 / 在值的左侧添加填充字符 / 在符号和值之间添加填充字符

三种操纵符需要 iomanip 库中的 setw 来实现

#include <iostream>
#include <iomanip>

using namespace std;

int main(void)
{
    int x = 12345;

    cout << left << setw(12) << x << endl;	//输出12345
    cout << right << setw(12) << x << endl;	//输出       12345
    cout << showpos << internal << setw(12) << x << endl;	//输出+      12345

    return 0;
}

部分在 iomanip 中的操纵符

setfill

setfill(ch), 用 ch 填补空白

#include <iostream>
#include <iomanip>

using namespace std;

int main(void)
{
    int x = 12345;

    cout << setfill('*') << setw(12) <<x << endl;	//输出*******12345

    return 0;
}

setprecision

setprecision(n), 将浮点精度设置为 n

#include <iostream>
#include <iomanip>
#include <cmath>

using namespace std;

int main(void)
{
    cout << setprecision(12) << sqrt(2) << endl;	//输出1.41421356237

    return 0;
}

关于 os.precision

os.precision(n) 作用与 setprecision 相同, 不过其返回值为设置前的浮点数精度 (默认为6), 因此往往它作为单独的语句.

setbase

setbase(n), 将整数输出为 b 进制

#include <iostream>
#include <iomanip>
#include <cmath>

using namespace std;

int main(void)
{
    int x = 12345;

    cout << setbase(10) << showbase << x << endl;	//输出12345
    cout << setbase(16) << showbase << x << endl;	//输出0x3039
    cout << setbase(8) << showbase << x << endl;	//输出030071

    return 0;
}

未格式化的 I/O 操作 (单字节)

未格式化 I/O 操作允许我们将一个流当作一个无解释的字节序列来处理

is.get(ch) / os.put(ch)

从 istream is 读取下一个字节存入字符 ch 中. 返回 is / 将字符 ch 输出到 ostream os. 返回 os

#include <iostream>

using namespace std;

int main(void)
{
    char ch;

    cin.get(ch);	//输入 a
    cout.put(ch);	//输出 a

    return 0;
}

is.get()

将 is 的下一个字节作为 int 返回

#include <iostream>

using namespace std;

int main(void)
{
    int x;

    x = cin.get();	//输入 A

    cout << x << endl;	//输出 65

    return 0;
}

is.putback(ch)

将字符 ch 放回 is. 返回 is

#include <iostream>

using namespace std;

int main(void)
{
    char a, b;

    cin.putback('c');
    cin >> a;

    cin.putback('d');
    cin >> b;

    cout << a << ' ' << b << endl;	//	输出 a b

    return 0;
}

is.unget()

将 is 向后移动一个字节. 返回 is

#include <iostream>

using namespace std;

int main(void)
{
    char a, b, c;

    cin >> a >> b >> c;			//输入abcdef
    cout << a << b << c << endl;	//输出abc

    cin.unget();		//将 c 放回输入流中

    char firstchar, secondchar;

    cin >> firstchar >> secondchar;
    cout << firstchar << secondchar << endl;	//输出cd

    return 0;
}

is.peek()

将下一个字节作为 int 返回, 但不从流中删除它

#include <iostream>

using namespace std;

int main(void)
{
    char a, b, c;

    cin >> a >> b >> c;		//输入abcdef
    cout << a << b << c << endl;	//输出abc

    int x = cin.peek();

    cout.put(x);	//输出d

    return 0;
}

关于 putback() 和 unget()

putback() 可以将指定要后退的字符放入流中, unget() 则是将最后读取的字符放入流中

未格式化的 I/O 操作 (多字节)

is.get

is.get(sink, size, delim) 从 is 中读取最多 size 个字节, 并保存在字符数组中, 字符数组的起始地址由 sink 给出. 读取过程中直到遇到字符 delim 或读取了 size 个字节或遇到文件尾时停止. 如果遇到了 delim , 则将其留在输入流中, 不读取出来存入 sink 中

#include <iostream>

using namespace std;

char str[14];

int main(void)
{
    cin.get(str, 14, ' ');	//输入Hello World

    cout << str << endl;	//输出Hello

    cin >> str;

    cout << str << endl;	//输出 World

    return 0;
}

is.getline

is.getline(sink, size, delim) 与 get 版本类似, 但会读取并丢弃 delim

#include <iostream>

using namespace std;

char str[14];

int main(void)
{
    cin.getline(str, 14, 'W');	//输入Hello World!

    cout << str << endl;	//输出Hello 

    cin >> str;

    cout << str << endl;	//输出 orld!

    return 0;
}

is.read

is.read(sink, size) 读取最多 size 个字节, 存入字符数组 sink 中. 返回 is.

#include <iostream>

using namespace std;

char str[14];

int main(void)
{
    cin.read(str, 14);	//输入Hello World!!

    cout << str << endl;	//输出Hello World!!

    return 0;
}

is.gcount

返回上一个未格式化读取操作从 is 读取的字节数

#include <iostream>

using namespace std;

char str[14];

int main(void)
{
    cin.get(str, 14, ' ');	//输入Hello World!

    cout << cin.gcount();	//输出 5

    return 0;
}

os.write

将字符数组 source 中的 size 个字节写入 os. 返回 os

#include <iostream>

using namespace std;

int main(void)
{
    const char* str = "Hello, World!";

    cout.write(str, 14);	//输出Hello, World!

    return 0;
}

sstream

sstream 定义了三个类型来支持内存 I/O, 这些类型可以向 string 写入数据, 从 string 读取流, 就像 string 是一个 I/O 流一样. 此外 sstream 也继承了 iostream 的部分特性, 例如各种操纵符等.

istringstream 从 string 读取数据, ostringstream 向 string 写入数据, 而头文件 stringstream 既可从 string 读取数据也可向 string 写入数据. 下面记录一些 sstream 中的基本操作. 同 iostream , 尽量先使用 istringstream 和 stringstream声明和定义对象

sstream strm(s)

strm 是一个 sstream 对象, 保存 string s 的一个拷贝. 此构造函数是 explicit 的 (即无法进行隐式类型转换)

#include <iostream>
#include <string>
#include <sstream>

using namespace std;

int main(void)
{
    string str = "Hello, World!";
    stringstream STR(str);	//此时 STR 中保存了一个 str 的副本

    return 0;
}

strm.str()

返回 strm 所保存的 string 的拷贝

#include <iostream>
#include <string>
#include <sstream>

using namespace std;

int main(void)
{
    string str = "Hello, World!";
    stringstream STR(str);

    cout << STR.str() << endl;	//输出 Hello, World!

    return 0;
}

strm.str(s)

将 string s 拷贝到 strm 中

#include <iostream>
#include <string>
#include <sstream>

using namespace std;

int main(void)
{
    string str1 = "Hello, World!";
    string str2 = "Hello";
    stringstream STR(str1);

    cout << STR.str() << endl;	//输出Hello, World!

    STR.str(str2);

    cout << STR.str() << endl;	//输出Hello

    return 0;
}

关于 stringstream 的重载运算符 >> 和 <<

由于 stringstream 是继承 iostream (istream, ostream ) 和 istream, ostream 的类, 因此其中也包含了它们中的重载运算符 >><<, 在 stringstream 中使用它们能够非常方便的进行字符串与数字之间的转换

#include <iostream>
#include <string>
#include <sstream>

using namespace std;

int main(void)
{
    stringstream sstr;

    sstr << 12345;
    cout << sstr.str() << endl; //输出12345
    sstr.str("");	//每次stingstream对象操作完后尽量将其清空
    sstr.clear();	//清除stringstream的 I/O 状态

    sstr << showbase << hex << 0xfffff;
    cout << sstr.str() << endl; //输出0xfffff
    sstr.str("");
    sstr.clear();

    int x = 0;
    int y = 0;

    sstr << dec << "12345";
    sstr >> x;
    cout << x << endl;		//输出12345
    sstr.str("");
    sstr.clear();

    sstr << hex << "0xfffff";
    sstr >> y;
    cout << showbase << hex << y << endl;	//输出0xfffff
    sstr.str("");
    sstr.clear();

    return 0;
}

fstream

fstream 类继承自 ifstreamofstream, 即读取文件流中的信息, 写入文件流中的信息, 而 fstream 则既可以读取文件流中的信息又可以写入文件流中的信息. 下面记录了一些 fstream 中的基本操作. 同 iostream , 先使用 ifstream 和 ofstream 声明和定义对象

fstream fstrm(s)

创建一个 fstream, 并打开名为 s 的文件. s 可以是 string, 也可以是 C 风格的字符串, 这些构造函数都是 explicit 的

#include <iostream>
#include <string>
#include <fstream>

using namespace std;

int main(int argc, char* argv[])
{
    string path = "C:\\notepad.exe";	//我将C:\Windows\System32\notepad.exe复制到了C:\

    fstream fstrm(path);

    return 0;
}

fstream fstrm(s, mode)

在前一个构造函数上添加了打开文件的方式

打开文件的方式
文件模式 解释
in 以读的方式打开
out 以写的方式打开
app 每次写操作均定义到文件末尾
ate 打开文件后立即定位到文件末尾
trunc 截断文件
binary 以二进制方式进行 I/O

#include <iostream>
#include <string>
#include <fstream>

using namespace std;

int main(int argc, char *argv[])
{
    string path = "C:\\text.txt";

    fstream fstrm(path, ios::out);	//在C盘创建一个text.txt

    return 0;
}

fstream.open(s)

打开名为 s 的文件, 并将文件与 fstream 绑定. 其他可参考 fstream 的构造函数

#include <iostream>
#include <string>
#include <fstream>

using namespace std;

int main(int argc, char *argv[])
{
    string path = "C:\\text.txt";

    fstream fstrm;

    fstrm.open(path, ios::out);	//将在C盘创建一个text.txt

    return 0;
}

fstrm.close()

关闭与 fstream 绑定的文件, 返回 void

#include <iostream>
#include <string>
#include <fstream>

using namespace std;

int main(int argc, char *argv[])
{
    string path = "C:\\text.txt";

    fstream fstrm;

    fstrm.open(path, ios::out);

    fstrm.close();	//与path绑定的fstrm已关闭

    return 0;
}

fstrm.is_open()

判断文件是否已经打开且尚未关闭, 返回一个 bool 值

#include <iostream>
#include <string>
#include <fstream>

using namespace std;

int main(int argc, char *argv[])
{
    string path = "C:\\text.txt";

    fstream fstrm;

    fstrm.open(path, ios::out);

    cout << fstrm.is_open() << endl;	//输出 1

    fstrm.close();

    cout << fstrm.is_open() << endl;	//输出 0

    return 0;
}

流随机访问

标准库提供了一对函数, 来定位 (seek) 到流中给定的位置, 以及告诉 (tell) 我们当前位置. 在大多数系统中, cin、cout、cerr 和 clog 的流不支持随机访问.

seek 和 tell 函数

seek 函数给定位置, tell 函数标记当前位置, seek 和 tell 函数都有 g(get) 和 p(put) 版本, 分别指 "获取 (读取) 数据""放置 (写入) 数据"

seekg(off, from) / seekp(off, from)

在一个输入流或输出流中将标记定位到 from 之前或之后 off 个字符, from 可以是下列值之一

beg : 偏移量相对于流开始的位置

cur : 偏移量相对于流当前的位置

end : 偏移量相对于流结束的位置

#include <iostream>
#include <string>
#include <fstream>

using namespace std;

int main(int argc, char *argv[])
{
    string path = "C:\\notepad.exe";

    fstream fstrm(path);

    fstrm.seekg(0, ios::end);

    return 0;
}

tellg() / tellp()

返回一个输入流中 (tellg) 或输出流中 (tellp) 标记的当前位置

#include <iostream>
#include <string>
#include <fstream>

using namespace std;

int main(int argc, char *argv[])
{
    string path = "C:\\notepad.exe";

    fstream fstrm(path);

    fstrm.seekg(0, ios::end);

    cout << fstrm.tellg() << endl;	//输出132096

    return 0;
}

seekg(pos) / seekp(pos)

在一个输入流或输出流中将标记重定位到给定的绝对地址. pos 通常是前一个 tellg 或 tellp 的返回值, 在此不再赘述

关于文件的读写

fstream 同样继承了 iostream , 因此 fstream 创建的对象可以使用部分 iostream 的操作

#include <iostream>
#include <string>
#include <fstream>

using namespace std;

char* text = nullptr;
int text_size = 0;

int main(int argc, char* argv[])
{
	string path = "C:\\notepad.exe";
	string new_path = "C:\\a.exe";

	ifstream file_in(path, ios::binary | ios::in);	//二进制读取notepad.exe

	if (file_in.is_open())
	{
		file_in.seekg(0, ios::end);
		text_size = file_in.tellg();	//得出文件的字节数

		text = new char[text_size] {0};

		file_in.seekg(0, ios::beg);		//将偏移量移动至文件起始处开始读取
		file_in.read(text, text_size);
	}

	else
		cout << "Failed !" << endl;

	ofstream file_out(new_path, ios::binary | ios::out);	//二进制写入a.exe, 没有a.exe则会创建一个

	if (file_out.is_open())
	{
		file_out.seekp(0, ios::beg);	//将偏移量移动至文件起始处开始写入
		file_out.write(text, text_size);
	}

	return 0;	//C盘会出现一个名为a.exe, 内容和notepad.exe一模一样的可执行文件
}
上一篇:12.3赛


下一篇:2020年西安电子科技大学新生赛现场赛代码