文件描述符是代表文件的标识。每个流会被对应一个开启的I/O通道。文件描述符可以将文件流初始化。
文件描述符是一个整数,用于识别被开启的I/O通道。尤其在UNIX系统中,文件描述符是属于底层接口的。通常,C++系统预先定义了3个文件描述符:①0(零)代表标准输入通道;②1代表标准输出通道;③2代表标准错误信息通道。
这些通道可能连接文件、控制台、其他进程或I/O设备。
目前,C++ STL中不存在“运用文件描述符将流附着在I/O通道”的可能性。通常,这部分功能不具备移植性,目前多数操作系统也不存在这样的标准接口。
1.文件简介
为了准确控制文件处理模式,类ios_base定义了一组标识,其型别均为openmode,类似于fmtflags的位掩码型别,这些标识的意义见表7-7。
表7-7 文件标识的意义
标识binary使得stream能够阻止特殊字符或字符序列的转换。对于以前的MS-DOS操作系统的文字文件,每一行结束时均是以两个字符(“回车CR”和“换行LF”)表示的。正常模式下,将以上述两个字符替换newline(换行)字符。对于二进制模式,则不会进行上述转换。当文件是二进制时,需要使用标识binary。当复制文件时,将源文件字符逐一读出,不做任何修改地写入目标文件。如果文件是文本类型,不需要使用binary。若此时处理换行符号,则改为两个字符。
部分C++版本还提供了nocreate和noreplace,但这两个关键标识不是标准的内容。
多个标识可使用操作符“|”组合起来,其最终结果作为构造函数的第二参数。例如,
标识之间并不是可以任意进行“或”组合。常见的“C++标识组合”和“C的文件打开fopen()函数所使用之接口字符串”间的关联见表7-8。
表7-8 ios_base标识的意义
需要指出的是,无论是读文件还是写文件,在打开文件时,真正执行打开文件操作的是流缓冲区,对文件的实际操作才是由文件流实现的。
文件流类型的文件,是可以显式地被开启和关闭的。C++ STL定义了3个函数。这3个函数分别是:
C++程序员需要养成的一个好习惯:文件打开之后,完成必要的操作;使用完毕之后,在关闭文件之前,一定要使用clear()函数将设置于文件尾端的状态标识清除。因为在C++标准中,文件是可以被多个用户所共享的。
2.简单的文件I/O
要使用程序实现文件的写入,需要以下3步骤:
1)创建一个ofstream对象,管理输出流。
2)该对象与特定的文件进行关联。
3)以使用cout方式使用此对象——输出将进入文件,而不是屏幕。
使用类Ofstream时需要包含头文件<fstream>。对于大多数情况而言,包含该文件便会自动包括iostream文件,因此可以不必显式包含<iostream>。类Ofstream在创建对象时,会同时分配缓冲区空间。使用缓冲空间实现文件操作,可以大大提高文件传输数据的速度。
同样,程序在实现文件读取时,通常需要以下3个步骤:
1)创建一个ifstream对象,实现管理输入流。
2)此对象会与特定的文件关联起来。
3)以使用cin方式使用此对象。
读文件的步骤和写文件的步骤相似,同样需要包含头文件<fstream>,之后声明一个类Ifstream的对象,并与文件名关联。之后可以像使用cin一样,使用该流处理文件了。
需要指出的是,当输入流和输出流对象过期时,该流至文件的连接会自动关闭。当然也可以使用close()方式显式地关闭文件和流之间的联系。当调用close()函数时,并不会真正地删除流,仅仅是断开流到文件的连接而已,该流仍然存在,该流的缓冲区仍然存在,并且该流还可以重新连接到同一个文件或另一个文件。
例7-10
上述代码中,ofstream fout(filename.c_str())实现将文件流和数据文件关联,fout<<"For your eyes only!\ n"实现将字符串"For your eyes only!\ n"写入文件中。fout<<"Your secret number is"<<secret<<endl实现将变量输出至文件中,fout.clear()实现清楚文件状态标识,get(ch)是读取文件的一种形式,而fin2.rdbuf()是读取文件的另一种形式。
例7-10的执行效果如图7-4所示。
图7-4 例7-10的执行效果
3.文件的打开和文件模式
前面已经讲述了文件描述符和文件模式等内容。本小节主要讲述文件的打开和文件模式。前面的例题在使用文件时,在流定义时将流和文件进行关联,同时打开了该文件。类Ofstream还定义了open()函数和is_open()函数,用于实现文件的打开和是否打开的判断。
类Ifstream的open()函数的声明形式为:
is_open()的函数的声明形式为:
类Ofstream的open()函数的声明形式为:
is_open()函数的声明形式为:
C++文件流从类Ios_base处继承了一个流状态成员。该成员存储了标明流状态的信息,如一切顺利、已达文件尾、I/O操作失败等。通常,流状态为零。和其他状态一样,流状态也是通过将特定位设置为1而实现标识的。对于文件流,这些状态字还包括了判断“打开文件”是否成功。例如,当要打开的文件未找到或不存在时,failbit会被设置为1。例如,
is_open()函数用于检查“打开文件”是否顺利。类Ifstream和Ofstream均提供了is_open()函数,可用以判断文件是否被正确打开。例如,
前面已经介绍过文件模式的概念,此处再补充一些关于文件模式的内容。(www.xing528.com)
文件模式描述的是文件被如何使用,如读、写、追加等。流和文件关联时,可以提供指定文件模式的第二个参数。例如,
或
对于C++语言,类Ios_base定义了一个openmode类型,用于表示文件模式;与fmtflags和iostate类型是一样的,是bitmask数值类型。选择类Ios_base中定义的多个常量来指定模式。
无论是类Istream还是Ofstream类,其构造函数和open()函数均能提供两个参数,其第二个参数即是用来表示文件模式的。位操作符OR(“|”)用于将两个位值合并成一个可用于设置两个位的值。模式必须显式提供,类Fstream没有提供默认的模式值。在创建类对象时,类必须显式地提供模式。
ios_base::trunc标识意味着打开已有的现存文件,用于接收程序输出。文件打开之后,以前的内容将被删除。此类行为极大地降低了耗尽磁盘空间的危险。当C++提供其他选择时,例如在文件末尾追加新内容,即可使用ios_base::app模式。
C++标准根据ANSIC标准I/O定义了部分文件I/O。
实现上面的C++语句时,其中C++模式是一个openmode类型值,例如ios_base::in。在C++语言中,模式是相应的C模式字符串,例如“r”。在前面的内容中,C++模式和C模式的对应关系已经列出。值得注意的是,ios_base::ate和ios_base::app均会将文件指针指向将要打开的文件末尾。但是,ios_base::app模式仅允许将数据添加至文件末尾,而ios_base::ate模式会将指针放到文件尾。
(1)给文件追加内容
在文件末尾追加数据时,程序会维护一个存储清单的文件。该程序首先显示文件当前的内容。在文件被打开之后,使用is_open()函数来检查该文件是否存在。之后,程序以ios_base::app模式打开文件,进行输出。程序请求用户从键盘输入时,会将其添加到文件中。之后,程序显示修订后的文件内容。
下面给出一个关于给文件追加内容的例题。
例7-11
例7-11的输出结果为:
(2)二进制文件
标识std::ios::binary代表二进制打开模式,并且实参指定文件被以二进制文件模式打开,而不是以文本文件模式打开。值得提醒的是,在UNIX系统中,二进制文件和文本文件是没有区别的;在Windows系统中,实参std::ios::binary是有意义的。文本文件和二进制文件之间可以使用以下方法进行转换:
1)在程序向二进制文件写入新行符“\ n”时,文件系统将写入单个新行符;多数平台上,新行符与换行符(0xoa)相同。
2)在程序向文本文件写入新行符时,文件系统将写入两个字符:回车符(0x0d)和换行符(0x0a)。
3)程序从二进制文件中读取一个新行符时,文件系统将把单个新行符读取到存储器中。
4)在程序从文本文件中读取一对回车/换行符时,文件系统将把一对字符转换成存储器中一个新行符。
5)在程序从文本文件中读取单个新行符时,换行符前面是没有回车符,文件系统把一个新行符插入存储器。
由以上内容可知,上述方法不仅涉及新行符表示的转换,而且涉及文件位置的操作-see-king和telling。文本文件在存储器中的数据表示与磁盘上数据表示的长度不同,是存储器中单个新行符与磁盘之间回车符和换行符之间双向转换造成的。在文本文件和二进制文件有区别的平台上,文本文件中的seeking和telling是不可靠动作。程序需要在文件内查找和告知当前文件的位置,程序应该使用二进制模式打开文件。无论在读文件时,还是写文件时,均须完整处理和换行有关的符号“\ r \ n”,而不是仅仅处理符号“\ n”或std::endl。
将数据存储在文件中时,二进制格式具体地说是计算机内部表示。计算机不是存储字符,而是存储这个值的64位double表示。对于字符而言,二进制表示与文本表示是一样的,即字符的ASCII码的二进制表示。对于数字而言,二进制表示与文本表示有很大差别。文本格式便于读取,使用编辑器或字处理器来读取或编辑文本文件,可以很方便地将文本文件在计算机系统之间实现传输。对于数字而言,二进制格式比较精确。不会有转换误差或舍入误差。以二进制格式保存数据的速度非常快,因为另一个系统使用另一种内部表示,无法将数据传输给该系统。同一系统上不同的编译器可能使用不同的内部结构表示。必须编写一个将数据转换成另一种数据的程序。
使用二进制格式,更为有利的是,可以实现整块地写入和读取数据。通常在实现文件的读取和写入时,多数使用的是write()和read()函数。下面引用C++ Primer Plus(第五版)中的例题,来说明二进制文件的读取和写入。
例7-12
例7-12的执行效果如图7-5所示。
图7-5 例7-12的执行效果
4.命令行处理技术
文件处理程序通常使用命令行参数来指定文件。尤其在原有的MS-DOS和UNIX系统,或者现有的Windows操作系统的cmd运行环境中。命令行参数是用户在使用程序时即时输入的,即在命令行中输入的参数。例如,
在UNIX操作系统中使用命令wc,用于统计文件信息:
此时,wc是程序名,a.dat、b.dat、c.dat、d.dat和e.dat是命令行参数传递给程序的文件名。对于C/C++,在命令环境中运行的程序能够访问命令行参数。其使用方法是使用main()函数的参数。对于一个普通的main()函数,其形式多为:
其中参数argc是命令行中的参数个数,其中包括命令名本身。在上例中,所谓命令名本身即是指命令wc本身。参数argv变量为一个指针,指向char型指针。将参数argv看作指针数组,其中的指针指向命令行参数,argv[0]是一个指针,指向存储第一个命令行参数的字符串的第一个字符,以此类推。argv[0]是命令行中的第一个字符串。例如,在上例中,argv[1]代表文件名a.dat,以此类推。对于上例,
main()函数的各参数分别为argc等于6,argv[0]为“wc”,argv[1]为“a.dat”,argv[2]为“b.dat”,argv[3]为“c.dat”,argv[4]为“d.dat”,argv[5]为“e.dat”。
通过在main()函数的起始位置填写部分语句,将main()函数的各参数进行输出。当然,命令行参数与命令行操作系统紧密相关。其他程序可能允许使用命令行参数。
例7-13
例7-13的执行效果如图7-6所示。
图7-6 例7-13的执行效果
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。