I/O操作 – 文件流

时间:2022-06-10 10:03:13

1)          C++ 文件I/O流结构

include<fstream>,同时最好也include<iostream>,为了可移植性,因为fstream不一定会include<iostream>

 

l       ios_base基类 public ios

定义了所有流共有的内容,不依赖于流所处理的字符类型。

 

l       basic_ios<charT> public ios_base

定义跟字符相关的输入输出共有的类,一般很少使用。

 

l       basic_istream<charT> : public basic_ios<charT>

typedef basic_istream<char> istream;

typedef basic_istream<wchar_t> wistream;

 

istream cin;

 

l       basic_ifstream<charT> : public basic_istream <charT>

typedef basic_ifstream<char> ifstream;

typedef basic_ifstream<wchar_t> wifstream;

 

l       basic_ostream<charT> : public basic_ios<charT>

typedef basic_ostream<char> ostream;

typedef basic_ostream<wchar_t> wostream;

 

ostream cout;

ostream cerr;

 

cerrcout的不同是cerr不带缓冲区,比较快。

 

l       basic_ofstream<charT> : public basic_ostream <charT>

typedef basic_ofstream<char> ofstream;

typedef basic_ofstream<wchar_t> wofstream;

 

 

l       basic_iostream<charT>

: public basic_istream<charT>, 

  basic_ostream<charT>

                      typedef basic_iostream<char> iostream;

 

l       basic_fstream<charT> : public basic_iostream <charT>

typedef basic_fstream<char> fstream;

typedef basic_fstream<wchar_t> wfstream;

 

2)          C++文件处理

l       文件打开模式

ios::in

                      可用于读取文件;防止更改或截断文件;

                      当用ofstream时,以这种模式打开的文件,

           可往文件中间覆盖存在的内容。

 

ios::out

当用ifstream时,以这种模式打开也可以写入文件,参看下文。

 

ios::app

仅用于追加文件内容

以这个方式打开文件,无论怎么移动文件指针,都只能往文件末尾追加。

 

ios::ate

如果文件存在,文件指针默认为文件末尾。

当用ofstream时,以这种模式打开和trunc是一样的效果,文件内容被清空。

 

ios::trunc

如果文件存在,则清空文件内容

默认输出是这样方式,如果没有指定app in

 

ios::binary

文件以二进制打开,默认为文本方式。

只在windows上存在的方式:文件系统用回车(13)/换行(10)来表示行的结束符。当写入’/n’ (10)时,系统写入回车换行符。读取时做相反处理。

而用二进制方式读写时,系统会忽略这种转换,完全按字节存取。

 

例子:

ios::in | ios::out | ios::binary

 

l       优雅的文件流缓冲 推荐的文件存取方式

结构定义

basic_streambuffer<charT>

basic_filebuf<charT>: public basic_streambuffer<charT>

basic_stringbuf<charT>: public basic_streambuffer<charT>

 

流接口

任何流对象都可以通过rdbuf()获取流缓冲

 

使用例子

ifstream inFile(“a.txt”);

cout<<inFile.rdbuf()<<endl; //输出文件内容

 

//需要重置文件指针

inFile.seekg(0,ios::beg);

 

ofstream outFile(“b.txt”);

outFile<<inFile.rdbuf(); //拷贝文件。

 

l       文件流定位

记住当前流指针

ofstream –  streampos tellp()

tellp意思是tell me the put pointer

ifstream – streampos tellg()

tellg 意思是tell me the get pointer

 

绝对移动文件指针

利用streampos.seekpos()即可返回到指定的指针

比如,ofstream outFile(“a.txt”);

streampos pos = outFile.tellp();

pos.seekpos();

 

流位置定义

流的开始位置 0 – ios::beg

流的当前位置 1 – ios::cur

流的结束位置 2 – ios::end

 

相对移动文件指针

ofstream – seekp(字节数,方向)

字节数可正可负,注意合理性,比如指针为开始位置,字节数应为正。

 

ifstream – seekg(字节数,方向)

同上。

 

l       只能覆盖文件部分内容 不能往中间插入

ofstream out1("d://a.txt", ios::in);

out1.seekp(10,0);

out1.write("hello every one!",sizeof("hello every one!"));

out1.close();

 

l       输入输出文件

方式1

 fstream,设置ios::in|ios::out标志。

                     当从读变成写或从写变成读时,须重定位文件流或

           刷新流(flush)

 

方式2

ifstream inFile(“a.txt”, ios::in|ios::out);

ostream outFile(inFile.rdbuf());

//可移动指针outFile用来写

//可移动指针inFile用来读

                                outFile<<”内容”;

                                cout<<inFile.rdbuf();

 

3)          字符串输入输出流

include <sstream>

 

l       字符串流结构与文件流一样

basic_istringstream  替换 basic_ifstream

basic_ostringstream 替换 basic_ofstream

basic_stringstream  替换 basic_fstream

 

l       输入流用法

字符串转换

istringstream strStream(“345 45 1.54 hello”);

int i = 0; //345

int j = 0; //45

float k = 0; //1.54

string str; //hello

strStream >> i>>j>>k>>str;

 

如果改成istringstream strStream(“345s 45 1.54 hello”);

输出为:345 0 0

用这个可将小数点前后的变成整数。

 

类型转换

void DateInput(string& str)

{

istringstream strStream(str);

Date date;

strStream >> date;

if( strStream ) //bool返回值

{

//date正确

}

else

{

//输出错误
}

}

 

l       输出流用法

格式化字符串

ostringstream strStream;

int a = 10;

float b = 1.5;

string str = “yeming”;

strStream << a<<b<<str;

cout<<strStream.str();

 

 

将文件内容变成字符串

ifstream inFile(“a.txt”);

ostringstream os;

os<<inFile.rdbuf();

 

