C文件IO

ANSI C标准几乎被所有的操作系统支持,ANSI C标准提供了完善的I/O函数,使用这些I/O操作我们可以控制程序的输入输出、读写系统磁盘文件。本文记录了用户进程I/O缓冲介绍、文件的读写、文件定位操作等内容。

库函数与系统调用

文件是位于磁盘上的,如何在运行的程序(进程)中控制文件的读写,通过下面的这张图,我们可以看到应用程序如何控制系统资源(包括磁盘中的文件)的大概的原理。

操作系统帮助我们管理硬件资源,封装底层实现,以接口的形式(系统调用函数)供上层应用程序调用。直接使用操作系统提供的系统调用函数我们就可以控制文件的读写,但是一般我们在应用程序中不提倡这样做,因为会带来性能上的问题。应用程序调用系统调用函数的时候,操作系统会从用户态转变成内核态,完成调用后,再从内核态转成用户态(Linux中是通过软中断实现的),而频繁的系统状态切换是需要开销的,会给用户应用程序带来性能上的问题。另外就是可移植性的问题,每个操作系统向外提供的接口是不尽相同的,如果直接在应用程序中使用系统调用函数,那么程序的可移植性将会很差。在ANSI
C标准中为我们提供了标准函数(库函数)去操作系统调用函数,库函数是一些能够完成特定功能的函数,一般由某个标准组织发布,并形成一种公认的标准,所以使用库函数可以屏蔽操作系统间对外接口的差异。

下面是网友总结的库函数和系统调用函数之间的差异

缓冲和非缓冲

对于文件的访问操作,可以按照是否使用缓冲区,分为缓冲文件操作和非缓冲文件操作

缓冲文件操作:高级文件操作,如第一幅图用户应用程序的左分支,在用户进程空间为打开的文件分配缓冲区。ANSI C标准就是使用的这种文件操作。本文讨论的正是这种。

非缓冲文件操作:低级文件操作,如第一幅图用户应用程序的右分支,在用户进程空间不设文件缓冲区。POSIX C标准的I/O就是使用的非缓冲文件操作。

说明:这里所说的缓冲全部指的是用户进程空间的文件缓冲,即使是非缓冲的文件操作,在内核部分也是使用了多级缓冲,而不是直接访问磁盘(可以参考操作系统的存储器结构)。

文件与文件流

流是一种抽象的数据结构,是ANSI C用来高效的管理打开了的文件信息的,在实际编程中的体现就是struct FILE结构体,在stdio.h头文件中定义,流对象最重要的机制就是缓冲区和格式转换。在Linux系统中系统默认为每个进程打开3个文件,对应3个流就是标准输入流、标准输出流、标准错误流。除此之外,需要用到的其他文件需要自己打开和关闭。

文件的I/O操作

按照读/写的对象、可以分为按字符、行、块、格式化等几种读写操作。

(1)按字符读/写文件流

下面的这段程序是使用fgetc和fputc来读取指定文件中的内容到标准输出(显示器),运行时需要指定一个文件

#include<stdio.h>

int main(int argc,char *argv[])
{
    FILE *fp=NULL;
    char ch;
    if(argc<=1)
    {
        printf("check usage of %s \n",argv[0]);
        return -1;
    }
    if((fp=fopen(argv[1],"r"))==NULL)
    {
        printf("can not open %s\n",argv[1]);
        return -1;
    }
    while ((ch=fgetc(fp))!=EOF)
        fputc(ch,stdout);
    fclose(fp);
    return 0;
}

运行程序:

(2)按行读/写文件流

下面是一个使用fgets和fputs实现上述功能的程序

#include<stdio.h>
int main(int argc,char *argv[])
{
    FILE *fp=NULL;
    char str[20];
    if((fp=fopen(argv[1],"r"))==NULL)            //按只读的形式打开文件
    {
        printf("can not open!\n");
        return -1;
    }
    fgets(str,sizeof(str),fp);                          //从打开文件中读取sizeof(str)个字节到str中
    fputs(str,stdout);                                 //将str输出到标准输出
    fclose(fp);                                          //关闭文件
    return 0;
}

(3)按照块读写

下面是使用fread和fwrite来实现的按块读写的程序

