文件指针 python 文件指针的概念
  1g2gdZwlYAgk 2023年11月26日 38 0

1、文件指针和文件描述符  

  在任何一种操作系统中,程序在开始读写一个文件的内容之前,必须首先在程序与文件之间建立连接或通信通道,这一过程称为打开文件。打开一个文件的目的可以是为了读或者为了写,也可以是即读又写。

  有两种机制用于描述程序与文件的这种连接:文件描述符和文件流。

文件描述符这一概念只适用与UNIX/Linux这样的操作系统,UNIX/Linux内核提供相应的文件操作函数,因为Linux一切皆文件。

  文件指针是由C语言库定义的,UNIX/Linux和windows都可以用。

文件IO相关的函数都属于系统调用函数,不可移植。标准IO相关的函数是属于C语言标准库的,可以移植。所以标准IO可以用于Linux和windows,文件IO目前只能用于Linux。

2、文件指针(文件流)

  2.1、文件指针FILE

  在C语言中用一个指针变量指向一个文件,这个指针称为文件指针。通过文件指针就可对它所指的文件进行各种操作。

  C语言中使用的是文件指针而不是文件描述符做为I/O的句柄。"文件指针(file pointer)"指向进程用户区中的一个被称为FILE结构的数据结构。FILE结构包括一个缓冲区和一个文件描述符值。而文件描述符值是文件描述符表中的一个索引。从某种意义上说文件指针就是句柄的句柄。

  每个被使用的文件都在内存中开辟了一个区域,用来存放文件的有关信息,这些信息是保存在一个结构体类型的变量中,该结构体类型是由系统定义的,取名为FILE。

  定义文件指针的一般形式为:FILE *指针变量标识符;

  其中FILE应为大写,它实际上是由系统定义的一个结构,该结构中含有文件名、文件状态和文件当前位置等信息。在编写源程序时不必关心FILE结构的细节。

  在使用文件时,需要在内存中为其分配空间,用来存放文件的基本信息,给结构体类型是由系统定义的,C语言规定该类型为FILE型,其声明如下:

/*FILE结构体*/
#ifndef _FILE_DEFINED
struct _iobuf 
{
    char *_ptr;       //文件输入的下一个位置 
    int  _cnt;        //当前缓冲区的相对位置 
    char *_base;      //指基础位置(即是文件的其始位置) 
    int  _flag;       //文件标志 
    int  _file;       //文件的有效性验证 
    int  _charbuf;    //检查缓冲区状况,如果无缓冲区则不读取 
    int  _bufsiz;     //缓冲区大小 
    char *_tmpfname;  //临时文件名
};
typedef struct _iobuf FILE;

  例如:FILE *fp;

  fp是指向FILE结构的指针变量,通过fp即可找存放某个文件信息的结构变量,然后按结构变量提供的信息找到该文件,实施对文件的操作。习惯上也笼统地把fp称为指向一个文件的指针。

  实际上,FILE结构是间接地操作系统的文件控制块 (FCB)来实现对文件的操作的,如下图:

文件指针 python 文件指针的概念_文件描述符

  上面图中的_file实际上是一个描述符,作为进入打开文件表索引的整数。

  从上图可以看出,C语言通过FILE结构可以间接操作文件控制块(FCB)。为了加深对这些的理解,这里科普下操作系统对打开文件的管理。

  文件是存放在物理磁盘上的,包括文件控制块(FCB)和数据块。文件控制块通常包括文件权限、日期(创建、读取、修改)、拥有者、文件大小、数据块信息。数据块用来存储实际的内容。对于打开的文件,操作系统是这样管理的:系统维护了两张表,一张是系统级打开文件表【指向i节点】,一张是进程级打开文件表(每个进程有一个)【指向FILE结构体】。系统级打开文件表复制了文件控制块的信息等;进程级打开文件表保存了指向系统级文件表的指针及其他信息。 系统级文件表每一项都保存一个计数器,即该文件打开的次数。我们初次打开一个文件时,系统首先查看该文件是否已在系统级文件表中,如果不在,则创建该项信息,否则,计数器加1。当我们关闭一个文件时,相应的计数也会减1,当减到0时,系统将系统级文件表中的项删除。 进程打开一个文件时,会在进程级文件表中添加一项。每项的信息包括当前文件偏移量(读写文件的位置)、存取权限、和一个指向系统级文件表中对应文件项的指针。系统级文件表中的每一项通过文件描述符(一个非负整数)来标识。

  通过上面所讲述的可以发现:FILE结构体中的_file成员应该是指向进程级打开文件表,然后,通过进程级打开文件表可以找到系统级打开文件表,进而可以通过FCB操作物理磁盘上面的文件。

  2.2、流

  标准I/O库的所有操作都是围绕流来进行的,在标准I/O中流用FILE *来描述。

  标准I/O的核心对象就是流。当用标准I/O打开一个文件时,就会创建一个FILE结构体描述该文件(或者理解为创建一个FILE结构体和实际打开的文件关联起来)。我们把这个FILE结构体形象的称为流。标准I/O函数都基于流进行各种操作。

  在C语言中引入了流(stream)的概念。它将数据的输入输出看做是数据的流入和流出,这样不管是磁盘文件或者是物理设备(打印机,显示器,键盘等)都可以看做一种流的源和目的地,视他们为同一种东西,而不管其具体的物理结构,即对他们的操作,就是数据的流入和流出。这种把数据的输入输出作为操作对象,抽象化为一种流,而不管它的具体结构的方法有利于编程,而设计流的输出操作的函数可用于各种对象,与具体的实体无关,即具有通用性。

  C语言将每个文件简单地作为顺序字节流(如下图)。每个文件用文件结束符结束,或者在特定字节数的地方结束,这个特定的字节数可以存储在系统维护的管理数据结构中。当打开文件时,就建立了和文件的关系。在开始执行程序的时候,将自动打开3个文件和相关的流:标准输入流、标准输出流和标准错误。流提供了文件和程序的通信通道。例如,标准输入流使得程序可以从键盘读取数据,而标准输出流使得程序可以在屏幕上输出数据。打开一个文件将返回指向FILE结构(在stdio.h中定义)的指针,它包含用于处理文件的信息,也就是说,这个结构包含文件描述符。文件描述符是操作系统数组(打开文件列表的索引)。每个数组元素包含一个文件控制块(FCB, File Control Block),操作系统用它来管理特定的文件。标准输入、标准输出和标准错误是用文件指针stdin、stdout和stderr来处理的。

