(C/C++学习笔记) 二十. 文件和流

时间:2020-12-20 03:45:39

二十. 文件和流

● 文件的概念

文件(file)

一.

C/C++语言将文件作为字节序列(sequence of characters)来对待,但从编码角度,或说从对字节信息的解释来看,文件分为:文本文件或二进制文件。

在文本文件中存储是每个字符的ASCII的形式:31 32 33.(不过这些ASCII在计算机中实际上还是二进制的).

文本文件可以用字处理软件(如记事本)进行处理.

这种形式便于对字符进行逐个处理,也便于输出显示,但需要的存储空间相比二进制形式往往要大一些。

和1.

在二进制文件中的存储形式是将其ASCII码转换为二进制的形式----ASCII是31 32 33, 然后将该ASCII码转换为二进制,即00011111 00100000 00100001。

在二进制文件中的存储的是123的二进制形式为:00000000 00000000 00000000 01111011; 123在文本文件件中占3个字节, 在二进制文件中占4个字节. 整型数据666666的二进制形式为:00000000 00001010 00101100 00101010; 666666在文本文件件中占6个字节, 在二进制文件中占4个字节.因此, 二进制文件不一定能节省空间.

文本文件不可以用字处理软件(如记事本)进行处理, 否则会因解码形式不同而产生乱码.

二进制形式与字符不存在对应关系,可读性差一些,在极为特殊的情况下,二进制文件占的空间更大,在大多数情况下,文本文件占的空间更大, 处理速度更快。

  • 上面只是我们从文件的形式上的理解,但是在计算机的内存存储的都是二进制形式,我们之所以能够看见文本文件中的内容是因为文本文件采用的是ASCII解码形式,而二进制文件采用的则是二进制的解码形式. 因此有时候当我们用一个记事本打开一个二进制文件的时候会看见很多我们不希望看见的东西,那就是乱码, 因为它们采用的解码形式不一样.
  • 在实际应用中,大多数文件都是二进制文件,如图象文件(后缀包括.bmp, .jpg, .tif, .gif…等),影像文件,声音文件,数据库文件。

Microsoft Word 的.doc文件也是二进制文件,因为除了字符外,它还含有字体、字号、颜色等数据。输出二进制文件的方法是使用write()成员函数。

二.

从用户的角度(或所依附的介质), 文件分为普通文件(磁盘上或其它介质上的有序数据集)和设备文件(显示器, 打印机, 键盘等----操作系统把这些设备当作文件来进行管理)

● 流的概念

流(stream)

C++中的I/O流实际上是一个处于传输状态的字节序列,这些字节序列按顺序从一个对象传送到另一个对象,在对象之间的"流动"。流形象表示了信息从源到目的端的传送过程。

输入流/输出流: (从几个角度理解)

  1. 以内存为原点, 写入(write to)内存的流就是输入流; 从内存读取(read from)并输出的流就是输出流.
  2. 站在程序的角度, 往程序内部输入数据的流叫输入流, 从程序内部输出数据的流叫输出流
  3. 输入设备发出的流是输出流, 输出设备接收流是输入流. (注意: 硬盘软盘即是输入设备又是输出设备)

通信(correspondence)是在流和文件之间, 而不是流和设备之间.

流和设备的实体(如屏幕或键盘)程序使用的每个设备通常都有一个或多个相关的流, 这取决于它仅仅是输入设备(如键盘)或输出设备(如打印机), 还是既可输入又可输出的设备(如磁盘驱动器)----与磁盘的某个文件相关联的流可以是输入流, 这样你就只可以在这个文件中读取(read from)数据; 也可以是一个输出流, 这样你就只可以在这个文件中写入(write to)数据; 也可以既是输入流又是输出流, 这样你就只可以在这个文件中读取或写入数据.

  • 有的资料认为"流"有动态的意义, 如下列观点:

可以将一个字节形象地比喻成一滴水,字节在设备、文件和程序之间的传输就是流,类似于水在管道中的传输,可以看出,流是对输入源和输出目的地(input source and output destination)的一种抽象,也是对传输信息(information conveyance)的一种抽象; 因此"流"既是一个静态概念, 也是一个动态概念.

为了保证语言和平台无关,C++和C一样,不具备内部输入输出能力。语言的输入输出能力是和操作系统相关的,在最底层都是通过调用操作系统的I/O库实现。

_______________________________________

一. C语言对文件的操作分为两种方式,即:

①流式文件操作: 作是通过缓冲区来进行;流式文件操作是围绕一个FILE指针来进行,

