APUE学习笔记——5.5~5.7数据流的打开与读写

1.open

#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);
                                    --All three return: file pointer if OK,NULLon error

    fopen:用于打开一个特定的文件
    freopen:在特定的流上打开特定的文件,主要用于实现重定向功能。如果流已经被打开,则先关闭它,如果流已经被含有定向设置,则清除它,重新进行设置。经常用在标准输入流、标准输出流、标准错误流的操作。(eg:标准输出重定向到一个文件)
    fdopen:使打开的文件描述符与标准I/O流向结合。这里的文件描述符可能来自open、dup、dup2、pipe、fcntl等。很多文件只能由网络通信或者创建管道获得的文件描述符,无法直接使用fopen函数。这些特殊的文件打开后就需要与标准I/O流向结合,因此需要fdopen函数。
    关于type的取值如Figure5.2所示

example:

关于freopen
#include<stdio.h>
int
main()
{
        /*redirect standard output to file test.out*/
        if(freopen("./test.out","w",stdout)==NULL)
                fprintf(stderr,"errorredirectingstdout\n");
        /*this output will go to a file test.out*/
        printf("Hello World\n");
        /*close the standard output stream*/
        fclose(stdout);
        return 0;
}


printf函数的输出   Hello World会被重定向到test.out中,而不是直接在终端显示。
关于fdopen
#include<stdio.h>
int
main()
{
        FILE *fp = fdopen(0,"w+");
        fprintf(fp,"%s\n", "Hello world!");
        fp =fdopen(1,"w+");
        fprintf(fp,"%s\n","Hello world!");
        fclose(fp);
}

输出 
         Hello world!
         Hello world!
首先将标准输入流与标准I/O流的"w+"结合,这样就可以用fprintf对标准输入流写入数据Hello world!
然后将 标准输出流与标准I/O流的"w+"结合, 这样就可以用fprintf对标准输出流写入数据Hello world!(在此之前,标准输入流的数据Hello world会被先刷新输出。)    

2.read and write

    非格式化的数据读写操作可以分为三种类型(这里的非格式化是针对scanf和printf系这样的格式化读写而言)
    ~一次一个字符
    ~一次一行 字符
    ~直接I/O:
Input function:       
#include <stdio.h>
int getc(FILE *fp);
int fgetc(FILE *fp);
int getchar(void);
                        All three return: next character if OK,EOFon end of file or error

  getchar()相当于fp为标准输入的getc,也就是getc()(stdin)
         getc和fgetc的区别在于fgetc()一定是一个函数,而getc()则一般被实现为一个宏,也就是说getc的定义可能是这样的:
#define getc(_fp) _IO_getc (_fp)

         因为getc是一个宏,那么我们就不能使用带副作用的表达式作为它的参数,比如++i。设想如果我们使用++i作为参数,结果将如何。编译器会把函数中所有的_fp替换成++i,这样++i在函数中就可能被调用多次,也就是发生了多次的自加,结果显然不是我们需要的。
         宏的优点在于效率,函数调用需要花费时间。

    这三个函数在返回下一个字符时,都会将它从unsigned char 转化为 int。之所以制定为unsigned 是为了防止在高位为1时变成负数。

输入判断
#include <stdio.h>
int ferror(FILE *fp);
int feof(FILE *fp);
                     Both return: nonzero(true) if condition is true, 0 (false) otherwise
void clearerr(FILE *fp);


          一般来说,每个流都包含两个标志:
          •Anerror flag
          •Anend-of-file flag
         调用 clearerr可以将他们清除
#include <stdio.h>
int ungetc(intc,FILE *fp);
                 Returns:cif OK,EOFon error


          从流中读取数据后, ungetc 可以将char再push back回去
         ungetc将一个字节退回输入流,如果该字节没有被读出来,则不能再将其它字节pushback到输入流中。

Output function
    
#include <stdio.h>
int putc(intc,FILE *fp);
int fputc(intc,FILE *fp);
int putchar(intc);
                                            All three return:cif OK,EOFon error

关键点与Input function类似。

行缓冲 Line-at-a-Time I/O

Input
#include <stdio.h>
char *fgets(char *restrict buf,int n,FILE *restrict fp);
char *gets(char *buf);
                            Both return:buf if OK,NULL on end of file or error

          fgets表示从流fp中读取n字节数据到buf中。buf中的最后一个字符为null。在读数据的时候,换行符也会被读入。因此,fgets读取的数据应该<=n-1.(包括换行符)。     也就是我们如果在输入流中输入Hello然后回车结束,且n不小于7,那么buf中的数据就是
“Hello”+回车符+空字符。
         gets()不应该被使用。因为他没有指明buf的上限,因此可以无限读取。程序员需要确保buffer足够大,以便执行读操作是不会溢出。如果数据超过buffer的大小,那么就会有数据被覆盖,导致读取的数据出错。因此,gets()函数仅供娱乐。
          当输入的line比n-1大时,那么只有n-1个字符被读取,其它的字符等待其它fgets读:
example:
#include<stdio.h>
#define BUFFSIZE 4096
int
main()
{
        char buf[BUFFSIZE];
        fgets(buf,5,stdin);
        puts(buf);
        fgets(buf,10,stdin);
        puts(buf);
        return 0;
}
~     

运行结果:

windeal@ubuntu:~/Windeal/apue$ ./exe 
Hello world!
Hell
o world!

Output
#include <stdio.h>
int fputs(const char *restrict str,FILE *restrict fp);
int puts(const char *str);
                                  Both return: non-negative value if OK,EOFon error

   fputs就是输出str中的字符。
    puts()虽然不会像gets那么不安全,但是一般也不使用,因为它会在字节流后加入换行符,给我们一些情况造成困扰。


二进制文件读写

         二进制I/O,通常用来一次性读写结构化的数据块。(因为有时候我们要读写的数据中包含换行符或者Null字符,无法使用fgets或fputs,而使用getc和putc又需要花费太多的实效,效率太低,因此需要二进制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);
                                              Both return: number of objects read or written
 参数:
  (1)ptr:是一个指针,对fread来说,它是读入数据的存放地址。对fwrite来说,是要输出数据的地址。
  (2)size:要读写的字节数;
  (3)nobj:要进行读写多少个size字节的数据项;
  (4)fp:文件型指针。
Example
float  data[10];
if (fwrite(&data[2], sizeof(float), 4, fp) != 4)
err_sys("fwrite error");

将data[2]到data[5]的数据写入fp的文件
struct {
short  count;
long  total;
char  name[NAMESIZE];
}item;
if (fwrite(&item, sizeof(item), 1, fp) != 1)
err_sys("fwrite error");
Here, we specifysizeas the size of structur


将一个item对象的数据写入fp


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值