目录

9.输入/输出流

9.1C++流类库简介

9.2输入/输出流

9.3自定义类型的输入/输出

9.4文件输入/输出


9.输入/输出流

数据的输入、输出是最重要的操作,C++ 的输入、输出有 iostream 库提供支持。它利用多继承和虚拟继承实现了面向对象类层次结构。C++ 的输入、输出机制为内置数据类型的输入、输出提供了支持,同时也支持文件的输入、输出。 在此基础上,设计者可以通过扩展 iostream 库,为新类型的数据读写进行扩展。

9.1 C++流类库简介

C 语言中的 scanf 和 printf 很灵巧高效,但不是类型安全的,而且没有扩展性。scanf/printf 系列函数要求把读写的变量和控制读写的格式信息分开。而 C++ 正是克服了这些缺点,使用 cin/cout 控制输入/输出。(类型安全就是指两个类型要相互转换,则必须要显示的转换,而不能隐式的只用一个赋值操作就完成转换)

(1) cin,表示标准输入的 istream 类对象。cin 从终端读入数据。

(2) cout,表示标准输出的 ostream 类对象。cout 向终端写数据。

(3) cerr,表示标准错误输出(非缓冲方式),导出程序错误信息。

(4) clog,表示标准错误输出(缓冲方式),导出程序错误信息。

为了在程序中使用 cin/cout,必须要加 iostream 库的相关头文件 #include <iostream>

iostream 类同时从 istream 类和 ostream 类派生而来,允许双向输入/输出。输入有重载的操作符 >> 完成,输出由重载的操作符 << 来完成。输入输出格式:

cin >> 变量名;
cout << 变量名或常量;

除了用户终端的输入/输出,C++ 还提供了文件的输入/输出类:

(1) ifstream,从 istream 派生而来,把文件绑定到程序上输入。

(2) ofstream,从 ostream 派生而来,把文件绑定到程序输出。

(3) fstream,从 iostream 派生而来,把文件绑定到程序输入/输出。

使用 iostream 库的文件流,必须包含相关的头文件:#include <fstream>

文件读写示例:

#include <iostream>
#include <string>
#include <fstream>
using namespace std;int main()
{string srsfname("asdfile.txt");       //定义源文件ifstream srsfile(srsfname.c_str());   //打开源文件流类if(!srsfile)                          //失败给出信息{cout << "Error:unable to open the infile:" << srsfname <<endl;return -1;}string purfname("purfile.txt");       //定义目标文件ofstream purfile(purfname.c_str());   //打开目标文件流类if(!purfile)                          //失败给出信息{cerr << "Error:unable to open the infile:" << purfname <<endl;return -1;}string buf;while(srsfile >> buf){purfile << buf;               //输入到目标文件}return 0;
}

9.2 输入/输出流

9.2.1 基本输出流

C++ 的 ostream 提供了丰富的格式化和无格式化的输出功能:用流插入符输出标准数据类型;put 库成员函数输出字符;以八进制、十进制、十六进制输出数据;以各种精确方式输出浮点型数据;以科学计数法定点输出数据;以指定宽度输出数据;在区域内用指定字符填充等。

流输出可以用流插入运算符 —— 即重载的 << 来完成。运算符 << 左边的操作数是 istream 类的一个对象(如cout),右边可以是 C++ 的合法表达式。

用插入运算符实现流输出:

using namespace std;
int main()
{cout << "hello! C++\n";cout << "hello! C++" <<endl;return 0;
}

输出操作符可以接受任何内置数据类型的实参,包括 const char*,以及标准库 string 和 complex 类类型。任何表达式包括函数调用,都可以是输出操作符的实参,只要它的计算结果是一个被输出操作符实例接受的数据类型即可。

函数调用作操作符的实参:

using namespace std;
int main()
{cout << "hello! C++";cout << strlen("hello! C++") <<endl;return 0;
}

C++ 还提供了指针预定义输出操作符,允许输出项为显式对象的地址。默认情况下,地址以十六进制的形式显式。

#include <iostream>
using namespace std;int main()
{int i = 10;int *p = &i;cout << "i:" << i << " &i:" << &i <<endl;cout << "*p" << *p << "  p:" << p << " &p:" << &p <<endl;return 0;
}
#include <iostream>
using namespace std;int main()
{int i = 10, j = 20;cout << "The max is ";cout << (i>j)?i:j;cout <<endl;return 0;
}

说明:问题在于输入操作符的优先级高于条件操作符,所以输出 i、j 的比较结果的 bool 值。即表达式被解析为:( cout << ( i > j ) ) ? i : j; 。因为 i < j,所以结果为 false,输出结果 0。为了得到正确结果,整个表达式必须放在括号中:cout << ( ( i > j ) ? i : j );。由此得到正确结果:the max is 20。

