10.1 文件I/O操作概述

在Linux系统中,文件I/O操作可以分为两类,一类是基于文件描述符的I/O操作,另一类是基于数据流的I/O操作。

10.1.1 文件描述符简介

在文件操作一章中,也经常提到文件描述符这个概念。所谓文件描述符,就是进程与打开的文件的一个桥梁,通过这个桥梁,才可以在进程中对这个文件进行读写等操作。

在Linux环境下,每打开一个磁盘文件,都会在内核中建立一个文件表项,文件表项中存储着文件的状态信息、存储文件内容的缓冲区和当前文件的读写位置。如果同一磁盘文件打开了3次,就会创建3个这样的文件表项(a,b和c),读写文件时,只会改变该文件表项中的文件读写位置。这3个文件表项存储在一个文件表数组table[3]中,在这里table[0]=a,table[1]=b,table[2]=c。这个文件表的下标就称之为文件描述符,将这个文件描述符存储在一个数组中des[3]={0,1,2},那么,在进程中就可以通过这个des数组下标引用文件表项。也就是说,通过文件描述符就可以访问到这个磁盘文件。

10.1.2 数据流概述

从数据操作方式这个角度来说,Linux系统中的文件(无论是普通文件还是设备文件)可以看做是数据流。对文件进行操作之前,必须先调用标准I/O库函数fopen()将数据流打开。打开数据流之后,就可以对数据流进行输入和输出的操作。

标准I/O库函数是C语言中所特有的用于高级接口的函数,这些库函数存放在C语言的stdio.h头文件中,因此这些用于数据流的I/O操作函数不仅适用于Linux系统,还适用于其他的操作系统。由此可见,此库函数的引用大大增加了程序的移植性。

要对数据流进行读写操作时,需要标准I/O库函数和FILE类型的文件指针一起来实现。这个文件指针石达开数据流时返回的指针,该指针用来表示要操作的数据流。

当执行程序时,有3个数据流不需要特定的函数进行打开的操作,他们会自动打开。这3个数据流是标准输入、标准输出和标准错误输出。他们是自动打开的,当不使用时,也会自动关闭。

然而,调用标准I/O库函数fopen()打开的数据流,在对数据流进行操作后,需要调用fclose()函数将其关闭。fclose()函数在关闭数据流之前,会清空在操作过程中分配的缓冲区并保存数据信息。

10.2 基于文件描述符的I/O操作

10.2.1 文件的打开与关闭

1)open()函数

#include<sys/types.h>

#include<sys/stat.h>

#include<fcntl.h>

int open(const char *pathname, int flags)

int open(const char *pathname, int flags, mode_t mode)

int creat(const char *pathname, mode_t mode)

上述的两个open()函数和一个creat()函数在调用成功时,都会返回其新分配的文件描述符;否则返值为-1,并设置适当的error。

2)close()函数

#incldue<unistd.h>

int close(int fd)

当一个进程终止时,内核对该进程的所有尚未关闭的文件描述符调用close()函数关闭,所以即使用户程序不调用close()函数,在终止时内核也会自动关闭它打开的所有文件。但是,对于网络服务器这种一直运行的程序,文件描述符一定要及时关闭,否则随着打开的文件越来越多,会占用大量文件描述符和系统资源。

有函数open()返回的文件描述符一定是该进程尚未使用的最小描述符。由于程序启动时自动打开标准输入、标准输出和标准错误输出,因此文件描述符0,1,2会存在,那么第一次调用open()函数打开文件时返回的文件描述符通常会是3,再调用open()函数就会返回4.可以利用这一点在标准输入、标准输出或标准错误输出上打开一个新文件,是想重定向的功能。例如,首先调用close()函数关闭文件描述符1,然后调用open()函数打开一个常规文件,则一定会返回文件描述符1,这是标准输出就不再是终端,而是一个常规文件了,再调用printf()函数就不打印到屏幕上,而是写到这个文件中了。在文件操作一章中讲到的dup2()函数就是另外一种在指定的文件描述符上打开文件的方法。

10.2.2 文件的读写操作

1)read()函数

#include<unistd.h>

ssize_t read(int fd, void *buf, size_t count)

2)write()函数

#include<unistd.h>

ssize_t write(int fd, const void *buf, size_t count)

