unix环境高级编程(标准I/O库)笔记

流和FILE对象

  • 文件I/O函数是围绕文件描述符的,而标准I/O库是围绕流进行的。使用标准I/O库打开或者创建一个文件时,我们已使一个流和一个文件关联。标准I/O库是ISO C的标准,在很多操作系统上面都实现。Unix文件I/O函数都是针对文件描述符的。
  • 对于字符集,有的字符集(ASCII)是一个字符对应一个字节,而有的字符集一个字符对应多个字节。
  • 标准I/O预定义三个文件指针stdin、stdout和stderr(头文件stdio.h中),之前章节的文件I/O中定义的三个文件描述符和这里的对应。
  • 标准I/O库是为了减少对文件I/O中的read和write的调用,使用了缓冲,其中进行了两次复制:第一次是在内核和标准I/O缓冲之间(调用read和write),第二次是在标准I/O缓冲区和用户程序中的行缓冲区之间。
  • 流的定向决定了所读、写的字符是单还是多字节的。个流最初被创建时,它并没有被定向,如果在其上使用一个多字节IO函数(头文件wchar.h中的),则将该流的定向设置为宽定向的;如果在未定向的流上使用一个单字节IO函数,则将流的定向设为字节定向的。
#include <stdio.h>
#include <wchar.h>
// mode为正值设置为宽。为负设置为字节。0不设置定向,返回定向。
int fwide(FILE *fp, int mode);

fwide函数可用于设置流的定向。返回值:若流是宽定向的,返回正值;若流是字节定向的,返回负值;若流是未定向的,返回0。此函数并不能改变已经设置了定向的流。

缓冲

缓冲的目的就是前面所说的减少系统调用

  • 全缓冲:在这种情况下,在填满标准I/O缓冲区后才进行实际的I/O操作。
  • 行缓冲:在这种情况下,当在输入和输出中遇到换行符时,标准I/O库执行I/O操作。
  • 不带缓冲:标准I/O库不对字符进行缓冲存储。

很多系统默认使用下列类型的缓冲:

  1. 标准错误是不带缓冲的。
  2. 若是指向终端设备的流,则是行缓冲的;否则是全缓冲的。
#include <stdio.h>
void setbuf(FILE *restrict fp, char *restrict buf);
int setvbuf(FILE *restrict fp, char *restrict buf, int mode, size_t size);

更改缓冲类型。返回值:若成功,返回0;若出错,返回非0。

  • 可以使用setbuf 函数打开或关闭缓冲机制。为了带缓冲进行 I/O,参数buf必须指向一个长度为BUFSIZ的缓冲区(该常量定义在中)。通常在此之后该流就是全缓冲的,但是如果该流与一个终端设备相关,那么某些系统也可将其设置为行缓冲的。为了关闭缓冲,将buf设置为NULL。
  • setbuf()和setvbuf()函数的实际意义在于:用户打开一个文件后,可以建立自己的文件缓冲区,而不必使用fopen()函数打开文件时设定的默认缓冲区。这样就可以让用户自己来控制缓冲区,包括改变缓冲区大小、定时刷新缓冲区、改变缓冲区类型、删除流中默认的缓冲区、为不带缓冲区的流开辟缓冲区等。
  • 使用setvbuf,我们可以精确地说明所需的缓冲类型。这是用mode参数实现的:
    _IOFBF 全缓冲
    _IOLBF 行缓冲
    _IONBF 不带缓冲
    在这里插入图片描述
#include<stdio.h>
int fflush(FILE *fp); 

返回值:若成功,返回0;若出错,返回EOF
此函数使该流所有未写的数据都被传送至内核。作为一种特殊情形,如若fp是NULL,则此函数将导致所有输出流被冲洗

打开流

#include <stdio.h>
// 打开一个指定的文件
FILE *fopen(const char *restrict pathname, const char *restrict type);
// 在一个指定的流上打开一个指定的文件
FILE *freopen(const char *restrict pathname, const char *restrict type, FILE *restrict fp);
// 获取一个现有的文件描述符,使得一个I/O流与该描述符先结合,常用于由创建管道和网络通信通道函数返回的描述符。
FILE *fdopen(int fd, const char *type);
// 3个函数的返回值:若成功,返回文件指针;若出错,返回NULL

在这里插入图片描述
以读和写类型打开文件时(type参数中+号),有限制:
1.如果中间没有fflush、fseek、fsetpos、rewind函数,输出后面不能跟随输入。
2.如果中间没有fseek、fsetpos、rewind函数,或一个输入操作没有到达文件尾端,则输入后不能跟输出。
在这里插入图片描述

#include <stdio.h>
// 调用fclose关闭一个打开的流。返回值:若成功,返回0;若出错,返回EOF
int fclose(FILE *fp); 

该文件被关闭前,冲洗缓冲中的输出数据,缓冲区中任何输入数据被丢弃,如果标准IO库已经为该流自动分配了缓冲区,则释放此缓冲区。

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