9.2.2 基本输入流

对应于输出,C++ 提供了实用的输入功能。类似于输出流中的流插入符,输入中引入了流读取符,也称提取符。

流输入可以用流读取运算符 —— 即重载的 >> 来完成。类似于输出运算符 <<,读取符是个双目运算符,左边的操作数是 istream 类的一个对象(如 cin),右边的操作数是系统定义的任何数据类型的变量,例如:int i; cin >> i;从键盘输入的数据会自动转换成 i 的类型,存储到变量 i 中。

注意:(1) 输入运算符 >> 也支持级联输入。在默认情况下,运算符 >> 跳过空格,读入后面与变量类型相应的值。因此给一组变量输入值时,用空格或换行将输入的数值间隔开,例如:

int i;
float f;
cin >> i >> f;

(2) 当输入字符串(char* 类型)时,输入运算符 >> 会跳过空格,读入后面的非空格符,直到遇到另外一个空格结束,并在字符串末尾自动放置字符 '\0' 作为结束标志,例如:

char s[20];
cin>> s;

(3) 数据输入时,不仅检查数据间的空格,还做类型检查自动匹配,例如:

int i;
float f;
cin >> i >> f;

9.2.3 格式化输入/输出

在很多情况下,用户希望自己控制输出格式。C++ 提供了两种格式控制方法:通过使用 ios 类中的格式控制函数;使用称为操纵符的特殊类型函数。

ios 类成员函数控制格式

在 ios 类中,格式控制函数主要是通过对状态字、域宽、填充字及其精度来完成的。输入/输出格式由一个 long int 类型的状态字确定。在 ios 类的 public 部分进行了枚举。

skipws             跳过输入中的空白
left               左对齐格式输出
right              右对齐格式输出
internal           在符号位和数值之间填入字符
dec                十进制显示
oct                八进制显示
hex                十六进制显示
showbase           产生前缀,指示数值的进制基数
shwopoint          总是显示小数
uppercase          在十六进制下显示0X,科学计数法显示E
showpos            在非负数值中显示+
boolalpha          把true和false表示为字符串
scientific         以科学计数法形式显示浮点数
fixed              以小数形式显示浮点数
unitbuf            输出操作后立即刷新所有流
stdio              输出操作后刷新stdout和stderr

(1) 设置状态标志

设置状态标志可以使用 long ios::setf(long flags) 函数,使用格式如下:

stream_obj.setf(ios::enum_type);

说明:其中,stream_obj 为被影响的流对象,常用的是 cin 和 cout。enum_type 为上述列举的值。例如:cout.setf(ios::dec); 表示以十进制输出。要设置多个标志时,彼此间用运算符 " | " 连接(不能连接相反的格式控制符,如:不能 skipwsl|noskipws)。

例如:cout.setf(ios::dec|ios::scientific);//表示以十进制、科学计数法输出。

(2) 清除状态标识

清除状态标识可使用 long ios::unsetf(long flags) 函数。使用格式如下:

stream_obj.unsetf(ios::enum_type);

使用方法与 ios::setf() 函数相同。

(3) 取状态标识

取状态标志用 flags() 函数,在 ios 类中重载了两个版本:

long ios::flags();
long ios::flags(long flag);

前者用于返回当前状态标志字;后者将状态标志字存储在 flag 内。注意的是与 setf() 函数设置状态标志字不同,flags() 函数是取状态标志字的。

应用实例:

#include <iostream>
using namespace std;void showflags(long f)                   //输出标志字函数
{long i;for(i = 0x8000; i; i = i>>1)     //使用右移位{if(i&f)                  //某位不为0,则输出1,否则为0cout << "1";else cout << "0";}cout <<endl;
}int main()
{long flag;flag = cout.flags();showflags(flag);cout.setf(ios::dec|ios::boolalpha|ios::skipws);flag = cout.flags();showflags(flag);cout.unsetf(ios::skipws);flag = cout.flags();showflags(flag);return 0;
}

函数 showflags() 的功能是输出状态字的二进制数值。算法思想是,从最高位到最低位,逐位计算与二进制 1 的位与(即从 0x8000 开始逐个移位循环 16 次到 0x0001),并输出该二进制位的数值(只有二进制位为 1 时,结果才为 1,否则为 0),由此得到状态字各个二进制位的数值。

第一行显式的是系统默认状态下的状态标志字值,skipws 和 unitbuf 被设定(查相应的标志数值表得到)。第二行显式的默认状态下,并追加了 ios::dec|ios::boolalpha|ios::skipws 的状态标志, skipws 是已有标志,没有变化相应的二进制位,而 ios::dec 和 ios::boolalpha 位发生了变化。第三行显式的是去掉 ios::skipws(执行函数 unsetf() 后得到)后状态字的变化,显式最后一位是控制 ios::skipws 的标志位。

