linux C编程7-标准I/O

目录

1、标准 I/O 和文件 I/O 的区别

1.1、标准输入、标准输出和标准错误

2、标准 I/O函数

2.1、fopen()函数

2.2、fclose()函数

2.3、fread()函数

2.4、fwrite()函数

2.5、fseek()函数

2.6、ftell()函数

2.7、feof()函数

2.8、ferror()函数

2.9、clearerr()函数

3、stdio缓冲区

3.1、setvbuf()函数

3.2、setbuf()函数

3.3、setbuffer()函数

3.4、fflush()函数

3.5、刷新 stdio 缓冲区

4、文件描述符与 FILE 指针互转

4.1、fileno()函数

4.2、fdopen()函数


1、标准 I/O 和文件 I/O 的区别

1) 标准 I/O 是标准 C 库函数,而文件 I/O 则是 Linux系统调用;
2) 标准 I/O 是由文件 I/O 封装而来,标准 I/O 内部实际上是调用文件 I/O 来完成实际操作的;

3) 可移植性:标准 I/O 相比于文件 I/O 具有更好的可移植性,通常对于不同的操作系统,其内核向应用层提供的系统调用往往都是不同;而对于标准 I/O 来说,由于很多操作系统都实现了标准 I/O 库,标准 I/O 库在不同的操作系统之间其接口定义几乎是一样的,所以标准 I/O 在不同操作系统之间相比于文件 I/O 具有更好的可移植性。
4)性能:标准 I/O 库在用户空间维护了自己的 stdio 缓冲区, 所以标准 I/O 是带有缓存的,而文件 I/O 在用户空间是不带有缓存的,所以在性能、效率上,标准 I/O 要优于文件 I/O。

1.1、标准输入、标准输出和标准错误

文件I/O函数是围绕文件描述符的,对于标准I/O函数是围绕流进行的。

文件 I/O 中,可以使用 STDIN_FILENO、 STDOUT_FILENO、 STDERR_FILENO来表示标准输入、标准输出和标准错误。

#include <unistd.h>

/* Standard file descriptors. */
#define STDIN_FILENO 0 /* Standard input. */
#define STDOUT_FILENO1 /* Standard output. */
#define STDERR_FILENO2 /* Standard error output. */

标准 I/O 中,可以使用 stdin、 stdout、 stderr 来表示标准输入、标准输出和标准错误。

#include <stdio.h>

/* Standard streams. */
extern struct _IO_FILE *stdin; /* Standard input stream. */
extern struct _IO_FILE *stdout; /* Standard output stream. */
extern struct _IO_FILE *stderr; /* Standard error output stream. */
/* C89/C99 say they're macros. Make them happy. */
#define stdin stdin
#define stdout stdout
#define stderr stderr

1.2、字符集

对于ASCII字符集,一个字符用一个字节表示。对于国际字符集,一个字符可用多个字节表示。标准I/O文件流可用于单字节或多字节字符集(宽字节)。

流的定向决定所读写的字符是单字符还是多字节的。当一个流被创建时,并没有定向。

1)若在未定向的流上使用一个多字节I/O函数(<wchar.h>),则将该流的定向设置为宽定向。

2)若在未定向的流上使用一个单字节I/O函数,则将该流的定向设置为字节定向。

只有两个函数可以改变流从定向。

1.2.1、fwide

1)若值为负,将试图使指定的流是字节定向的。

2)若值为正,将试图使指定的流是宽定向的。

2)若值为0,将不试图设置流的定向,但返回表示该流定向的值。

返回值:若流是宽定向的,返回正值;若流是字节定向的,返回负值;若流是未定向的,返回0。

注:此函数并不改变已定向流的定向。

注:此函数无出错返回,但可以在调用该函数前先清除errno,从fwide返回时检查errno的值,从而来判断是否出错。

1.2.2、freopen

2、标准 I/O函数

2.1、fopen()函数

打开或创建文件。

#include <stdio.h>
FILE *fopen(const char *path, const char *mode);

参数 path: 参数 path 指向文件路径,可以是绝对路径、也可以是相对路径。
参数 mode: 参数 mode 指定了对该文件的读写权限,是一个字符串。