文件指针 python 文件指针的概念_文件指针 python_02

  在C语言中流可以分为2大类,即文本流(text stream)和二进制流(binarystream)。所谓的文本流就是指在流中流动的数据是以字符形式出现。在文本流中,‘\n’被换成回车和换行的代码0DH和0AH。而当输出时,则0DH和0AH被换成'\n‘。

  文本流:在流中处理的数据是以字符形式出现。在文本流中‘\n’被转换成回车符CR和换行符LF所对应的ASCII码0DH和0AH,而当输出时,0DH和0AH被转换成‘\n’。

  二进制流:流中处理的数据是二进制序列。若流中有字符,则用一个字节的二进制ASCII码值表示;若是数字,则用一个字节的二进制数表示,在流入流出时,对'\n'符号不进行变换。

  举例:比如2001这个数字,在文本流中的二进制ASCII码表示为‘2’‘0’‘0’‘1’即50 48 48 49共占用4个字节。而在二进制流中表示的是00000111 11010001用十六进制是07D1。只占2个字节。

  由此可以看出,二进制流比文本流节省空间,而且不用进行\n的转化,这样可以大大加快流的速度,提高效率。因而,对于含有大量数字信息的数字流,可以采用二进制流的方式;对于含有大量字符信息的流,可以采用文本流的方式。

  注意:在Linux下不区分文本流和二进制流。

   (1)文本文件与二进制文件的定义

  大家都知道计算机存储在物理上都是二进制的,所以文本文件与二进制文件的区别不在于物理上,而在逻辑上。这两层只是在编码层次上有差异。

  文本文件是基于字符编码的文件,常见的编码有ASCII编码,UNICODE编码。二进制文件是基于值编码的文件,可以根据具体应用,指定某个值是什么意思。文本文件基本都是定长编码的,基于字符,每个字符在具体编码中都是固定的,ASCII码是8个比特的编码,UNICODE一般占16个比特。而二进制文件可以看成是变长编码的,多少个比特代表一个值,完全由你决定。

  (2)文本文件与二进制文件的存取

  文本工具打开一个文件的过程是怎么样的呢?比如记事本,他首先读取物理上所对应的二进制比特流,然后按照你所选择的编码方式来解释这个流,然后将解释结果显示出来。比如,你选取的解码方式是ASCII解码,那么文本就会8个比特8个比特的解释这个文件流。

  事实上,任何东西要与其他东西进行通信的话,都存在一个既定的协议,既定的编码。人与人之间通过文字联络,比如中国的汉字“妈”,在汉语中和在日语中的意义是不同的,这就容易出现误解。用记事本打开开二进制文件也有类似的情况。记事本无论打开什么文件都按既定的字符编码工作,所以当打开二进制文件的时候,出现乱码是很必然的一件事,因为解释和译码不对应。例如文件流''00000000_00000000_00000000_00000001''可能在二

  进制文件中对应的是一个四字节的整数int 1,在记事本里解释就变成了"NULL_NULL_NULL_SOH"这四个控制符。

  (3)文本文件与二进制文件的优缺点

  因为文本文件和二进制文件仅仅是编码上的补贴,所以他们的优缺点就是编码的优缺点。一般认为,文本文件的编码基于字符定长,译码容易些;二进制文件编码是变长的,灵活,存储利用率要高些。

  文本文件的可读性要好些,存储要话费转换的时间,而二进制文件可读性差,存储不存在转换时间,这里的可读性是从软件使用者的角度来说,因为我们用通用的记事本工具就几乎可以浏览所有文本文件,所以说文本文件可读性好;而读写一个具体的二进制文件需要一个具体的文件解码器,所以说二进制文件可读性差,比如读BMP文件,必须用读图软件,而这里的转化存储时间是从编程角度来说的,因为有些操作系统如windows需要对回车换行符进行转化,所以文件读写时,操作系统需要一个一个字节的检查。

  (4)C的文本读写和二进制读写

  应该说C的文本读写与二进制读写是一个编程层次上的问题,与具体操作系统有关,所以“用文本读写的文件一定是文本文件,用二进制读写的文件一定是二进制文件”这类观点是错误的。

  C语言的文本读写与二进制读写的差别仅仅体现在回车换行符的处理上,文本方式写是,每遇到一个'\n',它将其转化为“\r\n”再写入文件;当读取文本时,每次遇到"\r\n",将其反转化成“\n”,然后送到缓冲区中,所以存在转化耗时。二进制读写时,其不存在任何转化,直接将写缓冲区的数据写入文件。

  总体来说,从编程角度来说,C语言中文本或者二进制读写都是缓冲区与文件中的二进制流进行交互,只是文本读写时有回车换行的转化,所以当写缓冲区中午“\n”时,文本写与二进制写的结果是一样的。

 3、Linux文件描述符

文件描述符(file descriptors,简称fd)而不是文件名来访问文件的,文件描述符实际上是一个非负整数, 对于 Linux 内核而言,所有打开的文件都会通过文件描述符进行索引。Linux中规定每个进程能最多能同时使用NR_OPEN个文件描述符,这个值在fs.h中定义,为1024*1024(2.0版中仅定义为256)。

