C++ IO

C++ IO


IO类
头文件
iostreamistream:从流读取数据 ostream:向流写入数据 iostream:读写流
fstreamifstream:从文件读取数据 ofstream:向文件写入数据 fstream:读写文件
sstreamistringstream:从string读取数据 ostringstrem:向string写入数据 stringstrem:读写string

类型ifsteam和istringstrem都继承自istream,类型ofsteam和ostringstrem都继承自ostream,因此我们可以像使用cout和cin那样使用这些类,用<<和>>读写数据

IO对象无拷贝或赋值

我们不能拷贝或对IO对选购赋值:

ofstream out1,out2;
out1 = out2; //错误,不能赋值
ofstream print(ofstream); //错误,不能初始化ofstream参数
out2 = print(out2); //错误,不能拷贝流对象

由于IO对象不能拷贝,当其作为形参或返回类型时,应当按引用传递,又由于读写一个IO对象会改变其状态,故传递和返回的引用不能是const的

条件状态

一个流一旦发生错误,其后续的IO操作都会失败。确当一个流对象的最简单办法就是将它当作一个条件来使用while (cin >> word)

文件IO

头文件定义的三个支持文件IO的类型:ifstream读文件,ofstream写文件,fstream读写文件。可以使用<<、>>和getline来读写文件。

fstream fstrm创建一个未绑定的文件流,fstream为三种文件IO类型之一
fstream fstrm(s)创建一个文件流并绑定文件s(string类型或者C风格字符串),使用默认mode
**fstream fstrm(s,mode) **使用mode打开文件
fstrm.open(s)打开名为s的文件,并与fstrm类绑定
fstrm.close()关闭与fstrm绑定的文件
fstrm.is_open()返回bool,指出与fstrm关联的文件是否成功打开且尚未关闭

创建一个文件流:

std::ifstream in(ifile);
std::ofstream out;
out.open(ifile+"copy");
if (out)  //由于open可能失败,有必要检查out
/*其他操作*/
in.close();
in.open(otherFile);

文件流对象在离开作用域时将被析构,析构函数里自动调用close

文件模式
in以读方式打开
out以写方式打开
app每次写操作前均定位到文件末尾
ate打开文件后立即定位到文件末尾
trunc截断文件
binary以二进制方式进行IO
  • 只能对ofstream或fstream对象设定out模式
  • 只可以对istream和fstream对象设定in模式
  • 只有当out模式也被设定时才可以设定trunc模式
  • 只要trunc没被设定,就可以设定app模式,在app模式下,文件总是以输出方式被打开
  • 即使没有指定trunc,以out模式打开的文件也会被截断,为了保留文件内容,必须同时指定app模式;或同时指定in模式
  • ate和binary模式可用于任何类型的文件流对象,且可以和其他任何类型组合

由于以out模式打开文件会丢失已有数据,阻止其清空文件内容的方法是指定app模式和in模式

ofstream app("file", ofstream::app); 
ofstream app2("file", ofstream::out | ofstream::app);
string流

sstream头文件定义了三种类型来支持内存IO,这些类型可以向string写入数据,从string读取数据:

istringstream从string读取数据,ostringstream向string写入数据,stringstream可读可写

sstream strm创建一个未绑定的stringstream对象
sstream strm(s)创建一个stringstream对象,保存string s的拷贝
strm.str()返回strm所保存的string的拷贝(调用以获得结果string)
strm.str(s)将string s拷贝到strm中。返回void

示例:要将一行来自文件的数据读取并存入类中:

lee 2015552368 862555012

morgan 6095550132

类定义:

class PersonInfo{
public:
	std::string name;
	std::vector<std::string> phones;
};

有:

#include<sstream>
/*...*/
std::string line,word;  //保存来自输入的一行和一个单词
std::vector<PersonInfo> people; //保存所有来自文件的人物信息记录
std::ifstream in("file");
while (std::getline(in,line)) {   //从文件中读取一行数据
	PersonInfo temp;  
	std::istringstream recond(line); //绑定读取的一行数据
	recond >> temp.name;  //先读取名字
	while (recond >> word) //读取电话
		temp.phones.push_back(word);//保存电话
	people.push_back(temp); //存入vector
}