mode说明对应于 open()函数的 flags 参数取值
r或rb以只读方式打开文件O_RDONLY
r+或r+b或rb+以可读、可写方式打开文件O_RDWR
w或wb以只写方式打开文件如果参数 path 指定的文件存在,将文件长度截断为 0;如果指定文件不存在则创建该文件O_WRONLY | O_CREAT | O_TRUNC
w+或w+b或wb+以可读、可写方式打开文件如果参数 path 指定的文件存在,将文件长度截断为 0;如果指定文件不存在则创建该文件。O_RDWR | O_CREAT | O_TRUNC
a或ab以只写方式打开文件打开以进行追加内容(在文件末尾写入),如果文件不存在则创建该文件。O_WRONLY | O_CREAT | O_APPEND
a+或a+b或ab+以可读、可写方式打开文件,以追加方式写入(在文件末尾写入),如果文件不存在则创建该文件。O_RDWR | O_CREAT | O_APPEND

使用字符b作为type的一部分,这使得标准I/O系统可以区分文本和二进制文件。但UNIX内核并不对这两种文件进行区分,所以在UNIX系统环境下指定字符b作为type的一部分实际上并无作用。

???当以读和写类型打开一个文件时(+),具有以下限制:

1)如果中间没有fflush、fseek、fsetpos或rewind,则在输出后面不能直接跟随输入。

2)如果中间没有fseek、fsetpos或rewind,或者一个输入操作没有达到文件尾端,则在输入操作之后不能太跟随输出。

返回值: 调用成功返回一个指向 FILE 类型对象的指针(FILE *),该指针与打开或创建的文件相关联,后续的标准 I/O 操作将围绕 FILE 指针进行。 如果失败则返回 NULL,并设置 errno 以指示错误原因。
注:调用 fopen()函数新建文件时无法手动指定文件的权限,但却有一个默认值权限(0666):

S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH 

2.1、freopen()函数

在一个指定的流上打开一个指定的文件,若该流已经打开,则先关闭该流。若流已经定向,则清除该定向。

此函数一般用于将一个指定的文件打开为一个预定义的流:标准输入、标准输出或标准错误。

2.1、fdopen()函数

取一个已有的文件描述符(从open、dup、dup2、fcntl、pipe、socket、socketpair或accept函数得到此文件描述符),并使一个标准I/O流与该描述符相结合。

此函数常用于由创建管道和网络通信通道函数返回的描述符。因为这些特殊类型的文件不能用标准I/O函数fopen打开,所以必须先调用设备专用函数以获得一个文件描述符,然后用fdopen使一个标准I/O流与该描述符相结合。

注:fdopen函数不能截断它为写打开的任一文件(例如,若该描述符原来是由open函数创建的。而且该文件已存在,则其O_TRUNC标志将决定是否截断该文件)

2.2、fclose()函数

关闭一个由 fopen()打开的文件。在该文件被关闭之前,冲洗缓冲区中的输出数据。缓冲区中的任何输入数据被丢弃。如果标准I/O库已经为该流自动分配了一个缓冲区,则释放此缓冲区。

当一个进程正常终止时(调用exit函数或从main函数返回),则所有带未写缓冲数据的标准I/O流都被冲洗,所有打开的标准I/O流都被关闭。

#include <stdio.h>
int fclose(FILE *stream);

参数 stream :为 FILE 类型指针,

返回值 :调用成功返回 0;失败将返回 EOF(也就是-1),并且会设置 errno 来指示错误原因。

2.5、fseek()函数

设置文件读写位置偏移量。

#include <stdio.h>
int fseek(FILE *stream, long offset, int whence);

参数 stream: FILE 指针。
参数 offset:偏移量,以字节为单位。
参数 whence: 

SEEK_SET读写偏移量将指向 offset 字节位置处(从文件头部开始算)
SEEK_CUR读写偏移量将指向当前位置偏移量 + offset 字节位置处, offset 可以为正、也可以为负,如果是正数表示往后偏移,如果是负数则表示往前偏移
SEEK_END读写偏移量将指向文件末尾 + offset 字节位置处,同样 offset 可以为正、也可以为负,如果是正数表示往后偏移、如果是负数则表示往前偏移。

返回值: 成功返回 0;发生错误将返回-1,并且会设置 errno 以指示错误原因; 