10.2.3 文件的定位

每个文件都记录着当前读写位置,打开文件时读写位置是0,表示文件开头,通常读写多少个自己就会将读写位置往后移多少个字节。

以O_APPEND方式打开文件,每次写操作都会在文件末尾追加数据,然后间读写位置移动到新的文件末尾。

1)lseek()函数

lseek()函数可以移动当前读写位置,通常称为偏移量,该函数的定义形式如下:

#include<sys/types.h>

#include<unisd.h>

off_t lseek(int fildes, off_t offset, int whence)

10.3 基于数据流的I/O操作

10.3.1 文件的打开与关闭

在操作文件之前要用fopen()函数打开文件,操作结束后,要用fclose()函数关闭文件。

1)fopen()函数

#include<stdio.h>

FILE *foepn(cosnt char *path, cosnt char *mode)

2)fclose()函数

#include<stdio.h>

int fclose(FILE *fp)

10.3.2 字符输入/输出

1)fgetc()函数

fgetc()函数从指定的文件中读一个字节,该函数的定义形式如下:

#include<stdio.h>

int fgetc(FILE *stream)

在程序中,偶尔会遇到getchar()函数,也是用于读取一个字节,但它是从标准输入读一个字节。在程序中调用getchar()函数相当于调动fgetc(stdin)

在使用fgetc()函数时需要注意一下几点:

①调用fgetc()函数时,指定的文件的打开方式必须是可读的。

②函数fgetc()调用成功时,返回的是读到的字节,应该为unsigned char,但fgetc()函数在原型中返回值类型时int,原因在于函数调用出错或读到文件末尾时fgetc()会返回EOF,即-1,保存在int型的返回值是0xffffffff,如果读到字节0xff,由unsigned char型转换int 型时0x000000ff,只有规定返回值是int型才能把这种情况区分开,如果规定返回值是unsigned char型,那么当返回值是0xff时则无法区分到底是EOF还是字节0xff。

2)fputc()函数

#include<stdio.h>

int fputc(int c, FILE *stream)

10.3.3 字符串输入/输出

1)fgets()函数

#include<stdio.h>

char *fgets(char *s, int size, FILE *stream)

对于fgets()函数而言,'\n'是一个特别的字符,作为结束符;而'\0'并无任何特别之处,只用作普通字符串读入。正因为'\0'作为一个普通的字符串,因此无法判断缓冲区中的'\0'究竟是从文件读上来的字符还是有fgets()函数自主添加的结束符,所以fgets()函数只用于读文本文件而不提倡读二进制文件,并且文本文件中的所有字符串不能有'\0'。

2)fputs()

#include<stdio.h>

int fputs(const char *s, FILE *stream)

缓冲区s中保存的是以'\0'结尾的字符串,fputs()将该字符串写入文件stream,但并不写入结尾的'\0',且字符串中可以有'\n',也可以没有'\n'。

10.3.4 数据块输入/输出

1)fread()和fwrite()函数

#include<stido.h>

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)

size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)

10.3.5 格式化输入/输出

所谓格式化输入/输出,就是按照一定的格式将数据进行输入/输出操作。在程序中经常用到的printf()函数和scanf()函数是用于对终端设备文件的读写操作,这两个函数被称为格式化输入/输出,因为在使用这两个函数时,需要制定读写数据的数据类型并按照一定的格式进行读写。

1)格式化输入函数

#include<stdio.h>

int printf(const char *format,...)

int fprintf(FILE *stream, const char *format)

int sprintf(char *str, size_t size, const char *format,...)

2)格式化输出函数

#include<stdio.h>

int scanf(const char *format,...)

int fscanf(FILE *stream, const char *format,...)

int sscanf(const char *str, const char *format,...)

10.3.6 操作读写位置的函数

1)fseek()函数

#inlcude<stdio.h>

int fseek(FILE *stream, long offset, int whence)

函数fseek()的作用是用来移动文件内部位置指针。

2)ftell()函数

#inlcude<stdio.h>

long ftell(FILE *stream)

ftell()函数的作用是得到stream指定的流式文件中的位置。

3)rewind()函数

void rewind(FILE *stream)

rewind()函数的作用是使位置指针重新返回文件的开头,该函数没有返回值。

