Linux中的标准文件I/O流

1.fopen

表头文件

include<stdio.h>

定义函数

FILE * fopen(const char * path,const char * mode);

函数说明

参数path字符串包含欲打开的文件路径及文件名,参数mode字符串则代表着流形态。
mode有下列几种形态字符串:
r 打开只读文件,该文件必须存在。
r+ 打开可读写的文件,该文件必须存在。
w 打开只写文件,若文件存在则文件长度清为0,即该文件内容会消失。若文件不存在则建立该文件。
w+ 打开可读写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。
a 以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。
a+ 以附加方式打开可读写的文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾后,即文件原先的内容会被保留。
上述的形态字符串都可以再加一个b字符,如rb、w+b或ab+等组合,加入b 字符用来告诉函数库打开的文件为二进制文件,而非纯文字文件。不过在POSIX系统,包含Linux都会忽略该字符。由fopen()所建立的新文件会具有S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH(0666)权限,此文件权限也会参考umask值。

返回值

文件顺利打开后,指向该流的文件指针就会被返回。若果文件打开失败则返回NULL,并把错误代码存在errno 中。

注意:

一般而言,开文件后会作一些文件读取或写入的动作,若开文件失败,接下来的读写动作也无法顺利进行,所以在fopen()后请作错误判断及处理。


2.fread函数和fwrite函数

1.函数功能

用来读写一个数据块。

2.一般调用形式

fread(buffer,size,count,fp);

fwrite(buffer,size,count,fp);

3.说明

(1)buffer:是一个指针,对fread来说,它是读入数据的存放地址。对fwrite来说,是要输出数据的地址。

(2)size:要读写的字节数;

(3)count:要进行读写多少个size字节的数据项;

(4)fp:文件型指针。

注意:1 完成一次写操(fwrite())作后必须关闭流(fclose()),因为fwrite写数据后,数据只是到了缓冲区,而没有真正的写在了文件里,等fclose执行后,fclose函数隐含包含了一次fflush操作,把缓冲区内的数据冲洗到终端;

2 完成一次读操作(fread())后,如果没有关闭流(fclose()),则指针(FILE * fp)自动向后移动前一次读写的长度,不关闭流继续下一次读操作则接着上次的输出继续输出;fread()用来从文件流中读取数据。参数fp为已打开的文件指针,参数ptr 指向欲存放读取进来的数据空间,读取的字符数以参数size*nmemb来决定。Fread()会返回实际读取到的nmemb数目,如果此值比参数nmemb 来得小,则代表可能读到了文件尾或有错误发生,这时必须用feof()或ferror()来决定发生什么情况。

fwrite()用来将数据写入文件流中。参数fp为已打开的文件指针,参数buffer指向欲写入的数据地址,总共写入的字符数以参数size*nmemb来决定。Fwrite()会返回实际写入的nmemb数目。


3 fprintf() :

#include <stdio.h>
int fprintf( FILE *stream, const char *format, ... );

函数功能:把格式化的数据写入某个字符串
函数原型:int sprintf( char *buffer, const char *format [, argument] … );
返回值:字符串长度(strlen)

    char name[20] = "Mary";
    FILE *out;
    out = fopen( "output.txt", "w" );
    if( out != NULL )
    fprintf( out, "Hello %s\n", name );

格式化规定符

 
  %d 十进制有符号整数  
  %u 十进制无符号整数  
  %f 浮点数  
  %s 字符串  
  %c 单个字符  
  %p 指针的值  
  %e 指数形式的浮点数  
  %x, %X 无符号以十六进制表示的整数  
  %0 无符号以八进制表示的整数  
  %g 自动选择合适的表示法  

按格式输入到流,其原型是int fprintf(FILE *stream, const char *format[, argument, ...]);其用法和printf()相同,不过不是写到控制台,而是写到流罢了。注意的是返回值为此次操作写入到文件的字节数。如int c = fprintf(fp, "%s %s %d %f", str1,str2, a, b) ;str1:10字节;str2: 10字节;a:2字节;b:8字节,c为33,因为写入时不同的数据间自动加入一个空格。


4.fclose

函数原型:int fclose(FILE *stream)

功能:关闭一个文件流,使用fclose就可以把缓冲区内最后剩余的数据输出到磁盘文件中,并释放文件指针和有关的缓冲区
返回值

若关文件动作成功则返回0,有错误发生时则返回EOF并把错误代码存到errno。


fopen /open区别

