《UNIX环境高级编程》--5 标准IO库

来源:本网整理

建议不要一开始看这本书。可以先看其他简单的,这本书统筹了所有流行的unix实现。复杂繁琐,适合高级可

标准IO库

流和 FILE对象

  1. 标准IO库与文件IO区别:

    说说你对unix系统、C语言的了解程度,才能判定。我也在学这本书,参考下面指标学习:至少学习

    • 标准IO库处理很多细节,如缓冲区分片、以优化的块长度执行IO等。
    • 文件IO函数都是围绕文件描述符进行。首先打开一个文件,返回一个文件描述符;后续的文件IO操作都使用该文件描述符
    • 标准IO库是围绕流进行的。当用标准IO库打开或者创建一个文件时,就有一个内建的流与之相关联

        Stevens著有三大本:UNIX环境高级编程、TCP/IP详解、UNIX网络编程。  我是

    标准IO库的函数很多都是以 f开头,如fopenfclose

  2. 如果学过了C,看《Linux程序设计》应该是没有问题的,只是这本书并不只是讲C语言的知识,它讲了许多

    对于ASCII字符集,一个字符用一个字节表示;对于国际字符集,一个字符可以用多个字节表示。

    • 标准IO文件流可用于单字节或者多字节字符集。流的定向决定了所处理的字符是单字节还是多字节的。
    • 当一个流最初被创建时,它并没有定向。

      1 你说没有反应我不太明白,但是2那里走不到应该是正常的,waitpid 等待子进程结束,也就是应该

      • 若在未定向的流上使用一个多字节IO函数,则将该流的定向设置为宽定向的(即处理多字节)
      • 若在未定向的流上使用一个单字节IO函数,则将该流的定向设置为字节定向的(即处理单字节)
    • 只有两个函数可以改变流的定向

      票房,原本只是一个数字,但是这样的数字,令老梁产生一个念头:《战狼2》应该立即下线!也许有很多人会很不理解,说好好的一部电影,势头正劲,而且为中国的电影争了光,进入了好莱坞垄断数十年的全球电影票房,目前已经挺进到了第80位,应该继续为《战狼2》点赞才对,为何会有如此的恶毒念头儿?在这里老梁想说的是:全球电影票房的排名固然重要,也需要中国这样一部电影能够争得一席之地,我们也已经实现了。中国也确实一部电影,在世界上扬眉吐气。但是,这样的成功,只属于《战狼2》,并不属于中国电影。《战狼2》爬得越高,未来对于中国电影来说就越是沉重的打击!我们不仅仅需要一部电影《战狼2》,我们需要更多。我们也不仅仅只需

      • freopen函数清除一个流的定向
      • fwide函数设置流的定向
  3. 射程最远的应该是瘟疫之源(老鼠),850码力压群雄!TOP1.瘟疫之源850码(550+300)老鼠的初始射程尽管只有550,这在ADC中只能算是中等,但当老鼠6级升级大招之后,那就是质的飞跃,大招给老鼠增加了整整300的射程,达到850!要知道防御塔的射程才825,老鼠开大可以不受攻击的点塔,太可怕了。而且deft去年给我们展示了以眼作为攻击跳板用溅射来提供伤害,让人叹为观止。目前飓风流老鼠盛行于召唤师峡谷之中,一旦老鼠发育良好,团战起来真的是毁天灭地呀,第一射程当之无愧!TOP2、大嘴大嘴是开技能射程第二远的英雄,大嘴5级W之后可以拥有710的射程,更有附加的百分比伤害,是一代肉盾杀手,所

    fwide函数:设置流的定向

    整体状况应该不太好。先说下周立波的基本情况吧。周立波1967年出生,是一个非常有名气的主持人和演员,他也是海派清口的创始人,海派清口是一种脱口秀的表现形式,带有浓重的表现色彩,把自己的观点,热点,社会关注的店全都结合在一起,表达出来。周立波曾经还创作了《笑侃三十年》和《笑侃大上海》,2009年,还与凤凰卫视一起,创办了脱口秀节目《壹周立波秀》。还曾经入职浙江卫视担任创意总监并担任《中国梦想秀》节目“梦想大使”。可以说非常受人欢迎,而且自身条件非常好的一个主持人和演员。但是正是这样一个主持人和演员。2017年驾车违规被拦截,因为携带枪支和毒品被警方逮捕,被控诉了5项罪名,分别是二级非法持有武器、

    #include<stdio.h> #include<wchar.h> int fwide(FILE *fp,int mode);

    细支烟虽然是市场的新贵,但是,提到细支烟,许多人都会认为,这款卷烟的主要消费群体限定于女性。其实,市场调查显示,细支烟的消费群体80%以上是男性,女性只占很小部分,虽然这与女性烟民相对较少有关系,但也侧面说明,男性烟民对细支烟并不排斥。特别是,一些高端品牌也纷纷推出细支烟,如“南京(细支九五)”,“苏烟(沉香)”、利群(休闲云端)、云烟(大重九细支)、黄金叶(天叶细支)等都达到了每条千元档,这可不是只用于玩味的女性客户所愿意消费的档次。最后,香烟也被称为精神口粮,并不像衣服一样对使用者有明显的界定,是属于男女皆宜的商品。一支细支卷烟其烟丝含量仅为正常卷烟量的一半甚至三分之一。同样一支细吸完,其

    • 参数:

      • fpFILE文件对象的指针
      • mode:流的定向模式。

        • 如果mode是负数,则函数试图使指定的流为字节定向(并不保证修改成功,因为fwide并不改变已定向流的定向)
        • 如果mode是正数,则函数试图使指定的流为宽定向的(并不保证修改成功,因为fwide并不改变已定向流的定向)
        • 如果mode为0,则函数不试图设置流的定向,而直接返回该流定向的值
    • 返回值:

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

    这里并没有函数失败的情况

  4. 注意:

    • fwide并不改变已定向流的定向。
    • 如果fp是无效流,由于fwide从返回值无法得知函数执行成功还是失败。那么我们必须采用这个方法:首先在调用fwide之前清除errno。然后在fwide之后检查errno的值。通过errno来检测fwide执行成功还是失败。
  5. FILE指针:当使用fopen函数打开一个流时,它返回一个执行FILE对象的指针。该对象通常是一个结构,包含了标准IO库为管理该流所需要的所有信息,包括:

    • 用于实际IO的文件描述符
    • 指向用于该流缓冲区的指针
    • 该流缓冲区的长度
    • 当前在缓冲区中的字符数
    • 出错标志

    应用程序没必要检验FILE对象,只需要将FILE指针作为参数传递给每个标准IO函数。

    FILE

  6. 操作系统对每个进程与定义了3个流,并且这3个流可以自动地被进程使用,他们都是定义在<stdio.h>中:

    • 标准输入:预定义的文件指针为stdin,它内部的文件描述符就是STDIN_FILENO
    • 标准输出:预定义的文件指针为stdout,它内部的文件描述符就是STDOUT_FILENO
    • 标准错误:预定义的文件指针为stderr,它内部的文件描述符就是STDERR_FILENO
  7. 标准IO库提供缓冲的目的是:尽量减少使用readwrite调用的次数。标准IO库对每个IO流自动地进行缓冲管理,从而避免了程序员需要手动管理这一点带来的麻烦。

    标准IO库提供了三种类型的缓冲:

    • 全缓冲:此时在标准IO缓冲区被填满后,标准IO库才进行实际的IO操作。
    • 行缓冲:此时当输入和输出中遇到换行符时,标准IO库执行实际的IO操作。但是注意:

      • 只要填满了缓冲区,即使还没有写一个换行符,也立即进行IO操作
      • 任何时候只要通过标准IO库,从一个不带缓冲的流或者一个行缓冲的流得到输入数据,则会冲洗所有行缓冲输出流。(即要缓冲输入,先冲洗输出缓冲)
    • 不带缓冲:标准IO库不对字符进行缓冲存储。此时任何IO都立即执行实际的IO操作。

    另外:

    • 在一个流上执行第一次IO操作时,相关标准的IO函数通常调用 malloc获取使用的缓冲区
    • 缓冲区可以由标准的IO操作自动地冲洗(如,当填满一个缓冲区时),也可以手动调用fflush函数冲洗一个流。
  8. ISO C 要求下来缓冲特征:

    • 当且仅当标准输入和标准输出并不指向交互式设备时,他们才是全缓冲的
    • 标准错误绝不会是全缓冲的。

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

    • 标准错误stderr时不带缓冲的
    • 标准输入stdin和输出stdout:若是指向终端设备的流,则是行缓冲的;否则是全缓冲的
  9. setbuf/setvbuf函数:设置流的缓冲类型

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

    • 参数:

      • fp:被打开的文件对象的指针
      • buf:一个缓冲区的指针。缓冲区长度必须为BUFSIZ常量(该常量定义在<stdio.h>中)。

        • 如果bufNULL,则是关闭缓冲
        • 如果bufNULL,则通常设定该流为全缓冲的。但若该流与一个设备终端相关,则设为行缓冲的

      对于setvbuf函数:

      • buf:一个缓冲区的指针。缓冲区长度为size

        • bufNULL,且mode_IONBF:则该流为不带缓冲的。因为此时忽略bufsize参数
        • bufNULL,且mode不是_IONBF:则标准IO库将自动为该流分片合适长度的缓冲区(即BUFSIZE长度),然后设定该流为指定的mode
      • mode:指定缓冲类型。可以为:

        • _IOFBF:全缓冲。
        • _IOLBF:行缓冲
        • _IONBF:不带缓冲。此时忽略bufsize参数
      • size:缓冲的长度
    • 返回值:

      • 成功: 返回0
      • 失败: 返回非0(并不是-1)

    注意:

    • 如果在一个函数内分配一个自动变量类型的标准IO缓冲区,则从该函数返回之前,必须关闭流。因此自动变量是栈上分配,函数返回之后自动变量被销毁
    • 某些操作系统将缓冲区的一部分存放它自己的管理操作信息,因此可以存放在缓冲区中的实际数据字节数将少于size
    • 通常推荐利用操作系统自动选择缓冲区长度并自动分配缓冲区。在这种情况下若关闭此流,则标准IO库会自动释放缓冲区
  10. fflush函数:手动冲洗一个流

    #include<stdio.h> int fflush(FILE *fp);

    • 参数:

      • fp:被打开的文件对象的指针
    • 返回值:

      • 成功:返回0
      • 失败:返回EOF (并不是-1)

    该函数会使得该流所有未写的数据都被传送至内核。当fpNULL时,此函数将导致所有输出流被冲洗。

    • 冲洗是双向的:输出流 —> 内核 —> 磁盘或者终端; 输入流—> 用户缓冲区
    • 冲洗并不是立即写到磁盘文件中。冲洗只是负责数据传到内核