10.3.7 C标准的I/O缓冲区

C标准库在调用fopen()函数时,都会给此文件分配一个I/O缓冲区,可以加速读写操作,原因在于用户程序需要调用C标准I/O库函数(如fread()、fwrite()等基于文件流I/O操作)读写文件,当缓冲区装满后,再由系统调用的I/O函数(如read()、write()等基于文件描述符的I/O操作)把读写请求传给内核,最终由内核驱动磁盘或设备完成I/O操作。

由此看来,为文件分配的内存缓冲区大小,直接影响到实际操作外村设备的次数,内存中为未见分配的缓冲区越大,操作外存的次数会越小,因此读写数据的速度会越来越快,效率就会随之增高。

然而,有时用户程序等不及将缓冲区都装满之后再传给内核,进行I/O操作,而是希望把I/O缓冲区中的数据立刻传给内核,让内核写回设备,这种行为叫做flush操作,对应的库函数是fflush()。

C标准库的I/O缓冲区有全缓冲、行缓冲和无缓冲3种类型。

1)全缓冲:如果缓冲区写满了,就写回内核。普通文件通常是全缓冲的。

2)行缓冲:如果用户程序写的数据中有'\n',就把这一行写回内核,或者缓冲区写满后就写回内核。标准输入和标准输出对应中断设备时通常是行缓冲。

3)无缓冲:用户程序每次调库函数做写操作都要通过系统调用写回内核。标准错误输出通常是无缓冲的。这样用户程序产生的错误信息就可以尽快输出到设备。

使用缓冲区时,会使用到如下两类操作,一个是设置缓冲区属性,另外一个是清空缓冲区。

4)设置缓冲区属性

#include<stdio.h>

void setbuf(FILE *stream, char *buf)

void setbuffer(FILE *stream, char *buf, size_t size)

void setlinebuf(FILE *stream)

int setvbuf(FILE *stream, char *buf, int mode, size_t size)

setbuf()函数主要实现了为参数buf所指定的缓冲区设置大小。此函数中,设定缓冲区大小的值只有两个,一个是常数BUFSIZ,另一个是NULL。当定义值为BUFSIZ时,代表设置缓冲区为全缓冲;若为NULL,则代表设置缓冲区为无缓冲形式。

setbuffer()与setbuf()功能相同,只是setbuffer()函数可以任意指定缓冲区大小为size

setlinebuf()函数实现了将stream指向的缓冲区设置为行缓冲。

setvbuf()函数融合了上述3种函数的功能,既可以设置缓冲区的任意大小size,也可以设置缓冲区的任意类型,如mode参数取值为_IOFBF(全缓冲类型)、_IOLBF(行缓冲类型)或_IONBF(无缓冲类型)。

5)清空缓冲区

#include<stdio.h>

int fflush(FILE *stream)

fflush()函数实现将缓冲区中的尚未写入文件的数据强制性地写进stream所指定的文件中,然后清空缓冲区。如果stream为NULL,此函数会将所有打开的文件数据更新。

10.4 小结

本章主要介绍了Linux系统下的文件I/O操作。在Linux系统下存在两种文件I/O操作,一种是基于文件描述符的I/O操作,这里面的I/O操作都是Linux系统中提供并直接作用于内核的,是非缓冲的I/O操作;另一种I/O操作是基于数据流的I/O操作,是由C语言的stdio库所提供的,需要在内存中开辟一块缓冲区,在缓冲区中进行快速地读写操作。本章主要结合典型实例介绍了上述两种I/O操作方式对文件的打开、关闭、读、写、文件定位等操作。

转载于:https://www.cnblogs.com/zuixiaoyao/p/3530298.html