② I/O文件操作: 通过直接存/取文件来完成对文件的处理, 此类文件操作是围绕一个文件的"句柄"来进行,什么是句柄呢?它是一个整数,是系统用来标识一个文件(在WINDOWS中,句柄的概念扩展到所有设备资源的标识)的唯一的记号。

二. C++支持两种I/O,第一种是从C语言继承来的I/O系统,一种是由C++定义的面向对象I/O系统。

"流"就是"流动",是物质从一处向另一处流动的过程。C++流是指信息从外部输入设备(如键盘和磁盘)向计算机内部(即内存)输入和从内存向外部输出设备(如显示器和磁盘)输出的过程,这种输入输出过程被形象地比喻为"流"。

C++语言系统为实现数据的输入和输出定义了一个庞大的I/O流类库,它包括的类主要有ios,istream,ostream,iostream,ifstream,ofstream,fstream,istrstream,ostrstream,strstream等,其中ios为根基类,其余都是它的直接或间接派生类。

为了实现信息的内外流动,C++系统定义了I/O类库,其中的每一个类都称作相应的流或流类,用以完成某一方面的功能; 根据一个流类定义的对象也时常被称为流。如根据文件流类fstream定义的一个对象fio,可称作为fio流或fio文件流,用它可以同磁盘上一个文件相联系,实现对该文件的输入和输出,fio就等同于与之相联系的文件。

在进行I/O操作时,首先是打开操作,使流和文件发生联系,建立联系后的文件才允许数据流入和流出,输入或输出结束后,使用关闭操作使文件与流断开联系。

有这么一个说法: 能fscanf决不ifstream 能fprintf决不ofstream 能sscanf决不istringstream 能sprintf决不ostringstream

● 流/文件流在计算机中分为: 文本流和二进制流两种.

流/文件流在计算机中分为: 文本流和二进制流两种.

预备知识: 对于回车符的定义:

Windows:0D0A

Unix/Linux: 0A

MAC: 0D

  • 0x0A: 十六进制的ASCII码, 换行符号, '/n'; 0x0D: 十六进制的ASCII码, 回车符号, '/r'

Unix 系统里,每行结尾只有"<换行>",即"\n";Windows系统里面,每行结尾是" <回车><换行>",即" \r\n";Mac系统里,每行结尾是"<回车>"。因此在文件读写时,如果以文本流方式向文件中写数值 0x0A ,在存储时windows系统会将其识别为回车键,并自动写入0x0D(\r:换行键)。如果以二进制方式打开那么只会写入0x0A。一个直接后果是,Unix/Mac系统下的文件在Windows里打开的话,所有文字会 变成一行;而Windows里的文件在Unix/Mac下打开的话,在每行的结尾可能会多出一个^M符号.

同时一个文件以文本流的方式打开,那么文件中连续存储的0D 0A组合会被转换为一个字符0A ,也就是一个换行符!!如果一个文件以二进制流的方式打开的话则不会出现上述的那种情况,而是一个字符一个字符的读取,也就是说有多少个字符我就读多少一个字符,我才不管连续存储的0D 0A组合要转换为一个字符0A。遇到了这个组合就直接读取0D 0A 。

文件处理方法:

① 缓冲文件系统:高级文件系统,系统自动为正在使用的文件开辟内存缓冲区

② 非缓冲文件系统:低级文件系统,由用户在程序中为每个文件设定缓冲区

(C/C++学习笔记) 二十. 文件和流(C/C++学习笔记) 二十. 文件和流

● C++流类库

像上面提到的, 流除了可以分为C++的流分为文本流和二进制流, 还可以分为文件流和字符串流(内存流):

文件流是以外存文件为输入输出对象的数据流(文件流本身不是文件)

输出文件流是从内存向外存文件写入数据(以内存为中心思考),

输入文件流是从外存文件向内存读出数据。每一个文件流都有一个内存缓冲区与之对应。

字符串流是以内存中用户定义的字符数组(字符串)为输入输出的对象数据流,

字符串流也称为内存流。

输出字符串流向字符串数组写入数据,

输入文件流从字符串读出数据。

注意: 上面的阐述有点绕, 总之记住: 输出流→写入, 输入流→读出

(C/C++学习笔记) 二十. 文件和流