读和写流

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

  • 每次一个字符的I/O。一次读或写一个字符,如果流是带缓冲的,则标准I/O函数处理所有缓冲。
  • 每次一行的I/O。如果想要一次读或写一行,则使用fgets和fputs。每行都以一个换行符终止。当调用fgets时,应说明能处理的最大行长。
  • 直接 I/O。fread和fwrite函数支持这种类型的I/O。每次 I/O操作读或写某种数量的对象,而每个对象具有指定的长度。这两个函数常用于从二进制文件中每次读或写一个结构。
#include <stdio.h>
int getc(FILE *fp);
int fgetc(FILE *fp);
int getchar(void);
// 用于一次读一个字符。3个函数的返回值:若成功,返回下一个字符;若已到达文件尾端或出错,返回EOF

不管是出错还是到达文件尾端,这3个函数都返回同样的值。为了区分这两种不同的情况,必须调用ferror或feof

#include <stdio.h>
int ferror(FILE *fp);
int feof(FILE *fp);
// 返回值:若条件为真,返回非0(真);否则,返回0(假)

void clearerr(FILE *fp);

在大多数实现中,为每个流在FILE对象中维护了两个标志:

  • 出错标志
  • 文件结束标志

clearerr可以清除这两个标志

#include <stdio.h>
// 从流中读取数据以后,可以调用ungetc将字符再压送回流中。返回值:若成功,返回c;若出错,返回EOF
int ungetc(int c, FILE *fp); 

使用ungetc回送的字符是写入到缓冲区,并没有立马写到文件磁盘中。

#include <stdio.h>
int putc(int c, FILE *fp);
int fputc(int c, FILE *fp);
int putchar(int c);

3个函数返回值:若成功,返回c;若出错,返回EOF。
对应于上面所述的每个输入函数都有一个输出函数。

每次一行IO

#include <stdio.h>
char *fgets(char *restrict buf, int n,FILE *restrict fp);
char *gets(char *buf);

int fputs(const char *restrict str, FILE *restrict fp);
int puts(const char *str); 

返回值:若成功,返回buf;若已到达文件尾端或出错,返回NULL

二进制IO

前面的读写操作是以字符或行,遇到null或换行就会停止,对一个结构进行写入到文件时,还需要使用循环,所以提供一下二进制操作。

#include <stdio.h>
size_t fread(void *restrict ptr, size_t size, size_t nobj, FILE *restrict fp);
size_t fwrite(const void *restrict ptr, size_t size, size_t nobj, FILE *restrict fp);
// 两个函数都是返回读写对象的数量

定位流

有三种方法定位标准流

  • ftell 和fseek 函数,它们都假定文件的位置可以存放在一个长整型中。
  • ftello和fseeko函数,使文件偏移量可以不必一定使用长整型。它们使用off_t数据类型代替了长整型。
  • fgetpos和fsetpos函数,这两个函数是由ISO C引入的。它们使用一个抽象数据类型fpos_t记录文件的位置。这种数据类型可以根据需要定义为一个足够大的数,用以记录文件位置。(需要移植到非UNIX系统上运行的应用程序应当使用fgetpos和fsetpos。)
#include <stdio.h>
long ftell(FILE *fp);
int fseek(FILE *fp, long offset, int whence);
void rewind(FILE *fp);
off_t ftello(FILE *fp);
int fseeko(FILE *fp, off_t offset, int whence);
int fgetpos(FILE *restrict fp, fpos_t *restrict pos);
int fsetpos(FILE *fp, const fpos_t *pos); 

whence与lseek的参数一样。
ftell返回值:若成功,返回当前文件位置指示;若出错,返回-1L
fseek返回值:若成功,返回0;若出错,返回−1
rewind函数可将一个流设置到文件的起始位置。

格式化IO

#include <stdio.h>
int printf(const char *restrict format, ...);
int fprintf(FILE *restrict fp, const char *restrict format, ...);
int dprintf(int fd, const char *restrict format, ...);
int sprintf(char *restrict buf, const char *restrict format, ...);
int snprintf(char *restrict buf, size_t n, const char *restrict format, ...);
  • printf(标准输出)、fprintf、dprintf返回值:若成功,返回输出字符数;若输出出错,返回负值。

  • sprintf返回值:若成功,返回存入数组的字符数;若编码出错,返回负值

  • snprintf返回值:若缓冲区足够大,返回将要存入数组的字符数;若编码出错,返回负值,会追加一个null,返回值不包括null
    格式化字符串:

     %[flags][fldwidth][precision][lenmodifier]convtype
    

    在这里插入图片描述

  • fldwidth说明最小字段宽度。转换后参数字符数若小于宽度,则多余字符位置用空格填充。字段宽度是一个非负十进制数,或是一个星号(*)。

  • precision 说明整型转换后最少输出数字位数、浮点数转换后小数点后的最少位数、字符串转换后最大字节数。精度是一个点(.),其后跟随一个可选的非负十进制数或一个星号(*)。

  • lenmodifier说明参数长度

在这里插入图片描述

  • convtype不是可选的。它控制如何解释参数

    在这里插入图片描述
#include <stdio.h>
int scanf(const char *restrict format, ...);
int fscanf(FILE *restrict fp, const char *restrict format, ...);
int sscanf(const char *restrict buf, const char *restrict format, ...);
// 返回值:赋值的输入项数;若输入出错或在任一转换前已到达文件尾端,返回EOF

格式字符串和上面基本一致。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值