第五章 标准I/O库

函数    fwide用于设置流的定向
    fwide(FILE *fp,int mode);
        若 mode 为负,fwide 将试图使指定的流的字节定向的
        若    为正,            宽定向的
        若    为0,将不试图设置流的定向。
    对于这个函数,书上没有过多介绍,我也没用过,暂且放在这里吧


文件描述符对应的标准I/O
    STDIN_FILENO    stdin
    STDOUT_FILENO    stdout
    STDERR_FILENO    stderr


缓冲
    标准错误是绝对不会带缓冲区的,因为需要将错误尽早暴露出来
函数    setbuf(FILE*, char *);
        可用于打开或关闭缓冲机制;将char*设置为NULL 即为关闭缓冲 ;不为NULL则是长度为BUFSIZE的用户缓冲区
        还有一些相关的函数在manpage中,可用于设置全缓冲、行缓存、无缓存;一旦设置了缓冲类型,即使char*为NULL,系统也会选择一段合适长度的系统缓冲区
强制冲洗函数    fflush(FILE*);
            若参数为NULL,那么所有输出流将被清洗。


打开流函数    fopen
        freopen(char *name,char *mode,FILE *)
            在一个指定的流上打开一个指定文件。如若该流已打开,则先关闭该流。其实与dup2类似。例如 freopen("file","w",stdout),
                那么将来输出的目标就不是屏幕而是file文件了
        fdopen(fd,mode)
            之前在管道部分接触过,用这个的一个原因是可以格式化输入输出,比如 fprintf ,fscanf等等
            不过,这里的mode 可是有些特殊的,因为既然有了fd,说明此文件已经存在了。因此:
                为写而打开并不截断(O_TRUNC)该文件(即已经用open打开的文件,fdopen则不能截断它为写而打开的任意文件)
            
    
    以下将标准I/O的 mode 和 系统调用中使用的mode 相互对应起来,使用 b 是将文件区分为文本文件和二进制文件
        r / rb            O_RDONLY
        w / wb            O_WRONLY | O_CREAT | O_TRUNC
        a / ab            O_WRONLY | O_APPEND |CREAT
        r+ / r+b / rb+        O_RDWR
        w+ / w+b / wb+        O_RDWR | O_CREAT | O_TRUNC
        a+ / a+b / ab+        O_RDWR | O_CREAT | O_APPEND
    一目了然哟

    还应该注意的是,当我们以 读+写 的方式打开文件后,如果中间没有 fflush、fseek、fsetpos、rewind,则在 输出 的后面不能直接跟 输入
                           如果中间没有 fseek、fsetpos、rewind,或者一个输入操作没有达到文件尾端,则输入之后不能直接跟输出
        关于这一点我也是有些体会的,当初虽然以读写的方式打开了一个文件,但输入之后立马输出却发生了未知错误
    fclose 关闭流


读和写流
    getc(FILE*) 与 fgetc(FILE*)        【相对应的自然有putc  fputc】
        这两者看似相同,但是有较大区别。 getc()可被实现为宏 ,而fgetc不能
        因此:
            getc的参数不应是具有副作用的表达式,因为可能会被计算多次
            因为fgetc是一个函数,所以可以得到其地址,所以可将其地址作为参数传给另一个函数    
            fgetc所需时间可能比getc长。
    这里有一个比较新奇的函数
        叫 ungetc(int,FILE*);可以将读出的字符回送至缓冲区。
            比如我们需要看看下一个字符是什么,拿出来后还想放回去就可使用这个函数
    
    fgets和gets
        fgets一直读到下一个换行符为止,但是不超过n-1个字符(这才是先决条件)。会将换行符存入缓冲区。
        不推荐使用gets,应为没有长度限制,会造成溢出。

