1.流和FILE对象
  对于国际字符集,一个字符可以由一个以上的字节来表示。标准I/O文件流可以用来操作单字节和多字节(宽,wide)字符集。一个流的方向(orientation)决定了字符是以单字节还是多字节的方式读取,当一个流被创建时,它没有方向。如一个多字节I/O函数(见<wchar.h>)用在了没有方向的流上,那么流的方向会设为面向宽字符的。如果一个字节I/O函数用在一个没有方向的流上,那么流的方向会设为面向字节的。只有两个函数可以在设置后改变这个方向。freopen函数(简单说明)将会清除一个流的方向,而 fwide函数用来设置一个流的方向。

#include <stdio.h>
#include <wchar.h>
int fwide(FILE *fp, int mode);
//流面向宽字符返回正值、面向字节返回负值、没有方向则返回0。

根据mode参数的不同值,fwdie函数执行不同的任务:
1).如果mode参数为负值,fwide将会尝试让流面向字节;
2).如果mode参数为正值,fwide将会尝试让流面向宽字符;
3).如果mode参数为0,fwid将不会设置方向,而是返回一个表示流的方向的值。
注意fwide不会改变一个已经有方向的流的方向。同样注意它不会返回错误。
当打开一个流时,标准I/O函数fopen返回一个指向一个FILE对象的指针。这个对象通常是一个包含所有标准I/O库管理流所需的所有信息的结构体。

2.缓冲
三种缓冲类型:
1).全缓冲:在这种情况下,当标准I/O的缓冲被填满后,真实的I/O才会发生。在磁盘上的文件通常都被标准I/O库完全缓冲。这个被使用的缓冲通常被标准I/O库在第一次操作一个流时通过调用malloc得到.注:Stdin和stdout如果被重定位到文件上,则是完全缓冲
术语“冲洗(flush)”描述了标准I/O缓冲的写操作。一个缓冲可以被标准I/O函数自动冲洗,比如在缓冲满时,或者我们可以调用函数fflush来冲洗一个流。不幸的是,在UNIX环境里,冲洗有两个意思。在标准I/O库里,它表示缓冲的内容写出,它可能是部分填充的。

2).行缓冲:在这种情况下,标准I/O库在输入输出时碰到一个换行符时会执行I/O操作。这让我们可以一次输出单个字符(使用标准I/O的 fputc函数),因为我们知道只当我们完成了每行的写操作时真实I/O才会发生。行缓冲被典型用在终端的流上:比如标准输入、标准输出。
行缓冲伴随两个警告。第一,标准I/O库用来收集各行的缓冲区大小是固定的,所以当我们在写一个换行符之前填满这个缓冲I/O可能会发生。第二、不管何时通过标准I/O库从a、一个未缓冲流或b、(需要从内核被请求数据的)一个行缓冲流请求一个输入时,所有行缓冲输出流都会被冲洗。b情况括号内的补充条件的原因是,请求的数据可能已经在缓冲里了,这时不再需要从内核中读取数据。显然,a情况下任何从未缓冲流的输入,都会要求从内核获取数据。

3).无缓冲:标准I/O库不缓冲字符。例如,如果我们用标准I/O函数fputs写15个字符,我们会期望这15个字符尽快地输出,它很可能使用了3.8节的write函数。
标准错误流通常是无缓冲的。这是为了使任何错误消息都能尽快地显示,而不管它们是否包含一个换行符。
通过调用下面两个函数的其中一个来改变缓冲:

#include <stdio.h>
void setbuf(FILE *restrict fp, char *restrict buf);
void setvbuf(FILE *restrict fp, char *restrict buf, int mode, size_t size);
//成功返回0,失败返回非0值。

3.打开流
1).下面三个函数打开一个标准I/O流

#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);
FILE *fdopen(int filedes, const char *type);
//成功返回文件指针,错误返回NULL。

区别:

a.fopen函数打开一个指定的文件。
b.freopen函数在一个指定的流上打开一个指定的文件,并关闭之前打开的流。如果之前的流有方向,freopen会清除它.
c.fdopen函数接受一个已有的文件描述符,它可以通过open、dup、dup2、fcntl、pipe、socket、socketpair或 accept函数得到