UNIX环境下的C 对二进制流文件的读写有两套班子:1) fopen,fread,fwrite ; 2) open, read, write
这里简单的介绍一下他们的区别。

  1. fopen 系列是标准的C库函数;open系列是 POSIX 定义的,是UNIX系统里的system call。
    也就是说,fopen系列更具有可移植性;而open系列只能用在 POSIX 的操作系统上。
  2. 使用fopen 系列函数时要定义一个指代文件的对象,被称为“文件句柄”(file handler),是一个结构体;而open系列使用的是一个被称为“文件描述符” (file descriptor)的int型整数。
  3. fopen 系列是级别较高的I/O,读写时使用缓冲;而open系列相对低层,更接近操作系统,读写时没有缓冲。由于能更多地与操作系统打交道,open系列可以访问更改一些fopen系列无法访问的信息,如查看文件的读写权限。这些额外的功能通常因系统而异。
  4. 使用fopen系列函数需要"#include <sdtio.h>";使用open系列函数需要"#include <fcntl.h>" ,链接时要之用libc(-lc)
    小结:
    总的来说,为了使程序获得更好的可移植性,未到非得使用一些fopen系列无法实现的功能的情况下,fopen系列是首选。

read/write和fread/fwrite区别

1,fread是带缓冲的,read不带缓冲.
2,fopen是标准c里定义的,open是POSIX中定义的.
3,fread可以读一个结构.read在linux/unix中读二进制与普通文件没有区别.
4,fopen不能指定要创建文件的权限.open可以指定权限.
5,fopen返回指针,open返回文件描述符(整数).
6,linux/unix中任何设备都是文件,都可以用open,read.
如果文件的大小是8k。
你如果用read/write,且只分配了2k的缓存,则要将此文件读出需要做4次系统调用来实际从磁盘上读出。
如果你用fread/fwrite,则系统自动分配缓存,则读出此文件只要一次系统调用从磁盘上读出。
也就是用read/write要读4次磁盘,而用fread/fwrite则只要读1次磁盘。效率比read/write要高4倍。
如果程序对内存有限制,则用read/write比较好。
都用fread 和fwrite,它自动分配缓存,速度会很快,比自己来做要简单。如果要处理一些特殊的描述符,用read 和write,如套接口,管道之类的
系统调用write的效率取决于你buf的大小和你要写入的总数量,如果buf太小,你进入内核空间的次数大增,效率就低下。而fwrite会替你做缓存,减少了实际出现的系统调用,所以效率比较高。
如果只调用一次(可能吗?),这俩差不多,严格来说write要快一点点(因为实际上fwrite最后还是用了write做真正的写入文件系统工作),但是这其中的差别无所谓。

fclose与close的区别

C FILE *流在内部使用缓冲I / O. fclose()刷新此缓冲区,然后在OS级别关闭文件描述符。close()'FILE *流可能无法刷新此内部缓冲区,您可能会丢失数据。因此对于C流总是使用C fxxx()函数
fclose函数隐含包含了一次fflush操作,把缓冲区内的数据冲洗到终端(因为标准I/O的操作对文件有缓冲,所以需要刷新缓冲区,close没有这个操作,也没有必要刷新的操作,因为对应的open,write,read没有缓冲)


在标准IO中,对于文件的读写无非三种方式:

(1) 每次一个字符的I / O。
(2) 每次一行的I / O。
(3) 直接I / O。

似乎这些函数看起来很简单,但只有理解了这些函数,你才知道该如何使用,例如怎样来判断读取的文件是否结束,怎样来统计文件的行数,等等。

下面先来回顾下读取文件的三种方式,然后再举例看下怎样使用。

一次读或写一个字符,使用getc、fgetc或getchar。如果流是带缓存的,则标准I / O函数处理所有缓存。三个函数原型如下:

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

三个函数的返回:若成功则为下一个字符,若已处文件尾端或出错则为EOF。强调的是不管是出错还是到达文件尾端,这三个函数都返回同样的值。为了区分这两种不同的情况,必须调用ferror()或feof()。

一次读或写一行,使用fgets和gets。两个函数原型如下:

        #include <stdio.h>
        Char *gets(char *s) ;
        Char *fgets(char *s,int size,FILE *steam)

两个函数返回:若成功则为buf,若已处文件尾端或出错则为null。这两个函数都指定了缓存地址,读入的行将送入其中。gets()从标准输入读,而fgets()则从指定的流读。

对于fgets(),必须指定缓存的长度为n。此函数一直读到下一个新行符为止,但是不超过n-1个字符,读入的字符被送入缓存,该缓存以null字符结尾。如若该行,包括后一个新行符的字符数超过n-1,则只返回一个不完整的行,而且缓存总是以null字符结尾。对fgets()的下一次调用会继续读该行。

直接I / O使用的是fread。每次I / O操作读某种数量的对象,而每个对象具有指定的长度。这个函数常用于从二进制文件中读一个结构。原型如下:
int fread(void * ptr,int objsize,int objnum,FILE *fp);