内核会向进程返回一个文件描述符, 用于指代被打开的文件,所有执行 IO 操作的系统调用都是通过文件描述符来索引到对应的文件,比如当调用 read/write 函数进行文件读写时,会将文件描述符传送给 read/write 函数。

  fd为打开文件的文件描述符,而每个进程都有一张文件描述表,fd文件描述符就是这张表的索引,同样这张表中有一表项,该表项又是指向前面提到打开文件的file结构体,file结构体才是内核中用于描述文件属性的结构体。

但是文件描述符这一概念往往只适用于UNIX、Linux这样的操作系统。

  文件描述符的有效范围是 0 到 OPEN_MAX。一般来说,每个进程最多可以打开 64 个文件(0 — 63)。对于 FreeBSD 5.2.1、Mac OS X 10.3 和 Solaris 9 来说,每个进程最多可以打开文件的多少取决于系统内存的大小,int 的大小,以及系统管理员设定的限制。Linux 2.4.22 强制规定最多不能超过 1,048,576 。

  一个进程可以打开多个文件, 但是在 Linux 系统中,一个进程可以打开的文件数是有限制,并不是可以无限制打开很多的文件, 大家想一想便可以知道,打开的文件是需要占用内存资源的,文件越大、打开的文件越多那占用的内存就越多,必然会对整个系统造成很大的影响,如果超过进程可打开的最大文件数限制,内核将会发送警告信号给对应的进程,然后结束进程; 在 Linux 系统下,我们可以通过 ulimit 命令来查看进程可打开的最大文件数,用法如下所示:

文件指针 python 文件指针的概念_打开文件_03

  该最大值默认情况下是 1024,也就意味着一个进程最多可以打开 1024 个文件,当然这个限制数其实是可以设置的,这个就先不给大家介绍了, 当然除了进程有最大文件数限制外,其实对于整个 Linux 系统来说,也有最大限制。

