理论教育 Linux操作系统文件操作常用C库函数

Linux操作系统文件操作常用C库函数

时间:2023-11-25 理论教育 版权反馈
【摘要】:不同平台、 不同编译器支持的库函数是有所不同的。标准C 库提供的文件操作类函数有fopen、 fread、 fwrite、 fclose、 fflush、 fseek 等, 注意引入stdio.h 头文件, 常用功能函数原型描述如下。若文件不存在则建立该文件。返回结果为读取到的字符, 若返回EOF 则表示到了文件尾。如果函数返回值为NULL 表示有错误发生。将数据写入文件流中。

Linux操作系统文件操作常用C库函数

不同平台、 不同编译器支持的库函数是有所不同的。 C 语言标准不断发展新特性, 引入新的库函数, 但不同编译器对新标准的支持程度不同。 由于不同行业的需求不一, 有的编译器对某些特性可能不支持, 有时还会根据自身需求在标准基础上自己扩展新特性。 以不断更新发展的GCC 为例, 它除了支持C99 标准外, 还自行扩展了如零长度数组、 属性声明等。 而微软主要是C++编译器, 标准基于ANSI C 不断发展, 其支持的C 库函数与glibc 库函数有差异。

标准C 库提供的文件操作类函数有fopen、 fread、 fwrite、 fclose、 fflush、 fseek 等, 注意引入stdio.h 头文件, 常用功能函数原型描述如下。

1.fopen 打开文件

由于库函数对文件的操作最终是通过系统调用实现的, 所以每打开一个文件所获得的FILE 结构指针都有一个内核空间的文件描述符fd 与之对应。 同样有预定义的三种默认文件描述: stdin、 stdout、 stderr。

函数的常用参数说明如下。

(1)path: 要打开的文件名, 可以是相对路径也可以是绝对路径。

(2)mode: 代表着流形态, 参数含义如下。

①r 打开只读文件, 该文件必须存在。

②r+打开可读写的文件, 该文件必须存在。

③w 打开只写文件, 若文件存在则文件长度清零、 内容清除。 若文件不存在则建立该文件。

④w+打开可读写文件, 若文件存在则文件长度清零、 内容清除。 若文件不存在则建立该文件。

⑤a 以附加数据的方式打开只写文件。 若文件不存在则建立该文件, 如果文件存在,在保留原来内容基础上, 写入的数据会被加到文件尾。

⑥a+以附加数据方式打开可读写的文件。 若文件不存在则建立该文件, 如果文件存在, 在保留原来内容基础上, 写入的数据会被加到文件尾。

⑦b 与上述符号组合使用, 如rb、 w+b 或ab+等组合, 代表打开的文件为二进制文件, 而非纯文字文件。

文件顺利打开后会返回一个指向流的文件指针, 若文件打开失败则返回NULL, 并把错误代码存在errno 中。 一般, 若打开文件失败, 接下来的读写动作也无法顺利进行, 所以在编程时要养成良好的习惯, 在代码中对打开后的返回值做判断, 并给出错误处理代码。

如果想得到类似前面open 系统调用打开文件时得到的文件描述符, 可以利用fileno 函数, fileno()可取得参数stream 指向的文件流对应的文件描述符:

学习Shell 命令时, 用chmod 查看新建的文件往往具有一组默认权限设置, 这是基于系统的umask 掩码设置得到的, 由fopen()所建立的新文件也会受umask 设置影响具有默认的权限。

2. 读文件

(1) fread 以记录为单位从文件流读取数据, 函数原型如下:

参数说明如下。

①ptr: 指向欲存放读取数据的数据缓冲区。

②size: 指定每个数据记录的长度

③nitems: 读取的记录个数。

④stream: 为已打开的文件指针。

Fread()会返回实际读取到的记录数目, 如果比参数nitems 小, 代表可能读到了文件尾或有错误发生, 这时必须用feof()或ferror()来判断发生了什么情况。

如果已到文件尾feof 会返回非零值, 其他情况返回0。

(2) 以字符为单位从文件流读取数据的有fgetc、 getc、 getchar, 函数原型如下:

fgetc 从参数stream 所指的文件中读取一个字符。 返回结果为读取到的字符, 若返回EOF 则表示到了文件尾。 getc()、 getchar()都是宏定义, 非真正的函数调用, 后者默认从stdin 标准输入获得字符。

(3) 以字符串为单位从文件流读取数据的有fgets、 gets, 函数原型如下:

fgets()用来从参数stream 所指的文件内读入字符并存到参数s 所指的内存空间, 一直读取到出现换行字符、 读到文件尾或是已读size-1 个字符为止, 最后加上NULL 作为字符串结束。 如果函数返回值为NULL 表示有错误发生。 代码举例:

gets()是从标准设备读入字符并存到参数s 所指的内存空间, 读到换行字符或文件尾为止, 调用成功返回s 指针, 而返回NULL 则表示有错误发生。 gets()无法知道字符串s的大小, 读取数据的写入容易造成缓冲溢出的安全性问题, 一般建议用fgets()取代。

3. 写文件

(1) fwrite 以记录为单位向文件流写入数据, 函数原型如下:

参数说明如下。

①ptr: 指向欲写入数据的数据缓冲区。

②size: 指定每个数据记录的长度。

③nitems: 读取的记录个数。(www.daowen.com)

④stream: 为已打开的文件指针。

将数据写入文件流中。 fwrite()会返回实际写入的记录数目。

(2) 以字符为单位写入文件的有fputc、 putc、 putchar, 函数原型如下:

fputc 将参数c 转为unsigned char 后写入参数stream 指定的文件中, 写入成功则返回参数c, 若写入失败返回EOF。

(3) 以字符串为单位写入文件的有fputs、 gets, 函数原型如下:

fputs()用来将参数s 所指的字符串写入到参数stream 所指的文件内。 写入成功则返回写的字符个数, 若写入失败返回EOF。 putchar()用来将参数c 字符写到标准输出设备。

(4) fflush(更新缓冲区)。

强迫将缓冲区内的数据写回参数stream 指定的文件中。 如果参数stream 为NULL, 代表不指定对象, 则fflush()会将所有打开的文件数据更新。 函数调用成功返回0, 失败返回EOF, 错误代码存于errno 中, 比如, 若错误代码为“EBADF”, 表示参数stream 指定的文件未被打开, 或打开状态为只读。

4. 操作读写位置

(1) fseek 移动文件流的读写位置。

函数原型如下:

函数说明如下。

①stream: 指向已打开的文件流的指针。

②offset: 为根据参数whence 来移动读写位置的位移数。

③whence: 设置offset 偏移值, 其用法如下。

•SEEK_ SET 设置从距文件开头offset 位移量处为读写位置。

•SEEK_ CUR 从当前读写位置往后增加offset 个位移量处为读写位置。

•SEEK_ END 从文件尾往后再增加offset 个位移量处为读写位置。

•设置SEEK_ CUR 或SEEK_ END 时, 参数offset 允许负值的出现。

举例如下:

当调用成功时则返回0, 若有错误则返回-1, errno 会存放错误代码。 fseek()不像lseek()会返回读写位置, 可用ftell()来取得目前读写的位置。

(2) rewind 重设文件流的读写位置为文件开头。

函数原型如下:

把文件流的读写位置移至文件开头。 参数stream 为已打开的文件指针。 此函数与上面的fseek 等效。

(3) ftell 获取文件的当前读写位置。

函数原型如下:

返回值是当前读写位置偏离文件头部的字节数。 用fseek 函数把位置指针移到文件尾,再用ftell 函数获得这时位置指针距文件头的字节数, 就能得到文件长度。

4.fclose 关闭文件

fclose()用来关闭fopen()打开的文件, 函数原型说明如下:

调用fclose 会引发缓冲区内的数据写入文件, 释放系统提供的文件资源。 若关文件动作成功则返回0, 有错误发生则返回EOF, 并把错误代码存到errno, 如常见的错误代码“EBADF”表示参数stream 不是已打开的文件。

文件编程还有缓冲设置、 目录处理等功能函数, 如setbuf 设置文件流的缓冲区, setlinebuf 设置文件流为线性缓冲区等, 此处不再展开, 需要的时候从手册查询学习。

【例5-5】 利用C 库函数写文件举例, 并与【例5-4】比较性能, testFopenW.c 源代码如下。

编译执行结果如下。

比较后能看到之前提到过C 库函数实现封装了缓冲处理, 效率更好。

◎C 库函数安全问题

标准C 库函数及数据类型在任何操作系统下都可以编译运行, 只是其内部的实现原理或不相同。 C 程序运行时调用的库函数通过glibc 与操作系统进行交互, 而在Windows 上则通过CRT(C 运行时库)来与操作系统交互。 如标准库函数fopen, glibc与msvcrt 分别调用自身系统的API: open 和CreateFile 分别实现fopen 的功能。 由于C语言存在很多不检查边界的问题, 其库函数中有不少不安全函数存在缓冲区溢出的危险, 在某些要求严格的编译环境会报错。 如VS 2017 中使用fopen、 sprintf 等函数会出现安全问题报错。 此时需要考虑用新标准提供的安全函数做替代。 但基于通用性, 本书学习仍采用了较为普适的标准函数。

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