打开关闭流

  1. fopen/freopen/fdopen函数:打开标准IO流

    #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 fd,const char*type);

    • 参数:

      • type:指定对该IO流的读写方式:

        • "r"或者"rb":为读打开
        • "w"或者"wb":写打开。若文件存在则把文件截断为0长;若文件不存在则创建然后写
        • "a"或者"ab":追加写打开;若文件存在每次都定位到文件末尾;若文件不存在则创建然后写
        • "r+"或者"r+b"或者"rb+":为读和写打开
        • "w+"或者"w+b"或者"wb+":若文件存在则文件截断为0然后读写;若文件不存在则创建然后读写
        • "a+"或者"a+b"或者"ab+":若文件存在则每次都定位到文件末尾然后读写;若文件不存在则创建然后读写

      • 其中b用于区分二进制文件和文本文件。但是由于UNIX内核并不区分这两种文件,所以在UNIX环境中指定b并没有什么卵用

      • 创建文件时,无法指定文件访问权限位。POSIX默认要求为:S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH

    对于 fopen函数:

    • pathname:待打开文件的路径名

      对于 freopen函数:

    • pathname:待打开文件的路径名
    • fp:在指定的流上打开文件。若fp已经打开,则先关闭该流;若fp已经定向,则清除该定向。

    对于 fdopen函数:

    • fd:打开文件的文件描述符
    • 对于fopentype意义稍微有点区别。因为该描述符已经被打开,所以fdopen为写而打开并不截断该文件。另外该文件既然被打开并返回一个文件描述符,则它一定存在。因此标准 IO追加写方式也不能创建文件
  2. 返回值:

    • 成功: 返回文件指针
    • 失败: 返回NULL
  3. 这几个函数的常见用途:

    • fopen常用于打开一个指定的文件,返回一个文件指针
    • freopen常用于将一个指定的文件打开为一个预定义的流(标准输入、标准输出或者标准错误)
    • fdopen常用于将文件描述符包装成一个标准IO流。因为某些特殊类型的文件(如管道、socket文件)不能用fopen打开,必须先获取文件描述符,然后对文件描述符调用fdopen

    注意:当以读和写类型打开一个文件时(type中带+号的类型),有下列限制:

    • 如果写操作后面没有fflush,fseek,fsetpos,rewind操作之一,则写操作后面不能紧跟读操作
    • 如果读操作后面没有fseek,fsetpos,rewind操作之一,也没有到达文件末尾,则在读操作之后不能紧跟写操作

    注意:按照系统默认,流被打开时是全缓冲的。但是如果流引用的是终端设备,则安装系统默认,流被打开时是行缓冲的。

  4. fclose:关闭一个打开的流

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

    • 参数:

      • fp:待关闭的文件指针
    • 返回值:

      • 成功: 返回 0
      • 失败: 返回 -1

    在该文件被关闭之前:

    • fclose会自动冲洗缓冲中的输出数据
    • 缓冲区中的输入数据被丢弃
    • 若该缓冲区是标准IO库自动分配的,则释放此缓冲区

    当一个进程正常终止时(直接调用exit函数,或者从main函数返回):

    • 所有带未写缓存数据的标准IO流都被冲洗
    • 所有打开的标准IO流都被关闭
  5. 示例

    #include <stdio.h> #include<string.h> #include<errno.h> #include<wchar.h> void print_FILE_wide(FILE* fp) { errno=0; int mode; mode=fwide(fp,0); if(errno!=0) { printf("\tprint_FILE_wide error,because %s\n",strerror(errno)); }else { if(mode>0) printf("\twide type is wide byte!\n"); else if(mode<0) printf("\twide type is byte!\n"); else printf("\twide type is undecided!\n"); } } void set_FILE_wide(FILE *fp,int mode) { errno=0; fwide(fp,mode); if(errno!=0) { printf("\tset_FILE_wide error,because %s\n",strerror(errno)); }else { printf("\tset_FILE_wide sucess\n"); } } void print_FILE(FILE *fp) { printf("\t\tfile descriptor is :%d\n",fp->_fileno); printf("\t\tbuffer address is :0x%x\n",fp->_IO_buf_base); printf("\t\tbuffer length is :%d\n",fp->_IO_buf_end-fp->_IO_buf_base); printf("\t\tFILE status is :0x%x\n",fp->_flags); } void print_stdin_out_err() { printf("\t stdin wide type:"); print_FILE_wide(stdin); printf("\t stdout wide type:"); print_FILE_wide(stdout); printf("\t stderr wide type:"); print_FILE_wide(stderr); printf("\tstdin FILE struct:"); print_FILE(stdin); printf("\tstdout FILE struct:"); print_FILE(stdout); printf("\tstderr FILE struct:"); print_FILE(stderr); } void set_FILE_buf(FILE *fp,int mode) { if(-1==setvbuf(fp,NULL,mode,0)) { printf("setvbuf failed,because %s!\n",strerror(errno)); }else { printf("setvbuf sucess!\n"); } } int main(int argc, char *argv[]) { FILE *fp; printf("Test stdin,stdout,stderr:\n"); print_stdin_out_err(); printf("Test a custom FILE:\n"); fp=fopen("/home/huaxz1986/test","wb"); if(fp==NULL) return; printf("\t wide type is:"); print_FILE_wide(fp); printf("\t expected set wide type to wide byte:"); set_FILE_wide(fp,1); printf("\t wide type is:"); print_FILE_wide(fp); printf("\t expected set wide type to byte:"); set_FILE_wide(fp,-1); printf("\t wide type is:"); print_FILE_wide(fp); printf("FILE struct is :"); print_FILE(fp); printf("set_FILE_buf to unbuffered:"); set_FILE_buf(fp,_IONBF); printf("FILE struct is :"); print_FILE(fp); fclose(fp); return 0; }

    FILE_struct

    可以看到:

    • 三个标准IO流的文件描述符依次为 0、1、2
    • 未被使用的流不会分配缓冲,因此stdinstderr的缓冲区地址是 NULL。刚被创建的流的缓冲区地址也是NULL
    • 未被使用的流是为定向的。因此stdinstderr是未定向的。刚被创建的流的也是未定向的
    • 对已经定向的流设置流向,并不会改变流的方向。但是也不报告失败。
    • 未分配缓冲区的流,与非缓冲流不是一个概念。非缓冲流是分配了缓冲区的,它的缓冲区长度为1。而未分配缓冲区的流,其缓冲区是无效待分配的。