所以对于一个进程来说,文件描述符是一种有限资源, 文件描述符是从 0 开始分配的,譬如说进程中第一个被打开的文件对应的文件描述符是 0、第二个文件是 1、第三个文件是 2、第 4 个文件是 3……以此类推,所以由此可知,文件描述符数字最大值为 1023(0~1023) 。 每一个被打开的文件在同一个进程中都有一个唯一的文件描述符,不会重复,如果文件被关闭后,它对应的文件描述符将会被释放,那么这个文件描述符将可以再次分配给其它打开的文件、与对应的文件绑定起来。

  内核(kernel)利用文件描述符(file descriptor)来访问文件。文件描述符是非负整数。打开现存文件或新建文件时,内核会返回一个文件描述符。读写文件也需要使用文件描述符来指定待读写的文件。

  每次给打开的文件分配文件描述符都是从最小的没有被使用的文件描述符(0~1023)开始,当之前打开的文件被关闭之后,那么它对应的文件描述符会被释放, 释放之后也就成为了一个没有被使用的文件描述符了。

  当我们在程序中,调用 open 函数打开文件的时候,分配的文件描述符一般都是从 3 开始,这里大家可能要问了,上面不是说从 0 开始的吗,确实是如此,但是 0、 1、 2 这三个文件描述符已经默认被系统占用了,分别分配给了系统标准输入(0)、 标准输出(1)以及标准错误(2)。习惯上,标准输入(standard input)的文件描述符是 0,标准输出(standard output)是 1,标准错误(standard error)是 2。尽管这种习惯并非Unix内核的特性,但是因为一些 shell 和很多应用程序都使用这种习惯,因此,如果内核不遵循这种习惯的话,很多应用程序将不能使用。

  Tips: Linux 系统下,一切皆文件,也包括各种硬件设备,使用 open 函数打开任何文件成功情况下便会返回对应的文件描述符 fd。每一个硬件设备都会对应于 Linux 系统下的某一个文件,把这类文件称为设备文件。所以设备文件对应的其实是某一硬件设备,应用程序通过对设备文件进行读写等操作、来使用、操控硬件设备,譬如 LCD 显示器、串口、音频、键盘等。

  标准输入一般对应的是键盘,可以理解为 0 便是打开键盘对应的设备文件时所得到的文件描述符;标准输出一般指的是 LCD 显示器,可以理解为 1 便是打开 LCD 设备对应的设备文件时所得到的文件描述符;而标准错误一般指的也是 LCD 显示器。

  在linux系统中,设备也是以文件的形式存在,要对该设备进行操作就必须先打开这个文件,打开这个文件就会获得这个文件描述符,它 是个很的正整数,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。

  文件描述兼容POSIX标准,许多Linux和UNIX系统调用都依赖于它。但是它不能移植到UNIX/Linux以外的系统上去,也不直观。

  POSIX 定义了 STDIN_FILENO、STDOUT_FILENO 和 STDERR_FILENO 来代替 0、1、2。这三个符号常量的定义位于头文件 unistd.h。

  文件描述符是由无符号整数表示的句柄,进程使用它来标识打开的文件。文件描述符与包括相关信息(如文件的打开模式、文件的位置类型、文件的初始类型等)的文件对象相关联,这些信息被称作文件的上下文。

 

  3.1、文件描述符、文件表、索引节点表

  每一个文件描述符会与一个打开文件相对应,同时,不同的文件描述符也会指向同一个文件。相同的文件可以被不同的进程打开也可以在同一个进程中被多次打开。

  系统为每一个进程维护了一个文件描述符表,该表的值都是从0开始的,所以在不同的进程中你会看到相同的文件描述符,这种情况下相同文件描述符有可能指向同一个文件,也有可能指向不同的文件。

  具体情况要具体分析,要理解具体其概况如何,需要查看由内核维护的3个数据结构。内核使用3种数据结构表示打开的文件,他们之间的关系决定了在文件共享方面一个进程对另一个进程的影响。

  (1)进程级的文件描述符表。每个进程在进程表中都有一个纪录项,纪录项中包含一张打开文件描述符表,每个文件描述符各占一项,与每个文件描述符相关的是:

    a. 文件描述符标志

    b. 指向一个文件表项的指针

  (2)系统级的打开文件描述符表。内核为所有打开文件维护一张文件表项,每个文件表项包含:

    a. 文件状态(读 写 同步 非阻塞等)

    b. 当前文件偏移量

    c. 指向改文件inode(Linux)节点(v节点unix )的指针

  (3)文件系统的i-node表。每打开一个文件或设备,都有一个inode节点结构,inode节点包含了文件类型和对此文件进行操作函数的指针,对于大多数文件,inode节点还包含了文件的i节点索引节点,这些信息是在打开文件时从磁盘读入内存的,所以,文件的所有文件信息都是随时可用的。inode节点包含了文件的所有者,文件长度,指向文件实际数据块在磁盘上位置的指针等。

  每个文件都有一个32位的数字来表示下一个读写的字节位置,这个数字叫做文件位置。每次打开一个文件,除非明确要求,否则文件位置都被置为0,即文件的开始处,此后的读或写操作都将从文件的开始处执行,但你可以通过执行系统调用LSEEK(随机存储)对这个文件位置进行修改。Linux中专门用了一个数据结构file来保存打开文件的文件位置,这个结构称为打开的文件描述(open file description)。这个数据结构的设置是煞费苦心的,因为它与进程的联系非常紧密,可以说这是VFS中一个比较难于理解的数据结构。

  进程打开一个文件,会与三个表发生关联,分别是:文件描述符表、文件表、索引结点表。

  当同一个进程对同一个文件多次使用open时;对一个文件描述符调用dup函数;父进程使用fork创建一个子进程,子进程和上面三个表的关系;当子进程调用exec函数,子进程和上三个表的关系又发生了什么变化;不同的进程打开同一个文件,那么这些进程又是以怎么样的形式相关联。本文将解释这些问题。

  文件描述符表、文件表、索引结点表存放地点:每个进程都有一个属于自己的文件描述符表;文件表存放在内核空间,由系统里的所有进程共享; 索引结点表也存放在内核空间,由所有进程所共享。

       3.2、 三个表的作用

        文件描述符表:该表记录进程打开的文件。它的表项里面有一个指针,指向存放在内核空间的文件表中的一个表项。它向用户提供一个简单的文件描述符,使得用户可以通过方便地访问一个文件。 当进程使用open打开一个文件时,内核就会在这个表中添加一个表项。如果对同一个文件打开多次,那么将有多个表项。使用dup时,也会增加一个表项。

        文件表:文件表保存了进程对文件读写的偏移量。该表还保存了进程对文件的存取权限。比如,进程以O_RDONLY方式打开文件,这将记录到对应的文件表表项中。

        索引结点表:在文件系统中,也是有一个索引结点表的。如下图所示:

文件指针 python 文件指针的概念_文件指针 python_04

 

   这两个索引结点表有千丝万缕的关系。因为内存中的索引结点表的每一个表项都是从文件系统中读入的,并且两个索引结点表有一对一的关系。所以,内存中的索引结点表的每一个表项都对应一个具体的文件。

        上面所说的三个表的功能,使得三个表紧密地联系在一起,文件描述符表项有一个指针指向文件表表项,文件表表项有一个指针指向索引结点表表项。

  3.3、不同的进程打开同一个文件

        不同的进程打开同一个文件,那么他们应该有各自对应的文件表表项。因为文件表表项记录了进程读写文件时的偏移量和存取权限。多个进程不可能共享一个文件偏移量。另外他们各自打开文件的权限也可能是不同的,有的是为了读、有的为了写,有的为了读写。所以,他们应该有不同的文件表表项。

  此外,因为是同一个文件,所以,多个进程会共享同一个索引结点表项。即他们的文件表表项指针会指向同一个索引结点,最终,如下图所示:

 

文件指针 python 文件指针的概念_文件指针 python_05

 

   3.4、使用dup函数复制一个文件描述符

        dup函数是用来复制一个文件描述符的。点击这个链接可以看到,复制得到的文件描述符和原描述符共享文件偏移量和一些状态。所以dup的作用仅仅是复制一个文件描述符表项,而不会复制一个文件表表项。

        于是使用dup函数后,有下图:

       

文件指针 python 文件指针的概念_文件描述符_06

 

  dup函数是一个很重要的函数。平时我们在shell里面通过 >  来进行重定向,就是通过dup函数来实现的。

  3.5、同一个进程多次打开同一个文件

        每打开一次同一个文件,内核就会在文件表中增加一个表项。这是因为每次open文件时使用了不同的读写权限,而读写权限是保存在文件表表项里面的。所以,效果如下所示:

文件指针 python 文件指针的概念_文件描述符_07

 

   3.6、父进程使用fork创建子进程

  由于fork一个子进程,子进程将复制父进程的绝大部分东西(除了进程ID、进程的父进程ID、一些时间属性、文件锁)。所以子进程复制了父进程的整个文件描述符表。结果如下图所示:

文件指针 python 文件指针的概念_二进制文件_08

 

  每个进程在PCB(Process Control Block)即进程控制块中都保存着一份文件描述符表,文件描述符就是这个表的索引,文件描述表中每个表项都有一个指向已打开文件的指针。已打开的文件在内核中用file结构体表示,文件描述符表中的指针指向file结构体。

  

  3.1、如何创建文件描述符

  进程获取文件描述符最常见的方法是通过open或create获取或者通过从父进程继承。后一种方法允许子进程同样能够访问由父进程使用的文件。文件描述符对于每个进程一般是特定的。当用fork子例程创建某个子进程时,该子进程会获得其父进程所有文件描述符的副本,这些文件描述符在执行fork时打开。在由fcntl、dup和dup2子例程复制或拷贝某个进程时,会发生同样的复制过程。

  对于每个进程,操作系统内核在u_block结构中维护文件描述符表,所有的文件描述符都在该表中建立索引。

 4、文件描述符和文件流的区别和联系

  一句话,流是文件描述符的抽象,一般使用文件描述符是系统层次的调用。

  当向一个文件读入或者输出时,既可以选择流,也可以选择使用文件描述符。文件描述符是int类型的,而流是用FILE *来表示的。

  流给用户程序提供了更高一级的(功能更强大,使用更简化)的I/O接口,它处在文件描述符方式的上层,也就是说,流函数是通过文件描述符函数来实现的。

  文件描述符提供了一个原始、低层次的输入输出接口。文件描述符和流都可以表示一个连接,可以是和设备的(例如终端),或者管道,或者一个和另一个进程的套接字,或者就是一个正常的文件(normal file)。但是,如果你想要对特殊设备进行特定的操作,你必须使用文件描述符。另外,如果你的程序需要以特殊模式进行输入输出(例如nonblocking, polled input, 参见File Status Flags),也必须使用文件描述符。

  而流提供了一个基于原始的文件描述符的高层次接口。流接口对于所有类型的文件的操作大多都是类似的,唯一的区别就是缓冲的策略(参见下面的流缓冲)。

  使用流的主要优势是操作流的函数比文件描述符多得多,而且更加强大方便。文件描述符仅仅提供了一个单一的函数用来传输字符块,但是流接口提供了很多格式化的输入输出(例如printf和scanf)和一些字符函数以及列读入输出函数。

  因为流是基于文件描述符的,所以实际上你可以“拆解”一个流得到对应的文件描述符然后进行低层次的操作。相反地,你也可以先用文件描述符和一个文件建立连接,然后建立一个链接这个文件描述符的流对象。

  通常情况下,你都应该使用流来进行输入输出,这样不仅方便强大,而且可以保证程序的移植性:你可以在任何一个遵守ISO C标准的机器上使用流,但是在一个非GNU机器上你可能无法使用文件描述符。

  4.1、文件描述符和流的相同点

  (1)都是用来表示用户程序与被操作的文件之间的连接,并在此连接的基础上对文件进行读写等访问。

  (2)都能表示与普通文件,与设备(如终端),与管道或者套接字的连接,用户打开一个文件,要么返回文件描述符,要么返回一个流。

  (3)都包含了一大类的I/O库函数

  4.2、文件描述符和流的不同点

  (1)文件描述符表示为int类型的对象。例如标准输入对应文件描述符0,标准输出对应文件描述符1。而流则表示为指向结构FILE的指针FILE* ,因此流也称为“文件指针”

  (2)如果需要对特定设备进行控制操作,必须使用文件描述符方式,没有函数能对流进行这类操作。

  (3)如果需要按照特殊的方式进行I/O操作(例如非阻塞的方式),必须使用文件描述符方式,也没有函数能对流进行这类操作。

  4.3、文件描述符和文件指针的比较

  文件描述符:

   (1)兼容POSIX标准,许多Linux和UNIX系统调用都依赖于它。

   (2)不能移植到UNIX和Linux以外的系统上去,也不直观。

   (3)由于文件描述符在形式上不过是个整数,当代码量增大时,会使编程者难以分清哪些整数意味着数据,那些意味着文件描述符。因此,完成的代码可读性也就会变得很差。

  文件指针:

  (1)执行实际输入输出操作的流函数集合比文件描述符函数要丰富很多,而功能也灵活,强大不少。 

  (2)文件描述符函数只提供简单的传送字符块的函数

  (3)流函数提供格式化I/O,字符I/O,面向行的I/O等大量函数

  (4)流函数有利于程序的移植,任何基于ANSI C的系统都支持流,文件描述符的支持则较弱

   4.4、 linux文件流与文件描述符之间的转换 

   根据文件流获取文件描述符:int fileno(FILE *stream); 

  根据文件描述符获取文件流:FILE * fdopen(int fildes,const char * mode);   

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月26日 0

暂无评论

1g2gdZwlYAgk