注:返回值成功返回 0;发生错误将返回-1!!!(与lseek的返回值不一样)

2.6、ftell()函数

获取文件当前的读写位置偏移量。

#include <stdio.h>
long ftell(FILE *stream);

参数 stream: FILE 指针。

返回值 偏移量。

注:可以通过 fseek()和 ftell()来计算出文件的大小。

2.7、feof()函数

用于测试参数 stream 所指文件的 end-of-file 标志。

#include <stdio.h>
int feof(FILE *stream);

参数 stream: FILE 指针。

返回值 :如果 end-of-file 标志被设置了,则返回一个非零值。如果 end-of-file 标志没有被设置,则返回 0。

2.8、ferror()函数

测试参数 stream 所指文件的错误标志。

#include <stdio.h>
int ferror(FILE *stream);

参数 stream: FILE 指针。

返回值 :如果错误标志被设置了,则调用 ferror()函数将返回一个非零值,如果错误标志没有被设置,则返回 0。

2.9、clearerr()函数

清除 end-of-file 标志和错误标志,当调用 feof()或 ferror()校验这些标志后,通常需要清除这些标志,避免下次校验时使用到的是上一次设置的值,此时可以手动调用 clearerr()函数清除标志。

#include <stdio.h>
void clearerr(FILE *stream);

参数 stream: FILE 指针。

注:对于 end-of-file 标志,除了使用 clearerr()显式清除之外,当调用 fseek()成功时也会清除文件的 end-offile 标志。

3、缓冲

标准I/O提供缓冲的目的是尽可能的减少read和write调用的次数。它也对每个I/O流自动地进行缓冲管理,从而避免了应用程序需要考虑这一点所带来的麻烦。

3.1、setvbuf()函数

对文件的 stdio 缓冲区进行设置,譬如缓冲区的缓冲模式、 缓冲区的大小、起始地址等。

#include <stdio.h>
int setvbuf(FILE *stream, char *buf, int mode, size_t size);

参数 stream: FILE 指针,用于指定对应的文件, 每一个文件都可以设置它对应的 stdio 缓冲区。
参数 buf: 如果参数 buf 不为 NULL,那么 buf 指向 size 大小的内存区域将作为该文件的 stdio 缓冲区,因为stdio 库会使用 buf 指向的缓冲区,所以应该以动态或静态的方式在堆中为该缓冲区分配一块空间,而不是分配在栈上的函数内的自动变量(局部变量)。如果 buf 等于 NULL,那么 stdio 库会自动分配一块合适长度(长度非size参数)的系统缓冲区作为该文件的 stdio 缓冲区(除非参数 mode 配置为非缓冲模式) 。
参数 mode: 参数 mode 用于指定缓冲区的缓冲类型,可取值如下:

_IONBF不对 I/O 进行缓冲(标准错误 stderr 默认属于这一种类型,从而保证错误信息能够立即输出) 意味着每个标准 I/O 函数将立即调用 write()或者 read(),并且忽略 buf 和 size 参数(可以分别指定两个参数为 NULL 和 0)
_IOLBF采用行缓冲 I/O(对于终端设备默认采用的就是行缓冲模式,譬如标准输入和标准输出

对于输出流,在输出一个换行符前将数据缓存(除非缓冲区已经被填满), 当输出换行符时,再将这一行数据通过文件 I/O write()函数刷入到内核缓冲区中;

对于输入流, 每次读取一行数据。

_IOFBF

采用全缓冲 I/O(默认普通磁盘上的常规文件默认常用这种缓冲模式)。
 

对于输出流,当 fwrite 写入文件的数据填满缓冲区时,才调用 write()将 stdio 缓冲区中的数据刷入内核缓冲区;

对于输入流,每次读取 stdio 缓冲区大小个字节数据。

size: 指定缓冲区的大小。
返回值: 成功返回 0,失败将返回一个非 0 值,并且会设置 errno 来指示错误原因。

3.2、setbuf()函数

setbuf()函数构建与 setvbuf()之上,执行类似的任务。要么将 buf 设置为 NULL 以表示无缓冲,要么指向由调用者分配的 BUFSIZ 个字节大小的缓冲区(BUFSIZ 定义于头文件<stdio.h>中,该值通常为 8192)

#include <stdio.h>

void setbuf(FILE *stream, char *buf);

setbuf()调用除了不返回函数结果(void)外,就相当于:

setvbuf(stream, buf, buf ? _IOFBF : _IONBF, BUFSIZ);

3.3、setbuffer()函数

对文件的 stdio 缓冲区进行设置。

#include <stdio.h>

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

setbuffer()调用除了不返回函数结果(void)外,就相当于:

setvbuf(stream, buf, buf ? _IOFBF : _IONBF, size);

3.4、fflush()函数

刷新stdio缓冲区。此函数使3该流所有未写的数据都传送至内核。作为一种特殊情况,若fd是NULL,则此函数将导致所有输出流被冲洗。

#include <stdio.h>

int fflush(FILE *stream);

参数 stream:FILE指针对象。

返回值:调用成功返回 0,否则将返回-1,并设置 errno 以指示错误原因。

3.5、刷新 stdio 缓冲区

1)调用 fflush()库函数可强制刷新指定文件的 stdio 缓冲区;
2)调用 fclose()关闭文件时会自动刷新文件的 stdio 缓冲区;
3)程序退出时会自动刷新 stdio 缓冲区(注如果使用_exit 或_Exit()终止程序则不会刷新) 。