当我们希望逐步构造输出最后一并打印时,可以用ostringstream。对于上一节的例子,我们希望逐个验证号码并改变其格式,如果所有号码都是有效的,我们希望输出一个新文件,包含改变格式后的号码,对于无效的号码,我们将不会输出其到新文件中,而是打印一条包含人名和无效号码的错误信息。我们只有在验证完所有号码时才将其输出到文件中,所有可以将其先输出到一个内存ostringstream流中:

std::ofstream out("file");
for (const auto& entry: people){
	std::ostringstream formatted,badNums; //分别保存格式化后的和错误的号码
	for (const auto& nums : entry.phones){
		if (!valid(nums))
			badNums << " " << nums
		else 
			formatted << " " << format(nums);
	}
	if (badNums.str.empty()) //所有号码无错误
		out << entry.name << " " << formatted.str() << std::endl;
	else
		std::cerr << "input error:" << entry.name << "invaild number(s)"
        << badNums.str() << std::endl;
}
流随机访问
tellg()返回输入流中标记的当前位置
tellp()返回输出流中标记的当前位置
seekg(pos)在一个输入流或输出流中将标记重定位到给定的绝对位置,通常是前一个tellg或tellp返回的值
seekp(pos)
seekp(off,from)定位到from之前或之后off个字符,from有以下取值:
seekg(off,from)beg:偏移量相对于流开始位置,cur:当前位置,end:流解为位置(如:fstream::brg)

g版本表示我们正在获得(gain)数据,p表示我们正在放置(place)数据.istrem和ifstream、istringstrem使用g版本,ostream和ofstream、ostringstream使用p版本,iostream和fstream、stringstream两个版本都可以使用

虽然有g、p版本之分,但是流中的标记只有一个,并不存在单独的读标记或者写标记

//记住当前的位置:
std::ostringstream writeStr;
std::ostringstream::pos_type mark = writerStr.tellp();
//...
if (cancelEntry)
	//回到刚才记住的位置
	writeStr.seekp(mark);

读写同一个文件

假定已经给定了一个要读取的文件,我们将要在此文件的末尾写入新一行,这一行包含文件中每行的相对起始位置。例如有以下文件:

abcd
efg
hi
j

将要输出:

abcd
efg
hi
j
5 9 12 14

注意每行不可见的换行符

程序将逐行读取文件,对每一行递增计数器,将刚刚读取的一行的长度加到计数器上,此即下一行的起始位置:

int main() {
	std::fstream inOut("copyOut",std::fstream::ate | std::fstream::in | std::fstream::out);
	if (!inOut){
		std::cerr << "Unable to open file!" << std::endl;
		return EXIT_FAILURE;
	}
	auto end_mark = inOut.tellg();
	inOut.seekg(0,std::fstream::beg);
	size_t count = 0;
	std::string line; 
	while (inOut && inOut.tellg() != end_mark && getline(inOut,line)) {
		cnt += line.size()+1;    //读取一行便增加一行的偏移量并输出至文件末尾
		auto mark = inOut.tellg(); //记住当前的读位置
		inOut.seekp(0,std::fstream::end);
		inOut << cnt;
		if (mark != end_mark) inOut << " "; //如果没有读至最后一行,需要在两个数字之间输出一个分隔符
		inOut.seekg(mark); //恢复到读的位置继续读
	}
	inOut.seekp(0,std::fstream::end);
	inOunt << "\n"; //于末尾换行
	return 0

}
iostrem迭代器

可用于IO类型对象的迭代器istream_iteratorostream_iterator,使用时,应将之与一个流进行绑定(如cout)

isfream_iterator操作
istream_iterator in(is)in从输入流is读取类型为T的值。T必须支持>>运算符
istream_iterator end读取类型为T的默认初始化istream_iterator迭代器,表示尾后位置
in1 = in2in1和in2必须读取相同的类型,如果它们都是尾后迭代器(即默认初始化的迭代器)或绑定到相同的输入,则true
*in返回从流中读取的值
++in,in++使用元素类型所定义的>>运算符从输入流中读取下一个值,前一个版本返回指向自增后迭代器的引用,后一个返回旧值
std::ifstream in("ifile");
std::istream_iterator<std::string> isIt(in),eof;//通常应该一并定义eof
while (isIt != eof) 
	std::cout << *isIt++;