2).一个打开的流可以调用fclose来关闭

#include <stdio.h>
int fclose(FILE *fp);
//成功返回0,错误返回EOF

任何缓冲的输出数据都在文件关闭前被冲洗。任何可能被缓冲的输入数据都会被舍弃。如果标准I/O库之前自动开辟了一个缓冲,这个缓冲会被释放

4.读和写流
打开一个流,可以从以下三种非格式化的I/O中选择:
● 一次一字符(Character-at-a-time)I/O。我们可以一次读写一个字符,而让标准I/O函数处理所有的缓冲事宜,如果流有缓冲的话。
● 一次一行(Line-at-a-time)I/O。如果我们读一次读写一行,我们使用fgets和fputs。每行都以一个换行符终止,而且当我们调用fgets时必须指定我们能处理的最大行长度。
● 直接(Direct)I/O。这种类型的I/O通过fread和fwrite函数支持。对于每个I/O操作,我们读写一些对象,而每个对象都有一个指定的尺寸。这两个函数经常用来处理二进制文件,那里我们每次操作读写一个结构。
1).输入函数:
下面三个函数允许我们一次读一个字符

#include <stdio.h>
int getc(FILE *fp);
int fgetc(FILE *fp);
int getchar(void);
//三者成功都返回下一个字符,失败都返回EOF。

函数getchar被定义为与getc(stdin)等价。前两个函数的区别是getc可以被作为一个宏而实现,而fgetc不能作为宏实现

2).注意上面三个函数当碰到一个错误或到达文件尾时返回同一个值。为了区别这两者,我们必须调用ferror或feof

#include <stdio.h>
int ferror(FILE *fp);
int feof(FILE *fp);
//两者如果条件成立的话返回非0(真),否则返回0(假)。
void clearerr(FILE *fp);

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

a.一个错误标志
b.一个文件结束标志
两个标志都可以调用clearerr来清除。

3).在从一个流读入后,我们可以调用ungetc来把字符放回去。

#include <stdio.h>
int ungetc(int c, FILE *fp);
//成功返回c,否则返回EOF。

放回的字符会被后续在流上的读操作以放回的相反顺序返回

4).输出函数
对应于每个之前讨论过的输入函数的输出输出有:

#include <stdio.h>
int putc(int c, FILE *fp);
int fputc(int c, FILE *fp);
int putchar(int c);
//三个函数成功都返回c,否则返回EOF。

和输入函数相似,putchar(c)和putc(c, stdout)等价,而putc可以作为一个宏实现(将被替换为IO_putc),但fputc不能作为一个宏实现

5.每次一行I/O
1).以下两个函数提供一次一行输入功能

#include <stdio.h>
char *fgets(char *restrict buf, int n, FILE* restrict fp);
char *gets(char *buf);
//两者成功返回buf,文件结束或错误返回NULL。

两都都指明一个把行读入的缓冲区的地址。gets函数从标准输入中读,而fgets从指定的流中读取。

2).一次一行输出由fputs和puts提供

#include <stdio.h>
int fputs(const char *restrict str, FILE *restrict fp);
int puts(const char *str);
//成功返回非负值,否则返回EOF。

函数fputs把一个null终止的字符串写入到指定的流中,末尾的null字节没有被写,puts函数在标准输出写一个空字符终止的字符串,但不会把这个空字节写出。不同的是puts接着会把一个换行符写入到标准输出

6.二进制I/O
以下两个函数被用来执行二进制I/O

#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);
//两者都返回读或写的对象数。

这些函数有两种普遍的使用方法:

1)读写一个二进制数组。
2)读写一个结构体。

7.定位流
有三种方式来定位一个标准I/O流
1)

#include <stdio.h>
long ftell(FILE *fp);
//如果成功返回当前文件位置指示器,错误返回-1L。
int fseek(FILE *fp, long offset, int whence);
//成功返回0,否则返回非0值。
void rewind(FILE *fp);

对于一个二进制文件,一个文件的位置指示器是从文件开始的字节来衡量的.