※ <strstream>中包含istrstream,ostrstream等类 (已经不建议使用(depreciate deprecated)

<sstream>中包含istringstream,ostringstream等类.

(C/C++学习笔记) 二十. 文件和流

(以内存为中心思考)输入流类: 从文件/字符串(数组)中读出内容; 输出流类: 向文件/字符串(数组)写入内容; 输入/输出流类: 既要读出又要写入文件/字符串

在C++语言中,数据的输入和输出(简写为I/O)包括①对标准输入设备键盘和标准输出设备显示器、②对在外存磁盘上的文件和③对内存中指定的字符串存储空间(当然可用该空间存储任何信息)进行输入输出这三个方面。

因此, 数据流可以分为①标准输入/输出流, ②文件流, ③字符串流----标准输入/输出流是在标准输入/输出设备进行传输的数据流; 文件流是以外存文件为输入输出对象的数据流; 字符串流不是以外存文件为输入输出的对象,而以内存中用户定义的字符数组(字符串)为输入输出的对象,即将数据输出到内存中的字符数组,或者从字符数组(字符串)将数据读入。字符串流也称为内存流。字符串流也有相应的缓冲区,开始时流缓冲区是空的。如果向字符数组存入数据,随着向流插入数据,流缓冲区中的数据不断增加,待缓冲区满了(或遇换行符),一起存入字符数组。如果是从字符数组读数据,先将字符数组中的数据送到流缓冲区,然后从缓冲区中提取数据赋给有关变量。

C++系统中的I/O类库,其所有类被包含在iostream.h,fstream.h和strstrea.h这三个系统头文件中,各头文件包含的类如下:

① 要使用cout, cin, cerr和clog这些预定义流对象进行对标准设备的I/O操作, 需包含iostream文件, 包括如下类:ios, iostream, istream, ostream, iostream_withassign, istream_withassign, ostream_withassign等。

※ 四个包含在iostream.h标准头文件中的I/O对象:

cin 标准输入, istream类的对象

cout 标准输出, ostream类的对象

cerr 标准错误输出, ostream类的对象

clog 带缓冲的标准出错信息输出, ostream类的对象

※ 输入流类istream重载了运算符>>, 其功能是从输入流中提取数据赋值给一个变量, 因此也称为提取运算符(extractor). 例如, int x; cin>>x;

输出流类ostream重载了运算符<<, 其功能是把表达式的值插入到输出流中, 因此也称为插入运算符(inserter). 例如, int x=10; cout<<x;

istream类的对象cin从输入流的数据读取(read from), 输出流的数据向ostream类的对象cout写入(write to). 如图所示:

(C/C++学习笔记) 二十. 文件和流

② 要使用文件流对象进行针对磁盘等非标准设备的I/O操作, 需包含fstream文件, 包括如下类:fstream, ifstream, ofstream和fstreambase,以及iostream.h中的所有类。

③ 要使用字符串流对象进行针对内存字符串空间的I/O操作, 需包含使用strstream文件, 包括如下类:strstream, istrstream, ostrstream和strstreambase,以及iostream.h中的所有类。

注意: 要使用setw(), fixed()等大多数操作符, 需包含iomanip文件; 为了使用新标准的C++流, 还必须在程序文件的声明区插入命名空间声明: using namespace std;

※ 标准I/O的相关硬件& 非标准I/O的相关硬件

(C/C++学习笔记) 二十. 文件和流

● 有格式输入输出和无格式输入输出

利用C+ +流既可进行有格式输入输出,也可进行无格式输入输出。

计算机所处理的数据都有内部存储格式和外部表现形式的区分,因此在输入输出过程中必须进行适当的转换,有格式输入输出就是完成这一任务的。有格式输入输出针对的是键盘、显示器、打印机等字符设备以及磁盘中的文本文件。对于有格式输入输出,无论输入输出的数据是什么数据类型,体现在外部设备上都是字符序列。

对于无格式输入输出,数据的内部存储存格式与外部存储格式完全相同(即二进制数),因此无格式输入输出只能针对磁盘文件(或磁带、光盘上的文件),而且这样的文件通常不能用一般的文本编辑器查看。进行无格式输入输出需调用流对象的专门的成员函数实现。

● 文件操作的相关函数

在C语言中:

① 文件的打开, 关闭:

fopen(); fclose();

② 文件的读写:

fputc(); fgetc(); fputs(); fgets(); fprintf(); fscanf(); fread(); fwrite();

③ 文件的定位:

fseek(); rewind();

ftell()

在C++语言中:

字符串流相关类:ifstream, ofstream, fstream (都位于头文件<fstream>中)

文件流先关类: istrstreeam, ostrstream,strstream (都位于头文件<strstream>中)

字符串流/文件流的通用类: istream, ostream, iostream (都位于头文件<iostream>中)

● 有关文件路径的写法

在Windows系统中, 假设有文件F:\test\a.txt, 那么在用fopen()等相关函数时, 文件路径应该写成, "F:\\test\\a.txt"; 因为在c++中\\是一种转义字符,他表示一个\,就像\n表示回车一样.

在Linux系统中, 类似的写法是"/home/test/a.txt".

另外, 在Microsoft的DOS系统中, /用来加参数, 不要与Linux中的/混淆.