系统编程:标准IO和文件IO

一、概述

标准IO:标准I/O是ANSI C建立的一个标准I/O模型,是一个标准函数包和stdio.h头文件中的定义,不依赖系统内核,所以移植性强。又称为高级磁盘I/O,遵循ANSI C相关标准。只要开发环境中有标准I/O库,标准I/O就可以使用。(Linux 中使用的是glibc,它是标准C库的超集。不仅包含ANSI C中定义的函数,还包括POSIX标准中定义的函数。因此,Linux 下既可以使用标准I/O,也可以使用文件I/O)。标准I/O库处理很多细节,例如缓冲分配,以优化长度执行I/O等。
文件IO:文件I/O即系统调用IO,也称为不带缓冲的I/O(unbuffered I/O)。不带缓冲指的是每个read,write都调用内核中的一个系统调用。也就是一般所说的低级磁盘I/O,遵循POSIX相关标准,任何兼容POSIX标准的操作系统上都支持文件I/O。——是操作系统提供的基本IO服务,与OS绑定,特定于Linux或Unix平台。

标准IO可以看成在文件I/O的基础上由标准I/O库(stdio.h)封装并维护了缓冲机制。

二、标准IO

头文件需求:

#include <stdio.h>

1.fopen和fclose

(1)fopen

fopen的函数功能是打开一个文件。
首先看看fopen的函数声明:

FILE *fopen(const char *path, const char *mode);

第一个参数path是文件地址,传入的是不可变的字符串;第二个参数是mode是指打开方式,传入的也是不可变的字符串;返回的是FILE指针。
mode的可选项主要有:

"r" Open text file for reading. The stream is positioned at the beginning of the file.
"r+" Open for reading and writing. The stream is positioned at the beginning of the file.
"w" Truncate file to zero length or create text file for writing. The stream is positioned at the beginning of the file.
"w+" Open for reading and writing. The file is created if it does not exist, otherwise it is truncated. The stream is positioned at the beginning of the file.
"a" Open for appending (writing at end of file). The file is created if it does not exist. The stream is positioned at the end of the file.
"a+" Open for reading and appending (writing at end of file). The file is created if it does not exist. The initial file position for reading is at the beginning of the file, but output is always appended to the end of the file.

(2)fclose

fclose的函数功能是关闭一个文件。
fclose的函数声明:

int fclose(FILE *fp);

传入的参数是FILE指针,即fopen创建的那个指针;成功则返回0,否则返回EOF,并将错误存储在errno中。

附:Linux中系统调用的错误都存储于 errno中,errno由操作系统维护,存储就近发生的错误,即下一次的错误码会覆盖掉上一次的错误。

2.fgetc和fputc

(1)fgetc

fgetc的功能是从stream中读取下一个character。
函数声明如下:

int fgetc(FILE *stream);

传入的参数是stream来源即FILE指针。

(2)fputc

fputc的功能是将一个character写入到stream中。
函数原型是:

int fputc(int c, FILE *stream);

第一个参数就是要写入的字符,虽然是int型,但是只用低八位(unsigned char);第二个参数即写入到的stream来源即FILE指针。

3.fgets和fputs

(1)fgets

fgets的功能是从stream中读取至多一定数量的字符,并且存入buffer中,遇到'\n'或者EOF就停止读取。
请看函数声明:

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

第一个参数是字符串buffer指针,传入的是char*,这里当然是可变的;第二个参数是至多读取的字符数,传入的是int;第三个参数就是stream来源即FILE指针。
这个函数把获取到的字符传入buffer后,还会自动在末尾加上'\0'表示字符串的结束。

(2)fputs

fputs的功能是将字符串s传到stream中。
函数声明是:

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

第一个参数是不可变的字符串,第二个参数就是要写到的stream来源这里是FILE指针。
字符串被写入stream后,还会自动在末尾加上'\0'表示结束。

4.fread和fwrite

(1)fread

函数声明:

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

fread函数从stream中读取nmemb个元素,其中每个元素的长度为size,读取后存入到本地的ptr所指空间中。返回实际读到的数据项个数nmemb,注意这里不是字节数,只有当函数参数size为1时,数据项个数nmemb才等于字节数

(2)fwrite

函数声明:

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

fwrite函数从ptr指针中读取nmemb个元素(由于不涉及修改ptr指向的内存,故为const),其中每个元素的长度为size,将其写入到stream即FILE指针中去。返回实际写入的数据项个数nmemb,注意这里不是字节数,只有当函数参数size为1时,数据项个数nmemb才等于字节数

5.printf和scanf

(1)printf

printf函数声明为:

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

如果成功的话,则返回已打印的字节数;如果失败,会返回一个小于0的负值。

(2)scanf

scanf的函数声明为:

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

scanf函数返回成功读入的数据项数,它可以比实际输入的少;如果遇到错误或者读入数据时遇到了“文件结束”则返回EOF。

6.fseek和ftell

(1)fseek

fseek的函数声明是:

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

fseek函数可以设置FILE指针stream的位置。

如果执行成功,stream将指向以whence为基准,偏移offset(指针偏移量)个字节的位置,函数返回0。如果执行失败(比如offset取值大于等于210241024*1024,即long的正数范围2G),则不改变stream指向的位置,返回一个非0值。
fseek函数和lseek函数类似,但lseek返回的是一个off_t数值,而fseek返回的是一个整型。lseek是文件IO函数,见下文。