文件的输入/输出操作相关推荐

  1. linux c文件操作,Linux C 文件的输入/输出操作

    10.1 文件I/O操作概述 在Linux系统中,文件I/O操作可以分为两类,一类是基于文件描述符的I/O操作,另一类是基于数据流的I/O操作. 10.1.1 文件描述符简介 在文件操作一章中,也经常 ...

  2. 独家|OpenCV1.8 使用XML和YAML文件实现文件的输入/输出

    翻译:陈之炎 校对:李海明本文约2400字,建议阅读5分钟本文为大家介绍了OpenCV使用XML和YAML文件实现的输入输出. 目标 本小节将回答以下问题: 如何使用YAML或XML文件打印和读取文本 ...

  3. JAVA订单的输入输出_Java的输入/输出操作

    Java的输入\输出机制 计算机的基本功能就是通过输入输出设备与外部其他设备尽心数据的交互,从其他设备读入数据叫做输入操作,将计算机内的数据写入到其他设备叫做输出操作.可以向计算机发送数据.又可以接受 ...

  4. 文件输出 java_用Java读写文件(输入/输出)-教程

    一.文件的Java I/O(输入/输出) 1.1.概述 在现代Java应用程序中,通常使用Java.nio.fileAPI来读写文件. Java将把所有输入作为字节流读取.input stream类是 ...

  5. C++ Primer 5th笔记(chap 17 标准库特殊设施)未格式化的输入/输出操作

    1. 格式化IO 输入和输出运算符(<< 和>>)根据读取或写入的数据类型来格式化它们. 输入运算符忽略空白符 输出应用补白 精度等规则操作 2. 未格式化 IO (unfor ...

  6. C 语言编程 — 输入/输出与文件操作

    目录 文章目录 目录 前文列表 输入/输出 scanf() 和 printf() getchar() 和 putchar() 文件操作 打开文件 关闭文件 写入文件 读取文件 二进制 I/O 函数 前 ...

  7. python文件输入符_python基础入门详解(文件输入/输出 内建类型 字典操作使用方法)...

    一.变量和表达式 >>> 1 + 1 2 >>> print 'hello world' hello world >>> x = 1 >&g ...

  8. JSP中的文件操作:数据流、File类、文件浏览、目录操作、上传下载

    ​ 文件可以永久地存储信息,从本质上讲文件就是存放在盘上的一系列数据的集合.应用程序如果想长期保存数据,就必须将数据存储到文件中,这就涉及到文件的操作.而在编写网站应用程序的过程中,有许多地方要对文件 ...

  9. 第15章-输入/输出 --- 理解Java的IO流

    (一)理解Java的IO流 JAVA的IO流是实现输入/输出的基础,它可以方便地实现数据的输入/输出操作,在Java中把不同的输入/输出(键盘.文件.网络连接等)抽象表述为"流"( ...

最新文章

  1. OnsenUI 前端框架(三)
  2. 2015 Multi-University Training Contest 1 - 10010 Y sequence
  3. 算法--------打家劫舍(动态规划,Java版本)
  4. QEMU — I/O QoS 的实现方式
  5. LNMP,PHP开启openssl,功能扩展,K哥
  6. Google Spanner:谷歌的全球分布式数据库
  7. CF1313D:Happy New Year(状压dp)
  8. mysql skip_counter_mysql的三个故障解决小结
  9. FCN用卷积层代替FC层原因(转)
  10. web.config中的InProc模式 与 StateServer模式[转]
  11. 软件绿色联盟开发者大会惊喜不断,今日还有重磅议程!
  12. html css . doc,html+CSS基础.doc
  13. 调查作业时,注意 【 调查深度 】 ,以及总结 【 中间成果物 】
  14. [NOIp2017 DG Day 2 T1] 奶酪
  15. ego电商项目:Rmi远程服务发布
  16. 【终结版】小家电安规要求以及世界各国安规认证知识分享
  17. (附源码)ssm教材管理系统 毕业设计 011229
  18. bzoj 3375: [Usaco2004 Mar]Paranoid Cows 发疯的奶牛
  19. laaS 、paaS和SaaS区别
  20. 医学影像开源数据集汇总

热门文章

  1. 华为root工具_华为Mate9解锁后无法ROOT 需要手动刷入Recovery怎么办【解决方法】...
  2. mysql 导出 没有函数_没有MYSQL FILE函数的CSV导出
  3. 8086条件转移指令JE,JZ
  4. c语言数组-1_C数组-智能问题与解答
  5. Java——多线程实现的三种方式
  6. 北京中信银行总行地址_中信银行拉萨分行举行“存款保险标识”启用和存款保险条例宣传活动...
  7. Linux内核设计与实现---虚拟文件系统
  8. python安全攻防---scapy使用
  9. hihoCoder 1227 2015 北京网络赛 A题
  10. 268. 缺失数字 golang