二进制I/O
    函数    fread 和 fwrite
        用于读或写一个结构
    比如:    将一个int数组的第 2 至 5 的元素写至文件中去:
            if(fwrite(&date[2],sizeof(int),4,fp) != 4)
                perror();
        可以看出,返回的是写入的对象的个数
    不过,使用这种二进制I/O也是有问题存在的。它只能用于读取 同一系统 上的已写的数据。
        现在网络通信,往往不同系统连接,但数据的存储方式往往有些差异,用fread/fwrite 会造成问题。


定位流
    fseek, ftell
    fgetpos(FILE *, fpos_t *) 和 fsetpos
        前者将当前值存入 fpos_t 指向的对象中, 后者可以使用该值重定位至该处。


格式化I/O
    printf, fprintf, dprintf, sprintf, snprintf
        snprintf是sprintf的安全保障,添加了指定字符串的长度设置。
        这里出现了一个 dprintf !

接下来演示了一下标准输出、输入、错误连接至终端    和    将标准输入、输出、错误定向至文件后 这三者的缓冲区状态、    
    当连接终端时,输入输出皆为行缓冲,错误为无缓冲
    当定向至普通文件时,输入输出为全缓冲,错误无缓冲。
    这里的程序写的应该是具有可移植性的,可以效仿            P131


临时文件
    函数    char *tmpnam(char *), FILE*  tmpfile(void)
            以前使用临时文件发现这东西删起来挺容易的,还易鉴别就是临时文件。。。
            tmpnam生成的是路径名!而非实实在在的文件,这也就是下面程序提示危险的原因之一 。
        书上有一段对tmpfile 的解释:
            tmpfile常常是 先调用tmpnam产生一个唯一的路径名,然后用该路径名创建一个文件,并 立即 unlink它! 还记得前一章?说道unlink部分
                ,只要一个进程打开着,那么我们就可以向其中写入和读出数据。尽管该文件名义上已经不存在了。
        还应该注意的是, tmpfile创建的文件最后会由该函数删除。
            
    这里我抄一下书上的例子:
   
#include  ----
    int main()
    {
        char name[L_tmpnam], line[MAXLINE]
        FILE *fp;
            
        printf("%s\n",tmpnam(NULL));

        tmpnam(name);
        printf("%s\n",name);

        if((fp=tmpfile()) == NULL)
            err_sys("tmpfile error");
        fputs("one line of output\n",fp);
        rewind(fp);
        if(fgets(line,MAXLINE,fp) == NULL)
            err ....
        fputs(line,stdout);

        exit(0);
    }


    编译时候出现了一句话:
        warning: the use of `tmpnam' is dangerous, better use `mkstemp'。
        上网查了下:
        原因一、    Race conditions: tmpnam() generates a file name that is not in use at the moment of the call, but there's no guarantee that some other program might not create such a file two nanoseconds later, before you get a chance to use the name tmpnam() built for you.
        原因二、  Security holes: It's at least conceivable that the race condition mentioned above could be exploited as part of a penetration of privilege barriers.
        原因三、  Disk pollution: When you create a file using the name tmpnam() gave you, you must remember to remove() it when you're through (assuming you want it to be temporary). If your program crashes or is stopped by ^C or some such and you don't remove() the file, it will hang around on the disk and take up space. This could become troublesome, especially if the "temporary" files tend to be large.

    运行结果是:
        $ ./a.out
        /tmp/fileT0Hsu6
        /tmp/filekmAsYQ
        one line of  output
    
    还有两个函数用于创建临时文件:
        char *mkstemp(char *) 和 int mkdtemp(char *)
        一个返回路径,一个返回文件描述符。
        两个创建文件后都不会像 tmpfile 一样删除文件,而是要靠我亲手来删掉。。。


最后书上提到了标准I/O的替代软件:
    当我们使用 fgets fputs时候,通常需要复制两次数据,一次在内核和标准I/O缓冲区之间(调用read和write时),第二次在标准I/O缓冲区和用户程序中的行缓冲区之间。
    快速I/O库 则是 使读一行的函数返回指向该行的指针,而不是将该行复制到另一个缓冲区
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值