(4) 设置域宽

域宽用于控制输出格式,在 ios 类中重载了两个函数来控制域宽,原型如下:

int ios::width();
int ios::width(int w);

第一个函数得到当前的域宽值;第二个函数用来设置新域宽,并返回原来的域宽值。需要注意的是:所设置的域宽仅仅对下一个输出的操作有效,当完成一次输出操作后,域宽值又恢复为 0。

(5) 设置填充字符

填充字符的作用是,当输出值不满足域宽时用设定的字符来填充,默认情况下填充的字符为空格。实际应用中填充字符函数与设置域宽函数 width() 配合使用,否则无空可填,毫无意义。ios 类提供了两个重载的成员函数来操作填充字符,原型如下:

char ios::fill();
char ios::fill(char c);

第一个函数返回当前使用的填充字符;第二个函数设置新的填充字符,并返回设置前的填充字符。

(6) 设置显示精度

类似地,ios 类也提供了重载的两个函数来控制显示精度,函数原型如下:

int ios::precision();
int ios::precision(int num);

第一个函数返回当前设置精度值;第二个函数设置新的显示精度。

输入/输出实例:

#include <iostream>
using namespace std;int main()
{int i = 123;float f =2018.0903;const char* const str = "hello! every one!";cout << "default width is:" << cout.width() <<endl;    //默认域宽cout << "default fill is:" << cout.fill() <<endl;      //默认的填充字符cout << "default precision is:" << cout.precision() <<endl;   //默认精度cout << "i = " << i << "  f = " << f << "  str = " << str <<endl;cout << "i = ";cout.width(12);                  //设域宽cout.fill('*');                  //设填充字符cout << i <<endl;cout << "i(hex) = ";cout.setf(ios::hex, ios::basefield);   //设十六进制cout << i;cout << "  i(oct) = ";cout.setf(ios::oct, ios::basefield);   //八进制cout << i <<endl;cout << "f = ";cout.width(12);cout.precision(3);                     //设精度cout << f <<endl;cout <<"f = ";cout.width(12);cout.setf(ios::fixed, ios::floatfield);cout << f <<endl;cout << "str = ";cout.width(20);cout.setf(ios::left);                  //设左对齐cout << str <<endl;return 0;
}

操纵符控制格式

通过上面代码发现,使用 ios 类成员函数控制输入、输出格式,必须靠流对象来调用,而且不能直接嵌入到输入、输出语句中,使用不够简洁方便。C++ 提供了另一中控制格式的方法,称为操纵符方法,类似于函数的运算符。使用操纵符方法可以嵌入到输入、输出语句中,使用简洁方便。

所有不带参数的操作符定义在头文件 iostream.h 中,带形参的操纵符定义在头文件 iomanip.h 中,使用操纵符时需要包含相应的头文件。常用的有:

ws                      在输入时跳过开头的空白符,仅用于输入
endl                    换行并刷新输出流,仅用于输出
ends                    插入一个空字符,仅用于输出
dec                     十进制显示
oct                     八进制显示
hex                     十六进制显示
setfill(ch)             用ch填充空白字符
setprecision(n)         将浮点数精度设置为n
setw(w)                 按照w个字符来读或写数值
setbase(b)              以进制基数b来输出整数值
setiosflags(n)          设置由n指定的格式标志
resetiosflags(n)        清除由n指定的格式标志

在进行输入、输出时,操纵符被嵌入到输入、输出语句中,用来控制格式。使用操纵符与成员函数的对比:

#include <iostream>
#include <iomanip>
using namespace std;int main()
{int i = 123;float f =2018.0903;const char* const str = "hello! every one!";cout << "default width is:" << cout.width() <<endl;cout << "default fill is:" << cout.fill() <<endl;cout << "default precision is:" << cout.precision() <<endl;cout << "i = " << i << "  f = " << f << "  str = " << str <<endl;cout << "i = " << setw(12) << setfill('*') << i <<endl;cout << "i(hex) = " << hex << i << "  i(oct) = " << oct << i << endl;cout << "f = " << setw(12) << setprecision(3) << f <<endl;cout << "f = " << setw(12) << fixed << f <<endl;cout << "str = " << setw(20) << left << str <<endl;return 0;
}

用户自定义的操纵符控制格式

在 C++ 中,除了系统提供的预定义操纵符之外,还允许用户自己定义操纵符,便于控制一些频繁使用的格式操作,使得格式控制更加方便。

自定义输出流操纵符算子格式如下:

ostream & 自定义输出操纵符算子函数名 (ostream& stream)
{...//自定义代码return stream;
}

自定义输入流操纵符算子格式如下:

istream & 自定义输出操纵符算子函数名 (istream& stream)
{...//自定义代码return stream;
}

自定义输出操作函数:

#include <iostream>
#include <iomanip>
using namespace std;ostream &myoutput(ostream& stream)
{stream << setw(10) << setfill('#') <<left;return stream;
}int main()
{int i = 123;cout << i <<endl;cout << myoutput << i <<endl;return 0;
}

自定义操纵符函数 myoutput(),功能是设置域宽为 10,填充字符为 #,并采用左对齐。函数参数为引用方式,cout 的返回值为引用方式且可以多级输出,所以在主函数中只写 myoutput 即可。

自定义输入操作函数:

#include <iostream>
using namespace std;istream &myinput(istream& stream)
{cout << "please input a hex integer:";stream >> hex;return stream;
}int main()
{int i;cin >> myinput >> i;cout << "i(dec) = " << i << "  i(hex) = " << hex << i <<endl;return 0;
}

9.2.4 其他的输入/输出函数

正如在 C 语言中遇到的问题一样,输入字符串时,仅靠 cin 只能得到不包括空格的字符串。针对此类问题,C++ 还提供了以下输入/输出的函数。

get()、put()函数

get(char& ch) 从输入流中提取一个字符,包括空白字符,并把它存储在 ch 中,返回被应用的 istream 对象。此函数在类 istream 里。对应于 get(),类 ostream 提供了 put(char ch) 函数,用于输出字符。

get()的重载版本:get(char *str, streamsize size, char delimiter = '\n');

str 代表一个字符数组,用来存放被读取的字符。size 代表可以从 istream 中读入的字符的最大数目。delimiter 表示如果遇到它就结束读取字符的动作,delimiter 本身不会被读入,而是留在 istream 中,作为 istream 的下一个字符。一个常见的错误是,执行第二个 get() 时省略 delimiter。下面例子使用 istream 的成员函数 ignore() 来去掉 delimiter。默认情况下,delimiter 为换行符。

get/put 函数实例(直接换行结束):

#include <iostream>
using namespace std;
int main()
{const int str_len = 100;char str[str_len];while(cin.get(str,str_len))//当读入的数据不为空时循环下一次,每次最多读入str_len个{int count = cin.gcount();    //当前实际读入多少个字符cout << "the number is:" << count <<endl;if(count < str_len)cin.ignore();        //在下一行之前去掉换行符}return 0;
}

getline() 函数

使用 get() 函数输入字符串时,经常忘记去掉 delimiter,所以引入函数 getline(),因为 getline() 会丢弃终结符。其原型与 get() 的重载版本相同:

getline(char *str, streamsize size, char delimiter = '\n');

write()、read() 函数

ostream 类成员函数 write() 提供一种输出字符数组的方法。它不是输出 "直到终止字符为止",而是输出某个长度的字符序列,包括空字符。函数原型如下:

write(char *str, streamsize length);

str 是要输出的字符数组,length 是要显示的字符个数。write() 返回当前被调用的 ostream 类对象。

istream 类的 read() 函数,原型如下:

read(char *str, streamsize size);

read() 函数从输入流中读取 size 个连续的字符,并将其放在地址从 str 开始的内存中。gcount() 方法返回最后一个非格式化的抽取方法读取的字符数。这意味着字符由 get()、getline()、ignore()、或 read() 方法读取的,不是由抽取操作符( >> )读取的,抽取操作符对输入进行格式化,使之与特定的数据类型匹配。

输入/输出综合实例:

#include <iostream>
using namespace std;int main()
{const int str_len = 100;char str[str_len];int iline = 0;       //行数int max = -1;        //最长行的长度while(iline < 3){cin.getline(str, str_len);int num = cin.gcount();cout << "the number is:" << num <<endl;iline++;if(num > max)max = num;cout << "Line#" << iline << "\t chars read:" << num <<endl;cout.write(str, num).put('\n').put('\n');}cout << "Total lines:" << iline <<endl;cout << "The longest line:" << max <<endl;return 0;
}

9.3自定义类型的输入/输出

当实现一个类的类型时,有时希望这个类支持输入和输出操作,以便可以将类对象嵌入到输入或输出流中。前面的函数都是 C++ 系统预定义数据类型的输入/输出。对于用户自己定义的数据类型的输入/输出,可以通过重载运算符 ">>" 和 "<<" 实现。

9.3.1 重载输出运算符 "<<"

其重载函数的格式如下:

ostream & operator << (ostream &out, class_name &obj)
{out << obj.data1;out << obj.data2;......out << obj.datan;return out;
}

函数中第一个参数 out 时对 ostream 对象的引用,即 out 必须是输出流对象;第二个参数是用户自定义要输出的类对象的引用。data1,data2,... datan 是类内要输出的数据成员。输出运算符 "<<" 不能作为类的成员函数,只能作为友元函数(要访问类的私有成员)来实现。