a.ftell用于二进制文件,返回值是这个字节位置。
b.为了用fseek定位一个二进制文件,我们必须指定一个字节偏移以及这个偏移如何被解释。whence的值和前面的lseek函数里的一样:SEEK_SET表示从文件开头开始;SEEK_CUR表示从当前文件位置开始;SEEK_END表示从文件末尾开始。
c.使用rewind函数也可将一个流设置到文件起始位置

2).ftello函数和ftell函数相同,而fseeko函数和fseek函数相同,除了偏移量的类型是off_t而不是long

#include <stdio.h>
off_t ftello(FILE* fp);
//成功返回当前文件位置,否则返回(off_t)-1。
int fseeko(File *fp, off_t offset, int whence);
//成功返回0,失败返回非0.

3).想移植到非UNIX系统上运行应用程序应该使用fgetpos和fsetpos

#inlcude <stdio.h>
int fgetpos(FILE *restrict fp, fpos_t *restrict pos);
int fsetpos(FILE *fp, const fpos_t *pos);
//两者成功都返回0,失败返回非0值。

fgetpos返回把文件位置指示器的值存储在由pos指向的对象里。这个值可以被之后的fsetpos调用使用,来重定位流的位置。

8.格式化I/O
1).格式化输出
格式化输出由4个printf函数处理:

#include <stdio.h>
int printf(const char *restrict format, ...);
int fprintf(FILE *restrict fp, 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向一个指定的流写,而sprintf把格式化的字符串放入到数组buf里。sprintf函数在数组末自动将上一个空字节,但这个空字节没有包含在返回值里。注意sprintf可能为溢出buf指向的缓冲区,这时调用者的执行来保证这个缓冲区足够大。

下面4个printf家族的变体与前4个函数相似,只是参数列表(...)被arg代替。

#include <stdarg.h>
#include <stdio.h>
int vprintf(const char *restrict format, va_list arg);
int vfprintf(FILE *restrict fp, const char *restrict format, va_list arg);
//两者成功都返回输出的字符数,失败返回负值
int vsprintf(char *restrict buf, const char *restrict format, va_list arg);
int vsnprintf(char *restrict buf, size_t n, const char *restrict format, va_list arg);
//两都成功都返回数组存储的字符数,编码错误返回负数。

2).格式化输入

格式化输入由三个scanf函数处理

#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。

scanf家族用来解析一个输入的字符串并转换成指定类型的变量。在格式后的参数包含需要用转换结果初绐化的变量的地址。

和printf家族一样,scanf家族也支持使用可变参数列表的函数,它们由<stdarg.h>指定。

#include <stdarg.h>
#include <stdio.h>
int vscanf(const char *restrict format, va_list arg);
int vfscanf(FILE *restrict fp, const char *restrict format, va_list arg);
int vsscanf(const char *restrict buf, const char *restrict format, va_list arg);
//三者都返回赋好值的输入项的数量,错误或转换前遇到文件结尾时返回EOF

9.实现细节
可以调用fileno来得到流的文件描述符

#include <stdio.h>
int fileno(FILE *fp);
//返回相关联的文件描述符。

10.临时文件

ISO C标准定义了两个由标准I/O库提供的函数,用来协助创建临时文件

#include <stdio.h>
char *tmpnam(char *ptr);
//返回指向唯一的路径名的指针。
FILE *tmpfile(void);
//成功返回文件指针,失败返回NULL

tmpname函数返回一个不与任何已有文件相同的合法路径名的字符串