读写流

  1. 一旦打开了流,可以在3中不同类型的非格式化IO中选择,对流进行读、写操作:

    • 每次一个字符的IO。一次读、写一个字符。若流是带缓冲的,则标准IO函数处理所有缓冲
    • 每次一行的IO。一次读、写一行。每一行都以一个换行符终止
    • 二进制IO。每次IO读、写某种数量的对象。

    格式化IO由printf族函数完成

  2. getc/fgetc/getchar函数:一次读一个字符:

    #include<stdio.h> int getc(FILE*fp); int fgetc(FILE*fp); int getchar(void);

    • 参数:

      • fp:打开的文件对象指针
    • 返回值:

      • 成功:则返回下一个字符
      • 到达文件尾端:返回EOF
      • 失败:返回EOF

    注意:

    • getchar()等价于getc(stdin)。它从标准输入中读取一个字符
    • getcfgetc的区别在于:getc可能通过宏定义来实现,而fgetc不能实现为宏。因此:

      • getc的参数不应该是具有副作用的表达式,因为它可能被计算多次
      • fgetc可以得到其地址,这就允许将fgetc的地址作为参数传递。而getc不行
      • 调用fgetc所需的时间可能比调用getc长,因为函数调用所需时间通常比调用宏长
    • 这三个函数在返回下一个字符时,将unsigned char类型转换成了int类型。

    因为需要通过返回EOF来标记到达末尾或者出错。而EOF通常是常量 -1 。所以需要返回 int

  3. ferror/feof函数:查看是读文件出错,还是到达读文件遇到尾端

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

    • 参数:

      • fp:打开的文件对象指针
    • 返回值:

      • 若条件为真:则返回非 0
      • 若条件为假: 则返回 0

    当读流返回EOF时,我们可能不清楚到底是遇到错误,还是读到了文件尾端。此时必须调用ferror或者feof来区别这两种情况。

  4. clearerr函数:清除文件出错标志和文件结束标志

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

    • 参数:

      • fp:打开的文件对象指针

    在大多数操作系统中,每个流在FILE对象中维护了两个标志:

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

    调用clearerr函数可以清楚这两个标志

  5. ungetc函数:将字符压回流中

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

    • 参数:

      • c:待压入字符转换成的整数值
      • fp:打开的文件对象指针
    • 返回值:

      • 成功:则返回 c
      • 失败:返回EOF

    注意:

    • 若根据某个序列向流中压入一串字符,则再从该流中读取的字符序列是逆序的。即最后压入的字符最先读出
    • 可以执行任意次数的压入单个字符,但是不支持一次压入多个字符
    • 不能压入 EOF。但是当已经读到文件尾端时,支持压入一个字符,此时ungetc会清除该流的文件结束标志

    ungetc通常用于这样的情形:正在读取一个输入流,然后需要根据某个字符串(标记字符串)来对输入进行切分。那么我们就需要先看一看下一个字符,来决定如何处理当前字符。此时需要方便的将刚查看的字符回送。

    ungetc只是将字符压入流缓冲区中,并没有压入底层的磁盘文件或者操作系统内核中

  6. putc/fputc/putchar函数:一次写一个字符

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

    • 参数:

      • c:待写字符转换成的整数值
      • fp:打开的文件对象指针
    • 返回值:

      • 成功:则返回 c
      • 失败:返回EOF

    注意:

    • putchar(c)等价于putc(c,stdout)。它向标准输出中写一个字符
    • putcfputc的区别在于:putc可能通过宏定义来实现,而fputc不能实现为宏
  7. fgets/gets函数:一次读一行字符:

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

    • 参数:

      • buf:存放读取到的字符的缓冲区地址

      对于 fgets函数:

      • n:缓冲区长度
      • fp:打开的文件对象指针
    • 返回值:

      • 成功:则返回buf
      • 到达文件尾端:返回EOF
      • 失败:返回EOF

    注意:

    • 对于fgets函数,必须指定缓冲区的长度n。该函数一直读到下一个换行符为止,但是不超过n-1个字符。

      • 无论读到多少个字符,缓冲区一定以null字节结尾
      • 若某一行包括换行符超过 n-1个字节,则fgets只返回一个不完整的行;下次调用fgets会继续读该行
    • 对于gets函数,从标准输入总读取字符。由于无法指定缓冲区的长度,因此很可能造成缓冲区溢出漏洞。故该函数不推荐使用
    • 对于发生错误和读到末尾,都是返回EOF
  8. fputs/puts函数:一次写一行字符:

    #include<stdio.h> int fputs(const char* restrict str,FILE*restrict fp); int puts(const char*str);

    • 参数:

      • str:待写的字符串
      • fp:打开的文件对象指针
    • 返回值:

      • 成功:则返回非负值
      • 失败:返回EOF

    注意:

    • fputsputs都是将一个以null字节终止的字符串写到流中,末尾的null字符不写出!。字符串不要求以换行符结尾!
    • puts将字符串写道标准输出,末尾的null字符不写出!但是puts随后又将一个换行符写到标准输出中!。而fputs不会自动添加换行符。

    虽然puts是安全的,但是我们也是要避免使用它,以免要记住它在最后是否添加了一个换行符。

  9. fread/fwrite函数:执行二进制读写IO

    #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);

    • 参数:

      • ptr:存放二进制数据对象的缓冲区地址
      • size:单个二进制数据对象的字节数(比如一个struct的大小)
      • nobj:二进制数据对象的数量
      • fp:打开的文件对象指针
    • 返回值:

      • 成功或失败: 读/写的对象数

        • 对于读:如果出错或者到达文件尾端,则此数字可以少于nobj。此时应调用ferror或者feof来判断究竟是那种情况
        • 对于写:如果返回值少于nobj,则出错

    使用二进制IO的基本问题是:它只能用在读取同一个操作系统上已写的数据。如果跨操作系统读写,则很可能工作异常。因为:

    • 同一个struct,可能在不同操作系统或者不同编译系统中,成员的偏移量不同
    • 存储多字节整数和浮点数的二进制格式在不同的操作系统中可能不同
  10. 示例:

    #include <stdio.h> #include<string.h> #include<errno.h> #define N 5 //################# binary io ############## struct my_struct{ int i1; double i2; }; struct my_struct datas[N]; void initital_datas() { int i=0; for(i=0;i<N;i++) { datas[i].i1=i; datas[i].i2=0.666; } } void clear_datas() { int i=0; for(i=0;i<N;i++) { datas[i].i1=0; datas[i].i2=0; } } void print_datas() { printf("Data is:\n"); int i=0; for(int i=0;i<N;i++) { printf("data[%d]: i1:%d, i2:%f\n",i,datas[i].i1,datas[2]); } printf("\n"); } void test_fread(FILE*file) { size_t len; len=fread(datas,sizeof(struct my_struct),N,file); if(len!=N) { printf("\tRead binary data failed:"); test_error_eof(file); }else { printf("\tRead binary data success!\n"); } } void test_fwrite(FILE*file) { size_t len; len=fwrite(datas,sizeof(struct my_struct),N,file); if(len!=N) { printf("\tWrite binary data failed,because %s!\n",strerror(errno)); }else { printf("\tWrite binary data success!\n"); } } //########## char io ################ void test_error_eof(FILE*file) { if(ferror(file)) printf("\tRead file error,because %s\n",strerror(errno)); else if(feof(file)) printf("\tAt the end of file\n"); else printf("\tUnkown error!\n"); clearerr(file); } void test_get_char(FILE*file) { int c; if((c=fgetc(file))!=EOF) { printf("\tRead char<%c> success\n",c); }else { printf("\tRead char failed:",c); test_error_eof(file); } } void test_put_char(int c,FILE*file) { int ok; if((ok=fputc(c,file))!=EOF) { printf("\tWrite char<%c> sucess\n",ok); }else { printf("\tWrite char<%c> error,beause %s \n",strerror(errno)); } } void test_ungetc(int c,FILE*file) { int ok; if((ok=ungetc(c,file))!=EOF) { printf("\tUngetc char<%c> sucess\n",ok); }else { printf("\tUngetc char<%c> failed:",ok); test_error_eof(file); } } //########### line io ############# char buffer[2048]; void print_buffer(int len) { int i=0; printf("buffer:\n"); for (i=0;i<len;i++) { printf("\t0x%x;",buffer[i]); } printf("\n"); } void test_fgets(FILE *file) { char* buf; buf=fgets(buffer,2028,file); if(buf==NULL) { printf("fpgets failed:"); test_error_eof(file); }else { printf("fpgets success\n"); } } void tet_fputs(const char*str,FILE *file) { int len; len=fputs(str,file); if(len==EOF) { printf("fputs <%s> failed,beause %s\n",str,strerror(errno)); }else { printf("fputs <%s> success\n",str); } } int main(int argc, char *argv[]) { FILE *fp_text,*fp_binary; fp_text=fopen("/home/huaxz1986/test","w+"); if(fp_text==NULL) return; //###### test char io ############# printf("\n-----------------Test char IO ----------------------\n"); printf("Test put char:\n"); test_put_char('A',fp_text); rewind(fp_text); printf("Test get char:\n "); test_get_char(fp_text); printf("Test ungetc:\n"); test_ungetc('B',fp_text); printf("Test get char:\n "); test_get_char(fp_text); printf("Test get char:\n "); test_get_char(fp_text); //######### Test line io ############## printf("\n-----------------Test line IO ----------------------\n"); rewind(fp_text); tet_fputs("abcdefghijklmnopq",fp_text); rewind(fp_text); test_fgets(fp_text); print_buffer(26); //####### Test binary io ############# printf("\n-----------------Test binary IO ----------------------\n"); fp_binary=fopen("/home/huaxz1986/test2","w+b"); if(fp_binary==NULL) return; initital_datas(); print_datas(); printf("Test fwrite:\n"); test_fwrite(fp_binary); rewind(fp_binary); printf("After Clear data:\n"); clear_datas(); print_datas(); printf("Test fread:\n"); test_fread(fp_binary); printf("After fread, the data is:\n"); print_datas(); return 0; }

    FILE_read_write

  11. 有三种方法定位标准IO流

    • 通过 ftell/fseek函数:

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

      • 参数:fp:打开的文件对象指针
      • 返回值:

        • 成功:返回当前文件位置指示
        • 失败:返回 -1L

        若是二进制文件,则文件指示器是从文件开始位置度量的,并以字节为度量单位。ftell就是返回这种字节位置。

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

      • 参数:

        • fp:打开的文件对象指针
        • offset:偏移量。其解释依赖于whence
        • whence:偏移量的解释方式:

          • SEEK_SET常量:表示从文件的起始位置开始
          • SEEK_CUR常量:表示从文件的当前位置开始
          • SEEK_END常量:表示从文件的尾端开始
      • 返回值:

        • 成功:返回 0
        • 失败:返回 -1

        原书说,对文本文件和二进制文件,fseek定位有某些限制。但是经过在ubuntu 16.04上测试,可以任意定位。并没有要求说不能定位到文件尾端,以及必须用SEEK_SET等诸多限制。

      #include<stdio.h> void rewind(FILE *fp);

      • 参数:

        • fp:打开的文件对象指针

        rewind函数将一个流设置到文件的起始位置

    • 通过 ftello/fseeko函数:除了偏移量类型为off_t而不是long以外,ftello/fseekoftell/fseek相同

      #include<stdio.h> off_t ftello(FILE *fp);

      • 参数:fp:打开的文件对象指针
      • 返回值:

        • 成功:返回当前文件位置指示
        • 失败:返回 (off_t)-1

      #include<stdio.h> int fseeko(FILE *fp,off_t offset,int whence);

      • 参数:

        • fp:打开的文件对象指针
        • offset:偏移量。其解释依赖于whence
        • whence:偏移量的解释方式:

          • SEEK_SET常量:表示从文件的起始位置开始
          • SEEK_CUR常量:表示从文件的当前位置开始
          • SEEK_END常量:表示从文件的尾端开始
      • 返回值:

        • 成功:返回 0
        • 失败:返回 -1
    • fgetpos/fsetpos函数:由 ISO C 引入

      #include<stdio.h> int fgetpos(FILE *restrict fp,fpos_t *restrict pos); int fsetpos(FILE * fp,const fpos_t * pos);

      • 参数:

        • fp:打开的文件对象指针
        • pos:存放偏移量的缓冲区
      • 返回值:

        • 成功: 返回 0
        • 失败: 返回非 0
  12. 示例

    #include <stdio.h> #include<string.h> #include<errno.h> #define N 5 //################# binary io ############## struct my_struct{ int i1; double i2; char i3; }; struct my_struct datas[N]; void test_fwrite(FILE*file) { size_t len; len=fwrite(datas,sizeof(struct my_struct),N,file); if(len!=N) { printf("\tWrite binary data failed,because %s!\n",strerror(errno)); clearerr(file); }else { printf("\tWrite binary data success!\n"); } } //########## char io ################ void test_error_eof(FILE*file) { if(ferror(file)) printf("\tRead file error,because %s\n",strerror(errno)); else if(feof(file)) printf("\tAt the end of file\n"); else printf("\tUnkown error!\n"); clearerr(file); } void test_put_char(int c,FILE*file) { int ok; if((ok=fputc(c,file))!=EOF) { printf("\tWrite char<%c> sucess\n",ok); }else { printf("\tWrite char<%c> error,beause %s \n",strerror(errno)); } } //########### seek ########### void test_tell(FILE *fp) { long ok=ftell(fp); fpos_t pos; if(ok==-1) { printf("\tftell error,because %s.",strerror(errno)); clearerr(fp); }else { printf("\tftell :%d.",ok); } if(0==fgetpos(fp,&pos)) { printf("\tfgetpos :%d\n",pos); }else { printf("\tfgetpos error,because %s\n",strerror(errno)); clearerr(fp); } } void test_seek(FILE *fp,int offset,int whence) { int ok; ok=fseek(fp,offset,whence); if(ok!=-1) { printf("\tfseek ok\n"); }else { printf("\tfseek error,because %s\n",strerror(errno)); clearerr(fp); } } int main(int argc, char *argv[]) { FILE *fp_text,*fp_binary; //####### test text file seek ######## fp_text=fopen("/home/huaxz1986/test","w+"); if(fp_text==NULL) return; printf("\n-----------------Test char IO ----------------------\n"); test_put_char('A',fp_text); test_put_char('B',fp_text); test_put_char('C',fp_text); test_put_char('D',fp_text); test_put_char('E',fp_text); printf("Test tell:"); test_tell(fp_text); printf("Test SEEK_SET:"); test_seek(fp_text,0,SEEK_SET); printf(" current pos:"); test_tell(fp_text); printf("Test SEEK_CUR:"); test_seek(fp_text,0,SEEK_CUR); printf(" current pos:"); test_tell(fp_text); printf("Test SEEK_END:"); test_seek(fp_text,0,SEEK_END); printf(" current pos:"); test_tell(fp_text); printf("\n-----------------Test binary IO ----------------------\n"); fp_binary=fopen("/home/huaxz1986/test2","w+b"); if(fp_binary==NULL) return; test_fwrite(fp_binary); printf("Test tell:"); test_tell(fp_binary); printf("Test SEEK_SET:"); test_seek(fp_binary,0,SEEK_SET); printf(" current pos:"); test_tell(fp_binary); printf("Test SEEK_CUR:"); test_seek(fp_binary,0,SEEK_CUR); printf(" current pos:"); test_tell(fp_binary); printf("Test SEEK_END:"); test_seek(fp_binary,0,SEEK_END); printf(" current pos:"); test_tell(fp_binary); return 0; }

    FILE_seek