输出运算符重载:

#include <iostream>
#include <string.h>
using namespace std;class Word
{char *word;size_t num;       //存储的字符个数public:Word(const char* const str = NULL);virtual ~Word(){if(word)  //清除指针delete []word;}friend ostream& operator<<(ostream& out, Word& sword);
};Word::Word(const char* const str)
{if(str != NULL){num = strlen(str);word = new char[num+1];strcpy(word,str);}
}ostream& operator<<(ostream& out, Word& sword)
{out << "<" << sword.num << ">" << sword.word <<endl;return out;
}int main()
{Word word("hello");cout << word;return 0;
}

程序为类 word 重载输出运算符 "<<",使得可以通过 cout 简单地输出 word 类对象的内容:字符串和字符串长度。在类 word 的构造函数里,为类成员 word 申请空间(包括字符串结束标志 '\0'),在析构函数中,相应地释放成员 word 的空间。重载函数的第二个参数为引用形式,可以转为普通形式,但是引用减少了调用开销。

9.3.2 重载输入运算符 ">>"

输入运算符 ">>",又称为提取运算符,定义其重载函数的格式如下:

istream &operator>>(istream &in, class_name& obj)
{in >> obj.data1;in >> obj.data2;......in >> obj.datan;return out;
}

函数中第一个参数 in 是对 istream 对象的引用,即 in 必须是输入流对象;第二个参数是用户自定义要输入的类对象的引用。data1,data2 ... datan 是类内要输入的数据成员。输入运算符也不能作为类的成员函数,只能作为类的友元函数或独立函数。重载函数第二个参数必须为引用,且不能为常引用,因为要对参数进行修改。

输入运算符重载:

#include <iostream>
#include <string.h>
using namespace std;class Word
{char *word;size_t num;public:Word(const char* const str = NULL);virtual ~Word(){if(word)delete []word;}friend ostream& operator<<(ostream& out, Word& sword);friend istream& operator>>(istream& in, Word& sword);
};Word::Word(const char* const str)
{if(str != NULL){num = strlen(str);word = new char[num+1];strcpy(word,str);}
}ostream& operator<<(ostream& out, Word& sword)
{out << "<" << sword.num << ">" << sword.word <<endl;return out;
}istream& operator>>(istream& in, Word& sword)
{char str[100];in.getline(str, 100);if(in.gcount() > 0){delete []sword.word;sword.num = strlen(str);sword.word = new char[sword.num+1];strcpy(sword.word, str);}return in;
}int main()
{Word word("hello");cout << word;cin >> word;cout << word;return 0;
}

9.4 文件输入/输出

变量中的数据保存在内存中,是临时的;文件数据保存在外存储器上,用于永久的保存大量的数据。根据数据的组织形式,文件分为文本文件和二进制文件。文本文件的每个字节存放一个 ASCII 代码,代表一个字符;二进制文件把内存中的数据,按照其在内存中的存储形式原样写到磁盘上存放。

C++ 把每个文件看成一个有序的字节流(字符流或二进制流)。每个文件不是以文件结束符结束,就是以在系统维护和管理的数据结构中特定的字符结束。文件打开时,就会创建一个对象,将这个对象和某个流关联起来。C++ 中,要进行文件的输入/输出,必须首先创建一个流,与文件关联,才能进行读写操作。

C++ 进行文件处理时,需要包含头文件 <iostream.h> 和 <fstream.h>。<fstream.h> 头文件包括流类 ifstream(从文件输入)、ofstream(向文件输出)和 fstream(从文件输入/输出)的定义。这三个流分别从 istream、ostream 和 iostream 类继承而来。#include<iostream.h> 是非标准输入输出流,#include<iostream> 是标准输入输出流。C++ 中为了避免名字定义冲突,特别引入了 "名字空间的定义",即 namespace。当代码中用 <iostream.h> 时,输出可直接引用 cout<<x;,<iostream.h> 继承 C 语言的标准库文件,未引入名字空间定义,所以可直接使用。当代码中引入 <iostream> 时,输出需要引用 std::cout<<x; 如果还是按原来的方法就会有错。使用 <iostream> 时,引入 std:: 有以下方法:

① using namespace std; cout << x;② using std::cout;cout << x;③ 最基本的std::cout << x;

其他头文件也是同样的道理。(有 ".h" 的就是非标准的,C 的标准库函数。无 ".h"的,就要用到命令空间,是 C++ 的。还有一部分不完全是有 ".h" 和没 ".h" 的差别。例如:math.h和cmath)。

9.4.1 顺序访问文件