系统编程--标准IO的更多相关文章

  1. linux系统编程--文件IO

    系统调用 什么是系统调用: 由操作系统实现并提供给外部应用程序的编程接口.(Application Programming Interface,API).是应用程序同系统之间数据交互的桥梁. C标准函 ...

  2. Linux系统编程--文件IO操作

    Linux思想即,Linux系统下一切皆文件. 一.对文件操作的几个函数 1.打开文件open函数 int open(const char *path, int oflags); int open(c ...

  3. Linux系统编程@终端IO

    Linux系统中终端设备种类  终端是一种字符型设备,有多种类型,通常使用tty 来简称各种类型的终端设备.终端特殊设备文件一般有以下几种: 串行端口终端(/dev/ttySn ) ,伪终端(/dev ...

  4. 系统编程--文件IO

    1.文件描述符 文件描述符是一个非负整数,当打开一个现有文件或创建一个新文件时候,内核向进程返回一个文件描述符,新打开文件返回文件描述符表中未使用的最小文件描述符.Unix系统shell使用文件描述符 ...

  5. linux系统编程:IO读写过程的原子性操作实验

    所谓原子性操作指的是:内核保证某系统调用中的所有步骤(操作)作为独立操作而一次性加以执行,其间不会被其他进程或线程所中断. 举个通俗点的例子:你和女朋友OOXX的时候,突然来了个电话,势必会打断你们高 ...

  6. UNIX环境高级编程 标准IO库

    标准I/O库处理很多细节,使得便于用户使用. 流和 FILE 对象 对于标准I/O库,操作是围绕 流(stream)进行的.当用标准I/O打开或创建一个文件时,我们已使一个流与一个文件相关联. 对于A ...

  7. 系统编程--高级IO

    1.非阻塞I/O 非阻塞I/O使我们可以调用不会永远阻塞的I/O操作,例如open,read和write.如果这种操作不能完成,则立即出错返回,表示该操作如继续执行将继续阻塞下去.对于一个给定的描述符 ...

  8. [APUE]标准IO库(下)

    一.标准IO的效率 对比以下四个程序的用户CPU.系统CPU与时钟时间对比 程序1:系统IO 程序2:标准IO getc版本 程序3:标准IO fgets版本 结果: [注:该表截取自APUE,上表中 ...

  9. 【linux草鞋应用编程系列】_1_ 开篇_系统调用IO接口与标准IO接口

    最近学习linux系统下的应用编程,参考书籍是那本称为神书的<Unix环境高级编程>,个人感觉神书不是写给草鞋看的,而是 写给大神看的,如果没有一定的基础那么看这本书可能会感到有些头重脚轻 ...

随机推荐

  1. 2018.9.5 Java中使用栈来模拟队列

    栈的规律是是先进后出 队列的规律是先进先出 栈模拟队列 首先我们定义两个栈,一个放数据,一个出数据,判断B栈是否有元素,有元素则直接pop:没有元素则需要我们将A里面的元素出栈然后放到B里面,再取出, ...

  2. python 删除空白

    Python能够找出字符串开头和末尾多余的空白.要确保字符串末尾没有空白,可使用方法rstrip() . >>> favorite_language = 'python ' > ...

  3. Spring boot 集成三种拦截方式

    三种拦截方式分别为: javax.servlet.Filter org.springframework.web.servlet.HandlerInterceptor org.aspectj.lang. ...

  4. Better exception message for missing @RequestBody method parameter

    https://jira.spring.io/browse/SPR-12888 Description When I use @RequestBody on one of my controllers ...

  5. mysql中locate和substring函数使用

    locate函数使用 LOCATE(substr,str) 1.如果子串 substr 在 str 中不存在,返回值为 0: 2.如果子串 substr 在 str 中存在,返回该字符串第一次出现的位 ...

  6. 6、SpringBoot+Mybatis整合------参数传递

    开发工具:STS 代码下载链接:https://github.com/theIndoorTrain/SpringBoot_Mybatis/tree/7892801d804d2060774f3720f8 ...

  7. Java连接数据库的一个问题

    问题描述: 利用HTML+servlet+MySQL写一个简单的登录注册案例,抛出了异常No suitable driver found for jdbc 解决方法 将mysql-connector- ...

  8. Linux下 tar 命令详解

    tar 是 unix/linux下的打包器 [解压] 输入命令: # tar  -zxvf  filename.tar.gz 参数解释: z :表示 tar 包是被 gzip 压缩过的 (后缀是.ta ...

  9. 简版会员私信表设计及sql 私信列表查询

    先上下表结构和数据 DROP TABLE IF EXISTS `message`; CREATE TABLE `message` ( `id` int(11) NOT NULL AUTO_INCREM ...

  10. Oracle客户端安装配置

    Oracle客户端安装配置.绿色版Oracle客户端配置使用PLSQL 安装Oracle客户端 准备工作,首先下载对应Oracle版本的客户端,这个可以去Oracle官网下载,加压完目录如下: 点击s ...