格式化IO

  1. 格式化输出函数:

    #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,...);

    • 参数:

      • format,...:输出的格式化字符串

      对于fprintf

      • fp:打开的文件对象指针。格式化输出到该文件中

      对于dprintf

      • fd:打开文件的文件描述符。格式化输出到该文件中

      对于sprintf:

      • buf:一个缓冲区的指针。格式化输出到该缓冲区中

      对于snprintf:

      • buf:一个缓冲区的指针。格式化输出到该缓冲区中
      • n:缓冲区的长度。格式化输出到该缓冲区中
    • 返回值:

      • 成功:返回输出字符数(不包含null字节)
      • 失败:返回负数

    printf将格式化输出写到标准输出;fprintf写到指定的流;dprintf写到指定的文件描述符;sprintf写到数组buf中;snprintf也是写到数组buf中,但是在该数组的尾端自动添加一个null字节(该字节不包含在返回值中)。

    • 通常不推荐使用sprintf,因为它可能引起缓冲区溢出流动
    • 如果格式化输出一共 s 个字节,那么snprintf的数组缓冲区至少为s+1个字节,否则发生截断
  2. 格式说明:%[flags][fldwidth][precision][lenmodifier]convtype

    • 标志flags有:

      • ' : 撇号,将整数按照千位分组字符
      • - : 在字段内左对齐输出
      • +: 总是显示带符号转换的正负号
      • :空格。如果第一个字符不是正负号,则在其前面加一个空格
      • #:指定另一种转换形式(如,对于十六进制格式,加 0x 前缀)
      • 0:添加前导0(而非空格) 进行填充
    • fldwidth:说明最小字段宽度。转换后参数字符如果小于宽度,则多余字符位置用空格填充。

      • 字段宽度是一个非负十进制数,或者是一个星号 *
    • precision:说明整型转换后最少输出数字位数、浮点数转换后小数点后的最少位数、字符串转换后最大字节数。

      • 精度是一个点.后跟随一个可选的非负十进制数或者一个星号*

    宽度和精度可以为*,此时一个整型参数指定宽度或者精度的值。该整型参数正好位于被转换的参数之前

  3. lenmodifier:说明参数长度。可以为:

    • hh:将相应的参数按照signed char或者unsigned char类型输出
    • h:将相应的参数按照signed short或者unsigned short类型输出
    • l:将相应的参数按照signed long或者unsigned long或者宽字符类型输出
    • ll:将相应的参数按照signed longlong或者unsigned longlong类型输出
    • jintmax_t或者uintmax_t
    • zsize_t
    • tptrdiff_t
    • Llong double
  4. convtype:控制如何解释参数

    • d或者i:有符号十进制
    • o:无符号八进制
    • u:无符号十进制
    • x或者X:无符号十六进制
    • f或者F:双精度浮点数
    • e或者E:指数格式双精度浮点数
    • g或者G:根据转换后的值解释为f、F、e、E
    • a或者A:十六进制指数格式双精度浮点数
    • c:字符(若带上长度修饰符l,则为宽字符)
    • s:字符串(若带上长度修饰符l,则为宽字符)
    • p:指向void的指针
    • n:到目前位置,此printf调用输出的字符的数目将被写入到指针所指向的带符号整型中
    • %:一个%字符
    • C:宽字符,等效于lc
    • S:宽字符串,等效于ls
  5. printf族的变体:将可变参数(...)替换成了va_list 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 vdprintf(int fd,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);

    其参数与返回值与前面的printf族完全相同

  6. 格式化输入函数:

    #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,...);

    • 参数:

      • format,...:格式化字符串

      对于fscanf

      • fp:打开的文件对象指针。从流中读取输入

      对于sscanf

      • buf:一个缓冲区指针。从该缓冲区中读取输入
    • 返回值:

      • 成功:返回赋值的输入项数
      • 提前到达文件尾端:返回EOF
      • 失败:返回EOF

    scanf族用于分析输入字符串,将字符序列转换成指定类型的变量。在格式之后的各参数中包含了变量的地址,用转换结果对这些变量赋值。

    • 除了转换说明和空白字符以外,格式字符串中的其他字符必须与输入匹配。如有一个字符不匹配,则停止后续处理,不再读输入的其余部分。
    • 转换说明的格式为:%[*][fldwidth][m][lenmodifier]convtype

      • *:用于抑制转换。按照转换说明的其余部分对输入进行转换,但是转换结果不存放在参数中而是抛弃
      • fldwidth:说明最大宽度,即最大字符数
      • lenmodifier:说明要转换结果赋值的参数大小。见前述说明
      • convtype:类似前述说明。但是稍有区别:输入中的带符号的数值可以赋给无符号类型的变量
      • m:用于强迫内存分配。当%c,%s时,如果指定了m,则会自动分配内存来容纳转换的字符串。同时该内存的地址会赋给指针类型的变量(即要求对应的参数必须是指针的地址)。同时要求程序员负责释放该缓冲区(通过free函数)
  7. scanf族也有一类变体:将可变参数(...)替换成了va_list arg:

    #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);

  8. 示例:

    #include <stdio.h> #include<string.h> #include<errno.h> #include<memory.h> #define LEN 32 char buffer[LEN]; void print_buffer() { int i; printf("Buffer:\n"); for(i=0;i<LEN;i++) printf("\t0x%x",buffer[i]); printf("\n"); } void test_snprintf_int(int i) { int ok; ok=snprintf(buffer,LEN,"%d",i); if(ok<0) { printf("\tsnprintf error,because:%s\n",strerror(errno)); print_buffer(); }else { printf("\tsnprintf success, write %d chars\n",ok); print_buffer(); } } void test_snprintf_char(char c) { int ok; ok=snprintf(buffer,LEN,"%c",c); if(ok<0) { printf("\tsnprintf error,because:%s\n",strerror(errno)); print_buffer(); }else { printf("\tsnprintf success, write %d chars\n",ok); print_buffer(); } } void test_snprintf_string(const char*s) { int ok; ok=snprintf(buffer,LEN,"%s",s); if(ok<0) { printf("\tsnprintf error,because:%s\n",strerror(errno)); print_buffer(); }else { printf("\tsnprintf success, write %d chars\n",ok); print_buffer(); } } void test_sscanf_int() { int ok; int i; ok=sscanf(buffer,"%d",&i); if(ok==EOF) { printf("\tsscanf error,because:%s\n",strerror(errno)); }else { printf("\tsscanf success, read %d items:i=%08d\n",ok,i); } } void test_sscanf_char() { int ok; char c; ok=sscanf(buffer,"%c",&c); if(ok==EOF) { printf("\tsscanf error,because:%s\n",strerror(errno)); }else { printf("\tsscanf success, read %d items:c=%c\n",ok,c); } } void test_sscanf_string() { int ok; char *s; ok=sscanf(buffer,"%ms",&s); if(ok==EOF) { printf("\tsscanf error,because:%s\n",strerror(errno)); }else { printf("\tsscanf success, read %d items:string=%s\n",ok,s); free(s); } } int main(int argc, char *argv[]) { print_buffer(); test_snprintf_int(99); test_sscanf_int(); test_snprintf_char('A'); test_sscanf_char(); test_snprintf_string("abcdefghijklmn"); test_sscanf_string(); return 0; }

    print_scan