C++ 把文件视作无结构的字节流,所以记录等说法在 C++ 中不存在。要正确地进行文件的输入\输出,需要遵循以下步骤。

(1) 定义相关的流对象

包含头文件 <fstream.h> 建立流,定义流对象。

例如:

ifstream in;     //输入文件流
ofstream out;    //输出文件流
fstream inout;   //输入/输出文件流

(2) 文件的打开

打开文件有两种方式。

①:使用 open() 函数

open 函数是 ifstream、ofstream 和 fstream 类的成员函数。原型定义如下:

void open ( const char *,ios_base::openmode mode = ios_base::in | ios_base::out );
void open ( const unsigned char *, int mode, int access = filebuf::openprot );

第一个参数用来传递文件名称;第二个参数 mode 决定文件的打开方式第三个。

打开方式:
ios::app             将输出写入文件末尾
ios::ate             打开文件,并将文件指针移到文件末尾
ios::in              打开文件进行输入
ios::out             打开文件进行输出
ios::nocreate        文件不存在,则open()失败
ios::noreplace       文件存在,则open()失败
ios::trunc           删除文件中现有数据(覆盖同名文件)
ios::binary          以二进制方式打开,进行输入/输出(默认方式为文本方式)打开文件的属性:
0                普通文件,打开操作
1                只读文件
2                隐含文件
4                系统文件
例如:
ofstream out;
out.open("test.tt", ios::out);   //表示用输出流对象out打开一个"test.tt"文件。

当一个文件需要多种方式打开时,可以用”或”操作符把几种方式连接在一起。例如打开一个能用于输入\输出的二进制文件:

fstream inout;
inout.open("test.tt", ios::in | ios::out | ios::binary);

打开文件后要判断是否打开成功:

if(inout.is_open())    //如果打开成功则进行读写操作
{...}

②:流类构造函数打开

虽然完全可以用 open() 函数打开文件,但是类 ifstream、ofstream 和 fstream 中的构造函数都有自动打开文件功能,这些构造函数的参数及默认值与 open() 函数完全相同。因此,打开文件的最常见形式简化为:文件输入/输出流类  流对象名("文件名称");

例如:ofstream out("test.tt");

使用构造函数打开文件后,直接使用流对象判断是否打开成功。

if(!out)
{cout << "文件打开失败!" <<endl;//错误处理
}

(3) 文件的读写

文件一旦打开,从文件中读取数据或向文件中写入数据将变得十分容易,只需要使用运算符 ">>" 和 "<<" 就可以了,只是必须用与文件相连接的流对象代替 cin 和 cout。例如:

ofstream out("test.tt");
if(out)
{out << 10 << "hello!\n";out.close();
}

(4) 文件的关闭

使用完文件后,应该把它关闭,即把打开的文件与流分开。对应于 open() 函数,使用 close() 函数关闭文件。在流对象的析构函数中,也具有自动关闭功能。

① 文本文件读写(文件的打开方式默认为文本方式)

#include <iostream>
#include <fstream>
using namespace std;int main()
{char str[20] = "Welcome to C++!";int i = 1234;char ch;ofstream out("test.tt");     //打开输出文件流if(out)                      //如果成功{out << str << '\n' << i;   //向文件输入信息out.close();               //关闭}ifstream in("test.tt");            //打开输入文件流if(in)                          {                while(in.get(ch))          //输出显示cout << ch;cout <<endl;in.close();}return 0;
}

② 二进制文件读写

文本文件中存放的是字符,而二进制文件中存放的是二进制位。对二进制文件读写有两种方式,一种是使用 get() 和 put() 函数。一种是使用 read() 和 write() 函数。当然,这 4 个函数也可用于文本文件的读写。

get() 函数和 put() 函数原型如下:

istream& get(unsigned char& ch);
ostream& put(char ch);

get() 是类 istream 的成员函数,功能是从文件流中只读取一个字节,并把值放在 ch 中,put() 是类 ostream 的成员函数,功能是向文件流中写入一个字节 ch。当读文件过程中遇到结束符时,与之相连的文件流值变为 0,所以可以根据此判断文件是否结束。

二进制文件读写实例:

#include <iostream>
#include <fstream>
using namespace std;void myread(const char* fname)
{ifstream file_read(fname, ios::binary);char c;if(file_read){while(file_read.get(c))       //文件没有结束时cout << c;              }
}void mywrite(const char* fname)
{ofstream file_write(fname, ios::binary);char c = 'A';if(file_write){for(int i = 0; i < 26; i++)file_write.put(c+i);}
}int main()
{char fname[20] = "word.file";mywrite(fname);myread(fname);cout <<endl;return 0;
}

read() 函数和 write() 函数原型如下:

istream& read(unsigned char* buf, int num);
ostream& write(const unsigned char* buf, int num);

有时需要读写一组数据到文件,read() 函数是类 istream 的成员函数,它从相应的流中读取 num 个字节放在 buf 所指向的缓冲区中。write() 函数是 ostream 的成员函数,它从 buf 所指的缓冲区中向相应的流写入 num 个字节。例如:

int list[] = {12, 13, 14, 15};
write((unsigned char*)&list, sizeof(list));

定义了一个整形数组 list,为了写入它的全部数据,必须在函数 write() 中指定它的首地址 &list,并转换为 unsigned char* 类型,由 sizeof() 指定要写入的字节数。

一组数据的文件读写实例:

#include <iostream>
#include <fstream>
using namespace std;void myread(const char* fname)
{ifstream file_read(fname, ios::binary);char c[30];if(file_read){file_read.read(c, 26*sizeof(char));c[26] = '\0';cout << c <<endl;}
}void mywrite(const char* fname)
{char c[30];for(int i = 0; i < 26; i++)c[i] = 'A' + i;ofstream file_write(fname, ios::binary);if(file_write)file_write.write(c, 26*sizeof(char));
}int main()
{char fname[20] = "word.file";mywrite(fname);myread(fname);return 0;
}

9.4.2 随机访问文件

顺序访问文件不适合快速访问的应用程序。为了增加程序的灵活性和快捷性,立即找到特定记录信息,C++ 提供了随机移动文件访问指针的函数,可以移动输入/输出流中的文件指针,从而对文件数据进行随机读写。

获得和设置流指针

所有输入/输出流对象都有至少一个流指针:

ifstream, 类似 istream,有一个被称为 get pointer 的指针,指向下一个将被读取的元素。
ofstream,类似 ostream,有一个指针 put pointer ,指向写入下一个元素的位置。
fstream, 类似 iostream,同时继承了get 和 put。

tellg() 和 tellp()

这两个成员函数不用传入参数,返回 pos_type 类型的值(根据 ANSI-C++ 标准) ,就是一个整数,代表当前 get 流指针的位置 (用 tellg) 或 put 流指针的位置(用 tellp)。

C++ 支持的文件流中,通过文件指针方式从相应的文件位置进行读写。其中的 get() 函数指针用于指出下一次输入操作的位置;put() 函数指针用于指出下一个输出操作的位置。每当发生一次读写操作时,相应的 get() 函数指针或 put() 函数指针会自动连续地增加。另外也可以使用 seekg() 函数和 seekp() 函数迅速定位,找到指定的位置访问文件。函数原型如下:

ostream& seekp(streamoff off, ios::seek_dir dir);
istream& seekg(streamoff off, ios::seek_dir dir);

函数 seekp() 用于输出文件,将相应的文件指针 get 从 dir 的位置移动 off 个字节;函数 seekg() 用于输入文件,将相应的文件指针 put 从 dir 的位置移动到 off 个字节。

其中类型 streamoff 相当于 long 类型,定义文件指针要偏移的位置范围。参数 dir 表示文件指针的起始位置,off 表示相对于这个位置移动的位偏移量。dir 取值如下:

ios::beg:从文件头开始,此时off取值为正。
ios::cur:从文件当前位置开始,此时off取值为负。
ios::end:从文件末尾开始,此时off取值可正可负。

获得一个二进制文件的大小:

#include <iostream.h>
#include <fstream.h>const char * filename = "test.txt";int main()
{long l,m;ifstream in(filename, ios::in|ios::binary);l = in.tellg();in.seekg (0, ios::end);m = in.tellg();in.close();cout << "size of " << filename;cout << " is " << (m-l) << " bytes.\n";return 0;
}

随机访问文件实例:

#include <iostream>
#include <fstream>
using namespace std;void myread(const char* fname)
{ifstream file_read(fname, ios::binary);char c[10];if(file_read){file_read.seekg(1, ios::beg);       //从开始位置移动1位file_read.get(c[0]);                //读取一个字符file_read.seekg(2, ios::cur);       //从当前位置移动2位file_read.get(&c[1], 4*sizeof(char));//一次读取1个字符,连续读取3次,最后一个'\0'cout << c;}
}void mywrite(const char* fname)
{char c[30];for(int i = 0; i < 26; i++)c[i] = 'A' + i;ofstream file_write(fname, ios::binary);if(file_write)file_write.write(c, 26*sizeof(char));
}int main()
{char fname[20] = "word.file";mywrite(fname);myread(fname);cout <<endl;return 0;
}

C++学习笔记:(九)输入/输出流相关推荐

  1. c++学习笔记之输入/输出流

    1.wchar_t 区别于char,用于存储unicode字符.而char是单字节,只能存储ascii字符. 2.cin/cout/cerr/clog cerr无缓冲错误流          clog ...

  2. 吴恩达《机器学习》学习笔记九——神经网络相关(1)

    吴恩达<机器学习>学习笔记九--神经网络相关(1) 一. 非线性假设的问题 二. 神经网络相关知识 1.神经网络的大致历史 2.神经网络的表示 3.前向传播:向量化表示 三. 例子与直觉理 ...

  3. JavaScript学习笔记(九)(验证框架,layer弹出层)

    JavaScript学习笔记(九) 一.jQuery Validate验证框架 1.引入相关插件路径 2. 修改一些规则 3. 自定义验证规则 4.异步验证 整体代码 二.layer弹出层 1.引入相 ...

  4. IOS学习笔记(九)之UIAlertView(警告视图)和UIActionSheet(操作表视图)基本概念和使用方法...

    IOS学习笔记(九)之UIAlertView(警告视图)和UIActionSheet(操作表视图)基本概念和使用方法 Author:hmjiangqq Email:jiangqqlmj@163.com ...

  5. python3.4学习笔记(九) Python GUI桌面应用开发工具选择

    python3.4学习笔记(九) Python GUI桌面应用开发工具选择 Python GUI开发工具选择 - WEB开发者 http://www.admin10000.com/document/9 ...

  6. ROS学习笔记九:用C++编写ROS发布与订阅

    ROS学习笔记九:用C++编写ROS发布与订阅 本节主要介绍如何用C++编写一个简单的ROS发布与订阅. 编写发布节点 在之前创建的例子beginner_tutorials软件包中,在其目录下的src ...

  7. at24c16如何划分出多个读写区_AVR学习笔记九、基于AT24C16的数据存储实验

    Ema{@AVR 学习笔记九.基于 AT24C16 的数据存储实验 ------- 基于 LT_Mini_M16 9.1 用 I/O 口模拟 I2C 总线实现 AT24C16 的读写 9.1.1 .实 ...

  8. OpenCV学习笔记(九)——图像轮廓(下)

    <OpenCV轻松入门:面向Python>学习笔记(九) 1-3 查找并绘制轮廓.矩特性及Hu矩 4-5 轮廓拟合及凸包 6. 利用形状场景算法比较轮廓 6.1 计算形状场景距离 6.2 ...

  9. Polyworks脚本开发学习笔记(九)-公差控制及制作报告

    Polyworks脚本开发学习笔记(九)-公差控制及制作报告 定义公差 系统有默认的公差设置,可以在选项中进行系统的默认设置,但往往不是想要的.比如下图的XYZ三向都是-/+1,我想只控制Y向并且公差 ...

最新文章

  1. 【JAVA学习笔记】个人设定
  2. 操作系统以什么方式组织用户使用计算机,操作系统习题
  3. mysql insert 二进制_MYSQL 插入二进制数的 2 种方法。
  4. mysql8 修改密码_Mysql 8新特性之(1):账户与安全更人性化
  5. mysql免费框架_MySQL(ORM框架)
  6. [233]树莓派裸机代码bootloader学习总结
  7. 印度孵化器的红利期才刚到,但花样还真不少
  8. FileSplit cannot be cast Exception
  9. log4j2配置详解及自定义Appender
  10. 专利检索常用的十八个网站
  11. 十进制12345在32位计算机中的十六进制表示方法
  12. Linux jar包在screen开机自启
  13. Pytest学习笔记(15)-allure之@allure.epic()、@allure.feature()和@allure.story()
  14. 2021中职组网络空间安全国赛CD模块分组混战镜像*
  15. 干细胞膜包载鱼精蛋白/胰岛素/5-氟脲嘧啶/盐酸伐昔洛韦纳米药物(细胞膜纳米仿生物)
  16. windows api 控制屏幕亮度
  17. 习题(3-3) 计算(a+b)*c的值
  18. 洛谷 P3041 视频游戏的连击Video Game Combos(AC自动机+拓扑排序+数位DP)
  19. 【国产MCU学步随笔------兆易创新GD32f103CBT6,及vscode编辑环境配置】
  20. ViSP学习笔记(二十):二维码检测

热门文章

  1. EventBus设计与实现分析——事件的发布
  2. 斐波那契数拓展问题:leetcode-70 爬楼梯问题 leetcode-1137 泰波那契数问题解法
  3. 第31讲:抓包利器 Charles 的使用
  4. 第17讲:aiohttp 异步爬虫实战
  5. scrapy发送翻页请求
  6. AWS 用户指南笔记
  7. 实战|QUIC协议助力腾讯业务提速30%
  8. 【送书福利8本】YYDS《剑指Offer》,百万程序员人手一册
  9. 知乎高赞:从源码层,拆解OracleJDK和OpenJDK有什么区别?网友:不愧是大神的回答~...
  10. 每日一题之 MySQL