使用算法操作流迭代器:

std::istream_iterator<int> inIt(in),eof;
int sum = std::accumulate(inIt,eof,0);

我们可以对任何具有输出运算符<<的类型定义ostream_iterator.在初始化一个该类型对象时,除了被绑定的流作为第一个参数,我们还可以提供第二个参数—一个C风格的字符串,表示在输出每个元素后都会打印此字符串。ostream_iterator无默认初始化,即没有eof

ostream_iterator操作
ostream_iterator out(os)out将类型为T的对象写入os流中
ostream_iterator out(os,d)每个值后面将输出一个d,d指向一个空字符结尾的字符数组
out = val用<<将val写入out所绑定的ostream中
*out,++out,out++不对out做任何事情,全部返回out
std::ostream_iterator<int> outIt(std::cout," ");
for (auto i : vec)
	outIt = i; //或*outIt++ = i,与istream_iterator对应的写法;
std::cout << std::endl;

拷贝算法接受三个迭代器,前两个表示一个输入范围第三个表示目的序列的起始位置,目的序列应至少包含与输入序列一样的元素;算法返回目的迭代器的值,指向拷贝至目的序列尾元素之后的值。

可以通过copy来打印vec中的元素,这比写循环更为简单(copy中含有循环,并自动递增目的迭代器):

std::ostream_iterator<int> outIt(std::cout," ");
std::copy(vec.begin(),vec.end(),outIt);
std::cout << std::endl;
格式化输入与输出

让bool类型输出为true/false而不是1/0:

std::cout << "default bool values: " << true << " " << false
	      << "\nalpha bool values: " << boolalpha
	      << true << " " << false << std::endl;
std::cout << noboolalpha //恢复为1/0格式

指定整数型的进制:

std::cout << "default: " << 20 << " " << 1024 << std::endl; 
std::cout << "octal: " << std::oct << 20 << " " << 1024 << std::endl; //八进制
std::cout << "hex: " <<std::hex << 20 << " " << 1024 << std::endl; //十六进制
std::cout << "decimal: " << std::dec << 20 << " " << 1024 << std::endl; //十进制
//浮点型的表示形式不受影响
std::cout << std::showbase; //将输出当前的进制
std::cout << std::noshowbase 

控制浮点数输出的三个格式:

  • 精度
  • 进制
  • 没有小数部分的浮点值是否打印小数点
  1. 指定打印精度

    使用IO对象的precision成员函数指定的精度,带int参数的版本用于设定,void返回当前的精度:

    std::cout.precision(12);
    std::cout << "Precision: " << std::cout.precision() << ", Value: " << std::sqrt(2.0) << std::endl;
    //另一种设置精度的方式是使用setprecision
    std::cout << std::setprecision(3) << std::cout.precision() << ", Value: " << std::sqrt(2.0) << std::endl;
    
  2. 打印小数点:

    默认情况下,当一个浮点数的小数部分为零时,不显示小数点,showpoint强制打印小数点:

    std::cout << 10.0 << std::endl;//打印10
    std::cout << std::showpoint << 10.0 //打印10
    		  << std::noshowpoint << std::endl; // 恢复默认
    

输出补白:

  • setw:指定下一个字符串或数字的最小空间
  • left:左对齐输出
  • right:右对齐输出(默认)
  • internal:控制负数的符号的位置,它左对齐符号,右对齐值,用空格填满中间空间
  • setfill:允许指定一个字符代替默认的空格来补白输出
std::cout << "i: " << setw(12) << i << std::endl;
std::cout << std::setfill('#');

控制输入格式:
默认情况下,输入运算符会忽略空白符(包括回车),以下循环

char ch;
while (std::cin >> ch)
	std::cout << ch;

当输入以下序列时,

a b cd

循环会执行4次,跳过中间的空格,输出为abcd。操作符noskipws会令输入运算符读取空白符。为恢复默认,可以使用skipws

std::cin >> noskipws;
while (cin >> ch)
	std::cout << ch;
std::cin >> skipws; //恢复默认
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值