其他

  1. fileno函数:获取文件对象的文件描述符

    #include<stdio.h> int fileno(FILE *fp);

    • 参数:

      • fp:打开的文件对象的指针
    • 返回值: 返回与该流相关联的文件描述符
  2. tmpnam/tmpfile函数:创建临时文件

    #include<stdio.h> char *tmpnam(char *ptr); FILE *tmpfile(void);

    • tmpnam参数:

      • ptr:指向存放临时文件名的缓冲区的指针

        • 若为NULL,则产生的路径名存放在一个静态区中,指向该静态区的指针作为函数值返回

    下次再调用tmpnam时,会重写该静态区

  3. 如果为非NULL,则认为它指向长度至少为L_tmpnam个字符的数组,产生的路径名存放在该缓冲区中,返回ptrL_tmpnam常量定义在<stdio.h>头文件中
  4. tmpnam返回值:返回指向唯一路径名的指针
  5. tmpfile返回值:

    • 成功:返回文件指针
    • 失败:返回NULL
  6. tmpnam函数产生一个与现有文件名不同的有效路径名字符串。每次调用它时,都产生一个不同路径名。最多调用次数是TMP_MAX次(定义在<stdio.h>中)

    它只创建独一无二的文件名,但是并不创建临时文件

    tmpfile是创建一个临时二进制文件(类型wb+),在关闭该文件或者程序结束时将自动删除这种文件

    UNIX对二进制文件、文本文件并不进行特殊区分

  7. mkdtemp/mkstemp函数:创建临时文件(由SUS 标准给出)

    #include<stdlib.h> char *mkdtemp(char *template); int mkstemp(char *template);

    • 参数:

      • template:一个字符串。这个字符是最末6个字符设置为XXXXXX的路径名。函数将这些占位符替代成不同的字符来构建一个唯一的路径名。若成功的话,这两个函数将修改template字符串来反映临时文件的名字

    因为函数会修改template,因此一定不能用常量字符串来赋值!

  8. mkdtemp返回值:

    • 成功:返回指向目录名的指针
    • 失败:返回NULL
  9. mkstemp返回值:

    • 成功: 返回文件描述符
    • 失败: 返回 -1
  10. mkdtemp函数创建了一个目录,该目录有一个唯一的名字;mkstemp函数创建了一个文件,该文件有一个唯一的名字。名字是通过template字符串进程构建的。

    • mkdtemp函数创建的目录具有权限位集: S_IRUSR|S_IWUSR|S_IXUSR。调用进程的文件模式创建屏蔽字可以进一步限制这些权限
    • mkstemp函数返回的文件描述符以读写方式打开。它创建的文件用访问权限位:S_IRUSR|S_IWUSR
    • mkstemp创建的临时文件并不会自动删除
  11. 示例:

    #include <stdio.h> #include<stdlib.h> #include<string.h> #include<errno.h> char buffer[L_tmpnam]; void print_buffer() { int i; printf("\tBuffer:\n"); for(i=0;i<L_tmpnam;i++) printf("\t0x%x",buffer[i]); printf("\n"); } void test_tmpname() { char *p; p=tmpnam(buffer); if(NULL==p) { printf("\ttmpnam failed,because of %s\n",strerror(errno)); }else { printf("\ttmpnam success,file name is: %s\n",buffer); } } void test_tmpfile() { FILE *fp; fp=tmpfile(); if(fp==NULL) { printf("\ttmpfile failed,because of %s\n",strerror(errno)); }else { printf("\ttmpfile success,file descriptor is: %d\n",fp->_fileno); } } void test_mkdtemp() { char * name; char old_name[128]="/home/huaxz1986/test/abc123XXXXXX"; name=mkdtemp(old_name); if(NULL==name) { printf("\ttest_mkdtemp failed,because of %s\n",strerror(errno)); }else { printf("\ttest_mkdtemp success,the new name is %s\n",name); } } void test_mkstemp() { int ok; char old_name[128]="/home/huaxz1986/test/abc123XXXXXX"; ok=mkstemp(old_name); if(-1==ok) { printf("\ttest_mkstemp failed,because of %s\n",strerror(errno)); }else { printf("\ttest_mkstemp success, file descriptor is: %d\n",ok); } } int main(int argc, char *argv[]) { print_buffer(); printf("Test tmpname:\n"); test_tmpname(); print_buffer(); printf("Test tmpfile:\n"); test_tmpfile(); printf("Test mkdtemp:\n"); test_mkdtemp(); printf("Test mkstemp:\n"); test_mkstemp(); return 0; }

    make_temp_file

  12. 内存流:一种标准IO流,虽然它通过 FILE指针来访问,但是并没有底层的文件 。所有的IO都是通过在缓冲区和主存之间来回传送字节来完成。

    虽然它看起来像是文件流,但是更适用于字符串操作

    • 创建内存流:

      #include<stdio.h> FILE *fmemopen(void *restrict buf,size_t size,const char *restrict type);

      • 参数:

        • buf:内存流缓冲区的起始地址
        • size:内存流缓冲区的大小(字节数)

          • bufNULL时,则函数负责分配size字节的缓冲区,并在流关闭时自动释放分配的缓冲区
        • type:控制如何使用流(即打开内存流的方式):

          • r或者rb:读打开
          • w或者wb:写打开
          • a或者ab:追加打开;为在第一个null字节处写打开
          • r+或者r+brb+:读写打开
          • w+或者w+bwb+:把文件截断为0,然后读写打开
          • a+或者a+bab+:追加;为在第一个null字节处读写打开
      • 返回值:

        • 成功:返回流指针
        • 失败:返回NULL

      注意:

      • 无论何时以追a方式打开内存流时,当前文件位置设为缓冲区中第一个null字节处。

        • 若缓冲区中不存在null字节,则当前位置设为缓冲结尾的后一个字节
      • 当内存流不是a方式打开时,当前位置设置为缓冲区的开始位置
      • 如果bufnull,则打开流进行读或者写都没有任何意义。因为此时缓冲区是通过fmemopen分配的,没办法找到缓冲区的地址。
      • 任何时候需要增加流缓冲区中数据流以及调用fclose、fflush、fseek、fseeko、fsetpos时都会在当前位置写入一个null字节
    • 创建内存流的其他两个函数:

      #include<stdio.h> FILE *open_memstream(char **bufp,size_t *sizep); #include <wchar.h> FILE *open_wmemstream(wchar_t **bufp,size_t *sizep);

      • 参数:

        • bufp:指向缓冲区地址的指针(用于返回缓冲区地址)
        • sizep:指向缓冲区大小的指针(用于返回缓冲区大小)
      • 返回值:

        • 成功:返回流指针
        • 失败:返回 NULL

      这两个函数创建的流:

      • 只能写打开
      • 缓冲区由函数自动创建
      • 关闭流后需要程序员释放缓冲区
      • 对流添加字节会增加缓冲区大小

      在缓冲区地址和大小使用上要遵守规则:

      • 缓冲区地址和长度只有在调用fclose或者fflush后才有效
      • 这些值只有在下一次写入或者调用fclose之前才有效。因为缓冲区可能增长,也可能需要重新分配
  13. 示例:

    #include <stdio.h> #include<string.h> #include<errno.h> #define LEN 32 char buffer[LEN]; void print_buffer() { int i; printf("\tBuffer:\n"); for(i=0;i<LEN;i++) printf("\t0x%x",buffer[i]); printf("\n"); } FILE * test_fmemopen() { FILE *fp; fp=fmemopen(buffer,LEN,"r+"); if(NULL==fp) { printf("\tfmemopen failed,because:%s\n",strerror(errno)); return NULL; } else { printf("\tfmemopen success\n"); return fp; } } int main(int argc, char *argv[]) { FILE *fp; char read_buffer[LEN]; print_buffer(); fp=test_fmemopen(); if(fp==NULL) return; printf("Write to mem FILE\n"); fputs("abcedfghijklmn",fp); fflush(fp);# 必须冲洗才能在 buffer 中体现出来 print_buffer(); printf("Seek to begin\n"); fseek(fp,0,SEEK_SET); printf("Write to mem FILE\n"); fputs("ABCDEFG",fp); fflush(fp); print_buffer(); printf("Read from mem FILE\n"); fgets(read_buffer,LEN,fp); printf("\tread:%s\n",read_buffer); return 0; }

    mem_FILE_stream

  14. 标准IO库的缺点:效率不高。这与它需要复制的数据量有关。当使用每次一行的函数fgets/fputs时,通常需要复制两次数据:

    • 内核和标准IO缓冲区之间(当调用read/write时)
    • 标准IO缓冲区和用户程序的缓冲区之间