这个函数返回:读的对象数。fputs()在遇到null字节时就停止,而在结构中可能含有null字节,所以不能使用每次一行函数实现这种要求,而fread能使我们一次读整个结构。

下面看下如何利用上面提到的标准IO读函数来判断你读取的文件是否结束以及如何来统计文件的行数。

1、判断文件结束有三种方式:

a) 当用fgetc(src)返回的int值为EOF时,读到文件的末尾
b) 当用fgets(p,1024,src)返回的指针为NULL时,读到文件的末尾
c) 当用fread(s,1,20,src)返回的个数小于20时,读到文件的末尾

实现起来简单明了,就不再细说。

2、统计文件的行数

a) 当用fgetc(src)返回的int值为’\n’时行数加一。
b) 当用fgets(p,1024,src)返回的指针不为NULL时,你的缓冲即提供的p[1024]已经读到字符,这个时候实际上有两种情况,一种你读进来的字符长度小于1024个,这个时候一行结束,行数加一,另一种比较复杂,由于受自己定义的缓冲的限制,一次只能读进来1023个,如果恰好这个时候你刚好读完一行,那p[]1022]里存放的一定’\n’,如果不是,那你的一行还一定没有结束了。

程序如下:
Int n;
A) While(fgetc(src)’\n’)
N++;
B) while(fgets(p,1024,src)!=NULL)
{
If(strlen(p)<1024||p[1022]
’\n’)
N++;
}

标准I/O操作函数

1)打开文件

打开文件有3个标准函数,分别为fopen()、fdopen()和freopen()。它们可以不同的模式打开,但都返回一个指向FILE的指针,该指针指向对应的I/O流。此后,对文件的读写都是通过这个FILE指针来进行的。其中,fopen()函数可以指定打开文件的路径和模式,fdopen()函数可以指定打开的文件描述符和模式,而freopen()函数除可指定打开的文件、模式外,还可指定特定的I/O流。

fopen()函数语法要点

其中,mode类似于open()函数中的flag,可以定义打开文件的访问权限等,表2.17说明了fopen()中mode的各种取值。

mode取值说明


注意:在每个选项中加入b字符用来告诉函数库打开的文件为二进制文件,而非纯文本文件。不过在Linux系统中会自动识别不同类型的文件而将此符号忽略。

fdopen()函数语法要点

freopen()函数语法要点

2)关闭文件

关闭标准流文件的函数为fclose(),该函数将缓冲区内的数据全部写入到文件中,并释放系统所提供的文件资源。

fclose()函数语法要点

3)读文件

在文件流被打开后,可对文件流进行读写等操作,其中,读操作的函数为fread()。

fread()函数语法要点

4)写文件

fwrite()函数用于对指定的文件流进行写操作。
fwrite()函数语法要点

文件打开之后,根据一次读写文件中字符的数目可分为字符输入/输出、行输入/输出和格式化输入/输出,下面分别对这3种不同的方式进行讨论。

1)字符输入/输出

字符输入/输出函数一次仅读写一个字符。其中字符输入/输出函数的语法如下表所示:

字符输入函数语法

字符输出函数语法

这几个函数功能类似,其区别仅在于getc()和putc()通常被实现为宏,而fgetc()和fputc()不能实现为宏,因此,函数的实现时间会有所差别。
下面这个实例结合fputc()和fgetc(),将标准输入复制到标准输出中。

 /*fput.c*/
    #include<stdio.h>
    main()
    {
        int c;
        /* 把fgetc()的结果作为fputc()的输入 */
        fputc(fgetc(stdin), stdout);
    }
输出结果:
  $ ./fput
    w(用户输入)
    w(屏幕输出)

2)行输入/输出

行输入/输出函数一次操作一行,其中行输入/输出函数语法要点如下表所示:

行输入函数语法

行输出函数语法

这里以gets()和puts()为例进行说明,本实例将标准输入复制到标准输出中,如下所示:

    /*gets.c*/
    #include<stdio.h>
    main()
    {
        char s[80];
        fputs(fgets(s, 80, stdin), stdout);
    }

    运行该程序,结果如下所示:

    $ ./gets
    This is stdin(用户输入)
    This is stdin(屏幕输出)

3)格式化输入/输出

格式化输入/输出函数可以指定输入/输出的具体格式,这里有读者已经非常熟悉的printf()、scanf()等函数,这里简要介绍一下它们的格式,如下表所示:

格式化输出函数1

格式化输出函数2

格式化输入函数

posted @ 2019-07-19 10:48  独孤剑—宇枫  阅读(2510)  评论(1编辑  收藏  举报