4、文件描述符与 FILE 指针互转

4.1、fileno()函数

将标准 I/O 中使用的 FILE 指针转换为文件 I/O 中所使用的文件描述符。如果要调用dup或fcntl等函数,则需要此函数。

#include <stdio.h>

int fileno(FILE *stream);

参数 stream:要转换的 FILE 指针

返回值:成功返回文件描述符,错误将返回-1,并且会设置 errno 来指示错误原因。

4.2、fdopen()函数

将文件 I/O 中所使用的文件描述符转换为标准 I/O 中使用的 FILE 指针。

#include <stdio.h>

FILE *fdopen(int fd, const char *mode);

参数 fd:要转换的文件描述符

参数 mode:同fopen的mode参数

返回值:成功返回FILE 指针,错误将返回-1,并且会设置 errno 来指示错误原因。

5、读和写流

一旦打开了流,则可在3种不同类型的非格式化I/O中进行选择,对其进行读、写操作。

1)每次一个字节的I/O

2)每次一行的I/O。fgets和fputs.每行都以一个换行符终止。

3)直接I/O.fread和fwrite函数,常用于从二进制文件中每次读或写一个结构。

5.1、输入函数

getc、fgetc、getchar

返回值为整型的理由是,可以返回所有可能的字符值再加上一个已出错或以达到文件尾端的指示值。

getchar等同于getc(stdin)

getc、fgetc的区别,getc可被实现为宏,而fgetc不能实现为宏:

1)getc的参数不应当是具有副作用的表达式,因为它可能会被计算多次

2)因为fgetc一定是个函数,所以可以得到其地址。这允许将fgetc的地址作为一个参数传递给另一个函数。

3)调用fgetc所需的时间可能比调用getc要长,因为调用函数所需的时间通常长于调用宏。

在大多数实现中,FILE对象中维护了2个标志:

1)出错标志

2)文件结束标志

调用clearerr可以清除这2个标志。

从流中读取数据以后,可以调用ungetc将字符再押送回流中。

不能回送EOF.但是当已经到达文件尾端时,仍可以回送一个字符。下次读将返回该字符,再读则返回EOF.原因是一次成功的ungetc调用会清除该流的文件结束标志。

注:用ungetc压送回字符时,并没有将它们写到底层文件或设备上,只是将它们写回标准I/O库的流缓冲区中。

当正在读一个输入流,并进行某种形式的切词或记号切分操作时,会经常用到回送字符操作。有时需要先看一看下一个字符,以决定如何处理当前字符。然后就需要方便地将刚查看的字符回送,以便下一次调用getc时返回该字符。

如果标准I/O库不提供回送能力,就需将该字符存放到一个我们自己的变量中,并设置一个标志以便下次需要一个字符时时调用getc,还是从自己的变量中取用这个字符。

5.2、输出函数

putc、fputc、putchar

与输入函数一样,putchar(c)等同于putc(c,stdout),putc可被实现为宏,而fputc不能实现为宏。

6、每次一行I/O

fgets、gets