#include<stdio.h>
int main(int argc,char *argv[])
{
    struct student
    {
        char name[10];
        int number;
    };
    FILE *fp=NULL;
    int i;
    struct student boya[2],boyb[2],*pp,*qq;
    if((fp=fopen("aa.txt","w+"))==NULL) //以可读写的方式打开文件;若该文件存在则清空,若不存在就创建
    {  //打开文件失败
        printf("can not open!\n");
        return -1;
    }
    pp=boya;
    qq=boyb;
    printf(“please input two students‘ name and number:\n");
    for (i=0;i<2;i++,pp++)
    scanf("%s\%d",pp->name,&pp->number);
    pp=boya;
    fwrite(pp,sizeof(struct student),2,fp); //将从键盘输入的信息写入到文件流fp中
    rewind(fp); //将读写位置定位到文件头
    fread(qq,sizeof(struct student),2,fp); //从文件流fp中读两个结构体到qq
    printf("name\t\t number\n");
    for(i=0;i<2;i++,qq++) //输出qq中的内容
        printf("%s\t\t %d\n",qq->name,qq->number);
    fclose(fp);
    return 0;
}

(4)按照格式化读/写

下面这段程序是使用sprintf和sscanf进行文件读/写操作

#include<stdio.h>

int main()
{
        FILE *fp = NULL;
        int i = 20;
        char ch = 'D';
        if((fp=fopen("f2","r+"))==NULL)
        {
                printf("open file f2 failed.");
                return -1;
        }

        fprintf(fp,"%d:%c\n",i,ch);  //按照指定格式写文件

        int new_i = 0;
        char new_ch;

        rewind(fp);  //使读写指针归位

        fscanf(fp,"%d:%c\n",&new_i,&new_ch);  //按照指定格式读文件

        printf("new_i=%d, new_ch=%c\n",new_i,new_ch);

        fclose(fp);

        return 0;
}

程序运行结果:

再说缓冲区

可以发现上述4中形式的读写操作,都没有指明缓冲区,但是它们都使用到了位于用户进程空间的文件缓冲区,这是因为,对于任意的流,如果没有指明其缓冲区的类型,系统将指定默认类型的缓冲区。如果用户希望自己指定缓冲区,可以使用setbuf( )或者setvbuf( )函数指定,这两个函数的声明如下:

extern  void  setbuf ( 流对象, 缓冲区);

如果将缓冲区设置为NULL,则关闭缓冲区。

extern  int  setvbuf (流对象,缓冲区, 模式,缓冲区大小)

setvbuf比setbuf更加灵活,其中模式可取值有0、1、2,分别表示全缓冲、行缓冲、无缓冲,如果模式是2,那么将会忽视第二和第四个参数。

下面是一个修改缓冲区的程序:

/* Example show usage of setbuf() &setvbuf() */
#include<stdio.h>
#include<error.h>
#include<string.h>
int main( int argc , char ** argv )
{
	int i;
	FILE * fp;
	char msg1[]="hello,wolrd\n";
	char msg2[] = "hello\nworld";
	char buf[128];

//open a file and set nobuf(used setbuf).and write string to it,check it before close of flush the stream
	if(( fp = fopen("no_buf1.txt","w")) == NULL)
	{
		perror("file open failure!");
		return(-1);
	}
	setbuf(fp,NULL);
	memset(buf,'\0',128);
	fwrite( msg1 , 7 , 1 , fp );
	printf("test setbuf(no buf)!check no_buf1.txt\n");
	printf("now buf data is :buf=%s\n",buf);

	printf("press enter to continue!\n");
	getchar();
	fclose(fp);

//open a file and set nobuf(used setvbuf).and write string to it,check it before close of flush the stream
	if(( fp = fopen("no_buf2.txt","w")) == NULL)
	{
		perror("file open failure!");
		return(-1);
	}
	setvbuf( fp , NULL, _IONBF , 0 );
	memset(buf,'\0',128);
	fwrite( msg1 , 7 , 1 , fp );
	printf("test setvbuf(no buf)!check no_buf2.txt\n");

	printf("now buf data is :buf=%s\n",buf);

	printf("press enter to continue!\n");
	getchar();
	fclose(fp);

//open a file and set line buf(used setvbuf).and write string(include '\n') to it,
//
//check it before close of flush the stream
	if(( fp = fopen("l_buf.txt","w")) == NULL)
	{
		perror("file open failure!");
		return(-1);
	}
	setvbuf( fp , buf , _IOLBF , sizeof(buf) );
	memset(buf,'\0',128);
	fwrite( msg2 , sizeof(msg2) , 1 , fp );
	printf("test setvbuf(line buf)!check l_buf.txt, because line buf ,only data before enter send to file\n");

	printf("now buf data is :buf=%s\n",buf);
	printf("press enter to continue!\n");
	getchar();
	fclose(fp);

//open a file and set full buf(used setvbuf).and write string to it for 20th time (it is large than the buf)
//check it before close of flush the stream
	if(( fp = fopen("f_buf.txt","w")) == NULL){
		perror("file open failure!");
		return(-1);
	}
	setvbuf( fp , buf , _IOFBF , sizeof(buf) );
	memset(buf,'\0',128);
	fwrite( msg2 , sizeof(msg2) , 1 , fp );
	printf("test setbuf(full buf)!check f_buf.txt\n");

	printf("now buf data is :buf=%s\n",buf);
	printf("press enter to continue!\n");
	getchar();

	fclose(fp);

}

其他文件操作

下面介绍一些在文件操作中常用的函数

打开、关闭文件操作fopen和fclose

fp = fopen(文件名,文件操作方式);

文件的操作方式有以下这些

关闭文件使用fclose(文件指针); 或者 fcloseall()函数。

文件流检测

extern  int  feof(流对象)

用于判断流对象是否读到文件尾部,如果是返回1,否则,返回0;

extern  int   ferror(流对象)

用于判断流对象是否出现了错误,若没有错误,则返回0,否则,返回非0。

extern  long  int  ftell(流对象)

返回当前读写位置距离文件开头位置的字节数,若执行失败,返回-1

extern  int  fseek(流对象,偏移距离,基准位置)

基准位置可取值有0、1、2分别表示文件开头、当前位置、文件结尾

将读写位置移到,距离基准位置偏移距离处。若成功,返回0;否则,返回,-1

extern  void  rewind(流对象)

将读写位置重置到文件开始处。

时间: 2024-10-18 10:59:31

C文件IO的相关文章

第七篇:两个经典的文件IO程序示例

前言 本文分析两个经典的C++文件IO程序,提炼出其中文件IO的基本套路,留待日后查阅. 程序功能 程序一打印用户指定的所有文本文件,程序二向用户指定的所有文本文件中写入数据. 程序一代码及其注释 1 #include <iostream> 2 #include <fstream> // 使用文件处理对象记着要包含这个头文件 3 #include <string> 4 #include <vector> 5 6 using namespace std; 7

MySQL系列:innodb引擎分析之文件IO

innodb作为数据库引擎,自然少不了对文件的操作,在innodb中所有需要持久化的信息都需要文件操作,例如:表文件.重做日志文件.事务日志文件.备份归档文件等.innodb对文件IO操作可以是煞费苦心,其主要包括两方面,一个是对异步io的实现,一个是对文件操作管理和io调度的实现.在MySQL-5.6版本的innodb还加入了DIRECT IO实现.做了这么多无非是优化io操作的性能.在innodb的文件IO部分中,主要实现集中在os_file.*和fil0fil.*两个系列的文件当中,其中o

Linux下用文件IO的方式操作GPIO(/sys/class/gpio)(转)

通过sysfs方式控制GPIO,先访问/sys/class/gpio目录,向export文件写入GPIO编号,使得该GPIO的操作接口从内核空间暴露到用户空间,GPIO的操作接口包括direction和value等,direction控制GPIO方向,而value可控制GPIO输出或获得GPIO输入.文件IO方式操作GPIO,使用到了4个函数open.close.read.write. 首先,看看系统中有没有"/sys/class/gpio"这个文件夹.如果没有请在编译内核的时候加入 

(二) 一起学 APUE 之 文件 IO

. . . . . 昨天我们讨论了标准 IO,今天主要说说系统 IO. 1.文件描述符 在 SYSIO 中贯穿了一个整形数,它就是文件描述符.对内核而言,所有打开的文件都是通过文件描述符引用的.它的作用与 STDIO 中的 FILE 结构体类似,但是它们的工作原理是完全不同的.它实际上是一个由内核保存的数组下标,所以不会是负数,下面我会用一张图来表示它的作用. 图1 SYSIO 文件描述符 图是在 Ubuntu 下好不容易找到了一个画图软件画的,质量不怎么样,小伙伴们先凑合着看吧. 我解释下图上

(一)一起学 APUE 之 文件 IO

. . . . . 最近在学习 APUE,所以顺便将每日所学记录下来,一方面为了巩固学习的知识,另一方面也为同样在学习APUE的童鞋们提供一份参考. 本系列博文均根据学习<UNIX环境高级编程>一书总结而来,如有错误请多多指教. APUE主要讨论了三部分内容:文件IO.并发.进程间通信. 文件IO: 标准IO:优点是可移植性高,缺点是性能比系统 IO 差,且功能没有系统 IO 丰富. 系统IO:因为是内核直接提供的系统调用函数,所以性能比标准 IO 高,但是可移植性比标准 IO 差. 并发:

0723------Linux基础----------文件 IO 之 dup、dup2 和 fcntl 函数

1. dup 函数 1.1 dup 函数用来复制一个文件描述符,复制后的文件描述符可以正常使用(见例1).dup函数返回当前文件描述符表中一个最小的可用的文件描述符(Linux下分配文件描述符的规则是:寻找最小可用),这个过程由系统来完成.dup函数成功执行后,两个文件描述符fd_1 和 fd_2 指向同一个文件表项,因它们共享偏移量(文件数据结构图见Unix环境高级编程),在内核中的数据结构表示为:1个进程表项,1个文件表项(这里两个文件描述符指向同一个文件表项),1个V结点.文件表项中有一个

imx6用文件io操作gpio

具体请参考: http://blog.csdn.net/u014213012/article/details/53140781 这里要注意的是: 要让linux支持文件io方式操作gpio,首先驱动必须得支持,也就是说设备树上必须先配置好gpio模式,然后参照以上链接去实现gpio操作 这里举例来说:hud项目中(imx6dl平台),有一个蓝牙电源的使能受GPIO1_IO30的控制,所以我们必须得在设备树上配置这个pad为GPIO模式 1.配置gpio模式 现在需要在设备树上配置GPIO1_IO

TLPI(liunx/unix系统编程手册)笔记(三) 文件IO:通用的IO模型

读下来总的就是介绍了四个IO的API--open,read,write,close. 大家都是知道的,everything is file,在linux系统里面一切都是看作文件来操作的,学习linux就得先学好文件IO,也可以看见TLPI这本书的介绍完一些概念之后就开始介绍文件IO了. IO,大概的分为磁盘文件IO,buffering(缓冲)IO.貌似缓冲的水很深,之后会写博客. ------------------------------------ (1)文件描述符. 在进行IO操作之前,总

linux 文件IO

1.文件描述符 (1)文件描述符的本质是一个数字,这个数字本质上是进程表中文件描述符表的一个表项,进程通过文件描述符作为index去索引查表得到文件表指针,再间接访问得到这个文件对应的文件表.(2)文件描述符这个数字是open系统调用内部由操作系统自动分配的,操作系统分配这个fd时也不是随意分配,也是遵照一定的规律的,我们现在就要研究这个规律.(3)操作系统规定,fd从0开始依次增加.fd也是有最大限制的,在linux的早期版本中(0.11)fd最大是20,所以当时一个进程最多允许打开20个文件

标准IO与文件IO 的区别【转】

本文转载自:http://blog.sina.com.cn/s/blog_63f31f3401013jrn.html 先来了解下什么是标准IO以及文件IO. 标准IO:标准I/O是ANSI C建立的一个标准I/O模型,是一个标准函数包和stdio.h头文件中的定义,具有一定的可移植性.标准IO库处理很多细节.例如缓存分配,以优化长度执行IO等.标准的IO提供了三种类型的缓存. (1)全缓存:当填满标准IO缓存后才进行实际的IO操作.         (2)行缓存:当输入或输出中遇到新行符时,标准