(2)ftell

ftell的函数声明是:

long ftell(FILE *stream);

该函数返回给定流stream的当前文件位置。返回的是一个long型,而long型占4个字节,考虑到long有符号,即文件最大不超过2^31bit,即2Gb。文件大小被限制在了这么小。其实这是一个历史遗留问题,当年的程序设计者的年代,他们觉得long型已经够大,但是他们没有预料到计算机硬件的发展速度之快。

7.fseeko和ftello

(1)fseeko

fseeko的函数原型是:

int fseeko(FILE *stream, off_t offset, int whence);

(2)ftello
ftello的函数原型是:

off_t ftello(FILE *stream);

fseeko和ftello两个函数的存在,就是为了解决fseek和ftell这两个函数中文件2G大小限制的历史遗留问题。

8.rewind

函数声明如下:

void rewind(FILE *stream);

rewind函数就是将stream的文件偏移量设为0,使其指向文件开头。它的功能等同于:

(void) fseek(stream, 0L, SEEK_SET)

9.fflush

函数声明如下:

int fflush(FILE *stream);

fflush()会强迫将缓冲区内的数据写回参数stream 指定的文件中。

10.getline

函数声明如下:

ssize_t getline(char **lineptr, size_t *n, FILE *stream);

参数:

getline() reads an entire line from stream, storing the address of the buffer containing the text into *lineptr. The buffer is null-terminated and includes the newline character, if one was found.

返回值:

On success, getline() return the number of characters read, including the delimiter character, but not including the terminating null byte ('\0'). This value can be used to handle embedded null bytes in the line read.

三、文件IO

头文件需求

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

1.open和close

(1)open

函数声明为:

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

函数接收文件路径pathname,返回一个文件描述符(File Descriptor,简称fd),故返回int型。并且返回的是当前可用的数字最小的文件描述符;若打开文件时发生错误,open()将返回−1,错误号 errno 标识错误原因。。
flags参数值介绍

(2)close

函数声明为:

int close(int fd);

函数关闭一个文件描述符fd。如果成功了就返回0,失败则返回-1,错误号errno标识错误原因。

2.read和write

(1)read

函数声明:

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

read函数的功能是从文件描述符为fd的文件中中读取count字节,并存入到buffer中。如果失败了则返回-1,错误号errno标识错误原因。如果成功的话则返回读取到的字节数(0就代表已经读到了文件末尾)。即使返回的字节数不等于我们要求的字节数count,也不代表发生了错误,因为可能会发生以下的情况:

  • 文件已经接近末尾
  • 是从pipe或terminal中读取
  • read函数被signal中断

(2)write

函数声明:

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

write函数的功能是从buffer中获得count字节的数据并把它存入到文件描述符为fd的文件中。如果失败了,则返回-1,错误号errno标识错误原因;如果成功了,则返回写入的字节数,0即代表什么都没被写入。

3.lseek

系统内核会记录其文件偏移量,有时也将文件偏移量称为读写偏移量或指针。文件偏移量是指执行下一个 read()或 write()操作的文件起始位置,会以相对于文件头部起始点的文件当前位置来表示。文件第一个字节的偏移量为 0。文件打开时,会将文件偏移量设置为指向文件开始,以后每次 read()或 write()调用将自动对其进行调整,以指向已读或已写数据后的下一字节。因此,连续的 read()和 write()调用将按顺序递进,对文件进行操作。
函数声明为:

off_t lseek(int fd, off_t offset, int whence);

offset 参数指定了一个以字节为单位的数值,而whence参数有以下三种:

用图片来表示的话如下:

lseek调用成功则会返回新的文件偏移量。如我们想获得文件偏移量的当前位置,有示例如下:
curr = lseek(fd, 0, SEEK_CUR);

4.ioctl

除了上述通用 I/O 模型外,ioctl()系统调用又为执行文件和设备操作提供了一种多用途机制。
ioctl函数声明如下:

int ioctl(int d, int request, ...);

其中第一个参数需要的是一个打开文件的文件描述符int型d,第二个参数是设备依赖请求码。ioctl()调用的第三个参数采用了标准 C 语言的省略符号(...)来表示(称之为 argp),可以是任意数据类型。ioctl()根据 request 的参数值来确定 argp 所期望的类型。通常情况下,argp是指向整数或结构的指针,有些情况下,不需要使用 argp。

文件IO的总结

为了对普通文件执行 I/O 操作,首先必须调用 open()以获得一个文件描述符。随之使用read()和 write()执行文件的 I/O 操作,然后应使用 close()释放文件描述符及相关资源。这些系统调用可对所有类型的文件执行 I/O 操作。所有类型的文件和设备驱动都实现了相同的 I/O 接口,这保证了 I/O 操作的通用性,同时也意味着在无需针对特定文件类型编写代码的情况下,程序通常就能操作所有类型的文件。对于已打开的每个文件,内核都维护有一个文件偏移量,这决定了下一次读或写操作的起始位置。读和写操作会隐式修改文件偏移量。使用 lseek()函数可以显式地将文件偏移量置为文件中或文件结尾后的任一位置。在文件原结尾处之后的某一位置写入数据将导致文件空洞。从文件空洞处读取文件将返回全 0 字节。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容