gets从标准输入读,fgets从指定流读。gets不将换行符存入缓冲区,fgets将换行符存入缓冲区。

gets是一个不推荐使用的函数。其问题是不能指定缓冲区的长度,这样就可能造成缓冲区溢出。

fputs、puts

puts会输出换行符到标准输出。

尽量避免使用puts,以免需要记住它最后是否添加了一个换行符。如果总是使用fgets和fputs,那么就会熟知在每行终止处必须自己处理换行符。

7、二进制I/O

对于读,如果出错或达到文件尾端,返回值可能小于预期值。在这种情况下,应调用ferror或feof以判断究竟是哪一种情况。对于写,如果返回小于所要求的数量,则出错。

使用二进制I/O的基本问题是,它只能用于读在同一系统上已写的数据。

2.3、fread()函数

对文件进行读操作。

#include <stdio.h>
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

参数 ptr: 将读取到的数据存放在参数 ptr 指向的缓冲区中;
参数 size:从文件读取 nmemb 个数据项,每一个数据项的大小为 size 个字节,所以总共读取的数据大小为 nmemb * size 个字节。
参数 nmemb: 参数 nmemb 指定了读取数据项的个数。
参数 stream: FILE 指针。
返回值: 调用成功时返回读取到的数据项的数目;如果发生错误或到达文件末尾,则 fread()返回的值将小于参数 nmemb。

注:fread()返回值不能区分文件结尾和错误, 究竟是哪一种情况,此时可以使用 ferror()或 feof()函数来判断。

2.4、fwrite()函数

对文件进行写操作。

#include <stdio.h>
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

参数 ptr: 将参数 ptr 指向的缓冲区中的数据写入到文件中。
参数 size: 参数 size 指定了每个数据项的字节大小。
参数 nmemb: 参数 nmemb 指定了写入的数据项个数。
参数 stream: FILE 指针。
返回值: 调用成功时返回读取到的数据项的数目;如果发生错误,则 fwrite()返回的值将小于参数 nmemb。

8、定位流

ftell、fseek、rewind\ftello\fseeko

除了偏移量off_t而非long以外,ftello函数与ftell相同,fseeko函数与fseek相同。

rewind

可将一个流设置到文件的起始位置

fgetops、fsetpos

fgetops将文件位置指示器的当前值存入由pos指向的对象中。在以后调用fsetpos时,可以使用此值将流重新定位至该位置。

注:需要移植到非UNIX系统上运行的应用程序应当使用fgetops和fsetpos

9、格式化I/O

10.1、printf()函数

格式化输出到stdin。

#include <stdio.h>

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

参数 format:格式

返回值:一旦成功返回,将返回打印的字符数(不包括用于结束字符串输出的空字节)。  

10.2、fprintf()函数

格式化输出到文件。

#include <stdio.h>

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

参数 stream:FILE指针

参数 format:格式

返回值:一旦成功返回,将返回打印的字符数(不包括用于结束字符串输出的空字节)。  

10.3、dprintf()函数

格式化输出到文件描述符。

#include <stdio.h>

int dprintf(int fd, const char *format, ...);

参数 fd:文件描述符

参数 format:格式

返回值:一旦成功返回,将返回打印的字符数(不包括用于结束字符串输出的空字节)。

10.2、sprintf()函数

格式化输出到缓冲区。

#include <stdio.h>

int sprintf(char *str, const char *format, ...);

参数 format:格式

返回值:一旦成功返回,将返回打印的字符数(不包括用于结束字符串输出的空字节)。

注:函数会在字符串尾端自动加上一个字符串终止字符'\0'。 

10.3、snprintf()函数

格式化输出到缓冲区(最多size字节)。

#include <stdio.h>

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

参数 str:缓存区

参数 size:最大字节

参数 format:格式

返回值:一旦成功返回,将返回打印的字符数(不包括用于结束字符串输出的空字节)。

注:函数会在字符串尾端自动加上一个字符串终止字符'\0'。 

下列5种printf族的变态类似上面的5种,但是可变参数表(...)替换成了arg。

格式化输入

scanf族用于分析输入字符串,并将字符序列转换成指定类型的变量。

与printf族相同,scanf族也使用由<stdarg.h>说明的可变长度参数表。

  • 0
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值