4)          输出流格式化1 – 函数调用

l       改变标志

fmtflags ios::flags(fmtflags newflags); //改变所有标志

fmtflags ios::setf(fmtflags mask); //可用 | 连接标志

fmtflags ios::unsetf( fmtflags mask); //清除标志

fmtflags ios::setf(fmtflags bit, fmtflags field); //请看Field标志部分

 

l       /关标志

ios::skipws

跳过空格,默认输入流就是跳过空格

 

ios::showbae

显示基数前缀

 

ios::showpos

cout.setf(ios::showpos)显示数字正负号

 

ios::unitbuf

cout.set(ios::unitbuf) 使得每次输出后都刷新缓冲区到文件中

cout<<”hello”;

cout<<”yeming”;

cerr就是把这个标志打开了,所以如果错误输出太多,会影响效率。

 

ios::showuppercase

输出十六进制时A…F是大写,科学计算为E

 

ios::showpoint

显示浮点数时,会在后面显示0

比如cout<<0.234f; //显示0.234000

 

l       Field标志

ios::basefield

-        ios::dec  //使整数为10进制,没有前缀

-        ios::hex  //16进制,0x

-        ios::oct  //8进制,0

cout.setf(ios::hex, ios::basefield);

 

                      ios::adjustfield

-        ios::left     //数字左对齐,用字符填充右边空位

-        ios::right    //数字右对齐,用字符填充左边空位,默认方式

-        ios::internal  //字符填充于正负号或基数符号的后面

   cout.setf(ios::internal, ios::adjustfield);

   +0000124; //而不是0000+124

 

                      ios::floatfield

-        ios::scientific //以科学技术显示浮点数(e的写法)

-        ios::fixed    //以固定格式显示浮点数

cout.setf(ios::scientific, ios::floatfield);

 

l       位宽、填充字符、精度设置

位宽

cout.width(); //返回当前宽度,默认为0 (生成能容纳数字的最小宽度)

cout.width(int n); //设置宽度并返回之前的宽度

如果设置为10,显示时,不足则补填充字符,包括符号位和基数;

如果超过10,则正常显示不会截断。

每次输出>>后都得重新设置宽度,否则默认为0

 

填充字符

fill(); //返回当前填充符,默认为空格

fill(int n); //设置并返回之前的填充符

 

精度

precision(); //返回当前的精度,默认为小数点后6

precision(int n); //设置并返回之前的精度

 

5)          输出流格式化2 – 操纵算子

用户实现跟前面一样的功能,只不过不通过显式的函数调用。

一直管用,直到有人显式的更改它。

               比如,cout<<showpos<<132<<endl;

 

l       无参数算子 <iostream>

                       showbase

                       noshowbase

                      

                       showpos

                       noshowpos

 

                       uppercase

                       nouppercase

                      

                       showpoint

                       noshowpoint

 

                       skipws // cin<<ws

                       noskipws

                      

                       left

                       right

                       internal

 

                       scientific

                       fixed

 

                       endl //换行,且刷新缓冲区

                       flush //刷新缓冲区

 

重载算子

ostream& endl(ostream& os)

{

return os << “/r/n”;

//对于endl算子,足够了,可以给别的算子加更多的逻辑
}

                                      

                       ostream& ostream::operator << (ostream& (*pf)(ostream&))

{

return pf(*this);
}

 

 

l       有参数算子 <iomanip>

用法: cout<<setfill(10)<<133<<endl;

 

setiosflags(fmtflags n) //相当于setf

resetiosflags(fmtflags n) //相当于unsetf

 

setbase(int n)

 

setfill(char n)

 

setprecision(int n)

 

setw(int n) //宽度,用于输入流时,只对字符串有意义。

 

重载算子 二进制输出

class Binary

{

private:

unsigned long m_number;

public:

Binary(unsigned long nNumber){ m_number = nNumber;}

 

friend ostream& operator << (ostream& os, const Binary& b)

{

const unsigned long MAX = numeric_limits<unsigned long>::max();

unsigned long bit = ~(MAX >> 1);

while(bit)

{

   os<<(b.n & bit ? ‘1’:’0’);

   bit >>=1;

}

return os;

}
};

 

                    ostringstream str;

                                       str<<Binary(23444);

                                       cout<<str.str()<<endl;

 

6)          国际化

l       宽字符

有时候一个函数处理char,需要一个函数去处理;而处理wchar_t,又需要另外一个函数去处理(C不允许函数重载)。

 

可以用char_traits来解决,比如:

template<class charT, class traits>

std::basic_ostream<charT, traits>& operator << (std::basic_ostream<charT, traits>& os, const Data& date)

{

             //实现

}

 

l       区域Locale <locale>

 

更换区域

默认的区域名为C,为英美地区使用。

可以针对输入输出换区域:

locale loc;

cout<<loc.name<<endl; //C

locale cur = cout.getloc();

cout.imbue(locale(“french”)); //换法国区域

cin.imbue(cur);//比如,输入数字可能以,或.分割

也可以使用这样的方式分割数字:

#include <locale>

class thousands_sep_facet:public numpunct<char>
{
public:
   explicit thousands_sep_facet( size_t r=0 ) : numpunct<char>(r)
   {
   }

protected:
   string do_grouping() const
   {
    return "/003";
   }
};

locale loc( locale(), new thousands_sep_facet );
cout.imbue( loc );

cout<<10000000<<endl; //10,000,000

 

区域分类

collate

按照不同的字符顺序比较字符串

 

ctype

对字符类型抽象

 

monetary

货币表示

local loc(“french”);

string symbol = use_facet<moneypunct<char> >(loc).curr_symbol();

cout<<symbol<<123<<endl;

 

numeric

数字显示

 

time

时间显示

 

messages

不同语言下的错误显示