$(function () { $('pre.prettyprint code').each(function () { var lines = $(this).text().split('\n').length; var $numbering = $('
    ').addClass('pre-numbering').hide(); $(this).addClass('has-numbering').parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($('
  • ').text(i)); }; $numbering.fadeIn(1700); }); });

    本文转载自taoyanqi8932博客,版权归taoyanqi8932所有

    扩展阅读,根据您访问的内容系统为您准备了以下内容,希望对您有帮助。

    UNIX环境高级编程的作品目录

    第1章 UNIX基础知识 1

    1.1 引言 1

    1.2 UNIX体系结构 1

    1.3 登录 1

    1.4 文件和目录 3

    1.5 输入和输出 6

    1.6 程序和进程 8

    1.7 出错处理 10

    1.8 用户标识 12

    1.9 信号 14

    1.10 时间值 15

    1.11 系统调用和库函数 16

    1.12 小结 17

    习题 18

    第2章 UNIX标准化及实现 19

    2.1 引言 19

    2.2 UNIX标准化 19

    2.2.1 ISO C 19

    2.2.2 IEEE POSIX 20

    2.2.3 Single UNIX Specification 25

    2.2.4 FIPS 26

    2.3 UNIX系统实现 26

    2.3.1 SVR4 26

    2.3.2 4.4BSD 27

    2.3.3 FreeBSD 27

    2.3.4 Linux 27

    2.3.5 Mac OS X 28

    2.3.6 Solaris 28

    2.3.7 其他UNIX系统 28

    2.4 标准和实现的关系 28

    2.5 * 29

    2.5.1 ISO C* 29

    2.5.2 POSIX* 30

    2.5.3 XSI* 32

    2.5.4 sysconf、pathconf和fpathconf函数 32

    2.5.5 不确定的运行时* 38

    2.6 选项 42

    2.7 功能测试宏 44

    2.8 基本系统数据类型 45

    2.9 标准之间的冲突 45

    2.10 小结 46

    习题 46

    第3章 文件I/O 47

    3.1 引言 47

    3.2 文件描述符 47

    3.3 open函数 48

    3.4 creat函数 49

    3.5 close函数 50

    3.6 lseek函数 50

    3.7 read函数 53

    3.8 write函数 54

    3.9 I/O的效率 54

    3.10 文件共享 56

    3.11 原子操作 59

    3.12 dup和dup2函数 60

    3.13 sync、fsync和fdatasync函数 61

    3.14 fcntl函数 62

    3.15 ioctl函数 66

    3.16 /dev/fd 67

    3.17 小结 68

    习题 68

    第4章 文件和目录 71

    4.1 引言 71

    4.2 stat、fstat和lstat函数 71

    4.3 文件类型 72

    4.4 设置用户ID和设置组ID 74

    4.5 文件访问权限 75

    4.6 新文件和目录的所有权 77

    4.7 access函数 77

    4.8 umask函数 79

    4.9 chmod和fchmod函数 81

    4.10 粘住位 83

    4.11 chown、fchown和lchown函数 84

    4.12 文件长度 85

    4.13 文件截短 86

    4.14 文件系统 86

    4.15 link、unlink、remove和rename函数 89

    4.16 符号链接 91

    4.17 symlink和readlink函数 94

    4.18 文件的时间 94

    4.19 utime函数 95

    4.20 mkdir和rmdir函数 97

    4.21 读目录 98

    4.22 chdir、fchdir和getcwd函数 102

    4.23 设备特殊文件 104

    4.24 文件访问权限位小结 106

    4.25 小结 106

    习题 107

    第5章 标准I/O库 109

    5.1 引言 109

    5.2 流和FILE对象 109

    5.3 标准输入、标准输出和标准出错 110

    5.4 缓冲 110

    5.5 打开流 112

    5.6 读和写流 114

    5.7 每次一行I/O 116

    5.8 标准I/O的效率 117

    5.9 二进制I/O 119

    5.10 定位流 120

    5.11 格式化I/O 121

    5.12 实现细节 125

    5.13 临时文件 127

    5.14 标准I/O的替代软件 130

    5.15 小结 130

    习题 130

    第6章 系统数据文件和信息 133

    6.1 引言 133

    6.2 口令文件 133

    6.3 阴影口令 136

    6.4 组文件 137

    6.5 附加组ID 138

    6.6 实现的区别 139

    6.7 其他数据文件 139

    6.8 登录账户记录 140

    6.9 系统标识 141

    6.10 时间和日期例程 142

    6.11 小结 146

    习题 146

    第7章 进程环境 147

    7.1 引言 147

    7.2 main函数 147

    7.3 进程终止 147

    7.4 命令行参数 151

    7.5 环境表 152

    7.6 C程序的存储空间布局 152

    7.7 共享库 154

    7.8 存储器分配 154

    7.9 环境变量 157

    7.10 setjmp和longjmp函数 159

    7.11 getrlimit和setrlimit函数 164

    7.12 小结 168

    习题 168

    第8章 进程控制 171

    8.1 引言 171

    8.2 进程标识符 171

    8.3 fork函数 172

    8.4 vfork函数 176

    8.5 exit函数 178

    8.6 wait和waitpid函数 179

    8.7 waitid函数 183

    8.8 wait3和wait4函数 184

    8.9 竞争条件 185

    8.10 exec函数 188

    8.11 更改用户ID和组ID 192

    8.12 解释器文件 196

    8.13 system函数 200

    8.14 进程会计 203

    8.15 用户标识 208

    8.16 进程时间 208

    8.17 小结 210

    习题 211

    第9章 进程关系 213

    9.1 引言 213

    9.2 终端登录 213

    9.3 网络登录 216

    9.4 进程组 218

    9.5 会话 219

    9.6 控制终端 220

    9.7 tcgetpgrp、tcsetpgrp和tcgetsid函数 221

    9.8 作业控制 222

    9.9 shell执行程序 225

    9.10 孤儿进程组 228

    9.11 FreeBSD实现 230

    9.12 小结 231

    习题 232

    第10章 信号 233

    10.1 引言 233

    10.2 信号概念 233

    10.3 signal函数 240

    10.4 不可靠的信号 242

    10.5 中断的系统调用 244

    10.6 可重入函数 246

    10.7 SIGCLD语义 248

    10.8 可靠信号术语和语义 250

    10.9 kill和raise函数 251

    10.10 alarm和pause函数 252

    10.11 信号集 256

    10.12 sigprocmask函数 258

    10.13 sigpending函数 259

    10.14 sigaction函数 261

    10.15 sigsetjmp和siglongjmp函数 266

    10.16 sigsuspend函数 268

    10.17 abort函数 274

    10.18 system函数 276

    10.19 sleep函数 280

    10.20 作业控制信号 282

    10.21 其他特征 284

    10.22 小结 285

    习题 285

    第11章 线程 287

    11.1 引言 287

    11.2 线程概念 287

    11.3 线程标识 288

    11.4 线程的创建 288

    11.5 线程终止 291

    11.6 线程同步 297

    11.7 小结 311

    习题 311

    第12章 线程控制 313

    12.1 引言 313

    12.2 线程* 313

    12.3 线程属性 314

    12.4 同步属性 318

    12.5 重入 324

    12.6 线程私有数据 328

    12.7 取消选项 331

    12.8 线程和信号 333

    12.9 线程和fork 336

    12.10 线程和I/O 339

    12.11 小结 340

    习题 340

    第13章 守护进程 341

    13.1 引言 341

    13.2 守护进程的特征 341

    13.3 编程规则 342

    13.4 出错记录 345

    13.5 单实例守护进程 348

    13.6 守护进程的惯例 350

    13.7 客户进程-服务器进程模型 354

    13.8 小结 354

    习题 354

    第14章 高级I/O 355

    14.1 引言 355

    14.2 非阻塞I/O 355

    14.3 记录锁 357

    14.4 STREAMS 370

    14.5 I/O多路转接 379

    14.5.1 select和pselect函数 381

    14.5.2 poll函数 384

    14.6 异步I/O 386

    14.6.1 系统V异步I/O 386

    14.6.2 BSD异步I/O 387

    14.7 readv和writev函数 387

    14.8 readn和writen函数 389

    14.9 存储映射I/O 390

    14.10 小结 395

    习题 396

    第15章 进程间通信 397

    15.1 引言 397

    15.2 管道 398

    15.3 popen和pclose函数 403

    15.4 协同进程 408

    15.5 FIFO 412

    15.6 XSI IPC 415

    15.6.1 标识符和键 415

    15.6.2 权限结构 416

    15.6.3 结构* 417

    15.6.4 优点和缺点 417

    15.7 消息队列 418

    15.8 信号量 422

    15.9 共享存储 427

    15.10 客户进程-服务器进程属性 432

    15.11 小结 434

    习题 434

    第16章 网络IPC:套接字 437

    16.1 引言 437

    16.2 套接字描述符 437

    16.3 寻址 439

    16.3.1 字节序 440

    16.3.2 地址格式 441

    16.3.3 地址查询 442

    16.3.4 将套接字与地址绑定 449

    16.4 建立连接 450

    16.5 数据传输 452

    16.6 套接字选项 464

    16.7 带外数据 466

    16.8 非阻塞和异步I/O 467

    16.9 小结 468

    习题 468

    第17章 高级进程间通信 469

    17.1 引言 469

    17.2 基于STREAMS的管道 469

    17.2.1 命名的STREAMS管道 472

    17.2.2 唯一连接 473

    17.3 UNIX域套接字 476

    17.3.1 命名UNIX域套接字 477

    17.3.2 唯一连接 478

    17.4 传送文件描述符 482

    17.4.1 经由基于STREAMS的管道传送文件描述符 484

    17.4.2 经由UNIX域套接字传送文件描述符 486

    17.5 open服务器版本1 493

    17.6 open服务器版本2 498

    17.7 小结 505

    习题 505

    第18章 终端I/O 507

    18.1 引言 507

    18.2 综述 507

    18.3 特殊输入字符 512

    18.4 获得和设置终端属性 516

    18.5 终端选项标志 516

    18.6 stty命令 522

    18.7 波特率函数 523

    18.8 行控制函数 524

    18.9 终端标识 524

    18.10 规范模式 529

    18.11 非规范模式 532

    18.12 终端的窗口大小 537

    18.13 termcap,terminfo和curses 539

    18.14 小结 540

    习题 540

    第19章 伪终端 541

    19.1 引言 541

    19.2 概述 541

    19.3 打开伪终端设备 544

    19.3.1 基于STREAMS的伪终端 547

    19.3.2 基于BSD的伪终端 549

    19.3.3 基于Linux的伪终端 551

    19.4 pty_fork函数 553

    19.5 pty程序 555

    19.6 使用pty程序 559

    19.7 高级特性 564

    19.8 小结 565

    习题 565

    第20章 数据库函数库 567

    20.1 引言 567

    20.2 历史 567

    20.3 函数库 568

    20.4 实现概述 569

    20.5 集中式或非集中式 572

    20.6 并发 574

    20.7 构造函数库 574

    20.8 源代码 575

    20.9 性能 598

    20.10 小结 600

    习题 601

    第21章 与网络打印机通信 603

    21.1 引言 603

    21.2 网络打印协议 603

    21.3 超文本传输协议 605

    21.4 打印假脱机技术 605

    21.5 源代码 607

    21.6 小结 644

    习题 645

    附录A 函数原型 647

    附录B 其他源代码 677

    附录C 部分习题答案 685

    参考书目 709

    索引 715

    《UNIX环境高级编程》的课后习题5.7求解

    《UNIX环境高级编程》中指出,调用fgets函数后会使终端设备自动将自动刷清,这在fgets函数那一节找不到答案,但是附录中的答案参考中指出了这一点。所以程序清单的程序运行时会有%输出。

    unix环境高级编程 重点章节有哪些

    每章都是平时工作中可能用到的内容,很难说谁重要谁不重要,我一般是把这本书当成参考书用的,实际编程中有什么东西想不起来就翻翻这本书相关章节。

    对于入门来说,一般来说建议把以下章节先看看

    文件、I/O 相关

    3. File I/O

    4. Files and Directories

    5. Standard I/O library

    进程,线程

    8. Process Control

    10. Threads

    进程间通讯

    15. Interprocess Communication

    以上纯属个人意见,供参考。另外,其他章节并非不重要,只是初学不一定会遇到相关问题。

    总之,我觉得这本书是随时翻一翻看看相关章节的那种参考工具书,而不是能抱着从头到尾看两遍就彻底学完的课本。

    unix环境高级编程,适合初学者吗

    我跟你说说我看APUE的经历。在看APUE之前,我的Linux知识已经积累很多了,C也是特别熟悉,以及C++,但是第一次看APUE还是让人很沮丧的,中途而废,因为里面的很多问题是完全之前没想过,接触过的,知识储备不够,所以我放了差不多半年,这半年我补了很多知识,看了基本Linux系统编程入门的书籍,了解了GCC、GDB的基本使用,学会了vim,学了些进程线程的东西。

    第二次看APUE,我坚持看完了,收获很大,但是不懂的东西还是很多。

    第三次看APUE,对很多问题有自己的看法,并且开始去尝试其中的一些例程。

    第四次看APUE,我就能够很顺利的梳理APUE的结构了,如果开发中遇到什么问题,我知道去哪儿找答案。

    第五次看APUE,我粗略的翻翻就知道哪些地方由于长期不用变得生疏。

    APUE是本圣经,可以说不多翻几次是看不出它是个好东西的。几年下来APUE被我都快翻烂了,确实学到不少东西。不管做不做系统看法,当看一些文档时我都能游刃有余了。

    我的建议,早看。早看早入门,只要你的C语言知识足够,Linux知识储备足够,早看!

    看完《unix环境高级编程》能做什么项目

    看完你只能写些简单的命令,或者程序。实际项目中要复杂得多,需要实践

  • 本文相关:
  • 原型聚类总结
  • 游戏测试---------第2章
  • 【HDU 5721】Palace(平面最近点对)
  • 回归- Regression
  • Git 入门 ---- Git 常用命令
  • 转行做程序员之前你应该考虑的三件事
  • 钉钉、企业微信等9款协同办公产品互相厮杀,你赌谁赢?
  • Linux-(C/C++)动态内存分配malloc以及相关学习
  • 从苏宁电器到卡巴斯基第15篇:我在苏宁电器当营业员 VII
  • [玩耍]贪吃蛇
免责声明 - 关于我们 - 联系我们 - 广告联系 - 友情链接 - 帮助中心 - 频道导航
Copyright © 2017 www.zgxue.com All Rights Reserved