Linux系统标准I/O(5.29)

/**********8****************
 *1.linux中一切皆文件
 *2.两种开发思路:
 *  A.基于库的开发
 *  B.基于系统调用的开发
 ****************************/

/*-----------------day - 1 基于库的开发----------------------------*/
一、流:
将数据输入输出(传送过程)抽象为数据流,也叫流/*{{{*/
标准I/O库的所有操作都是围绕流(stream)来进行的。
标准IO库把一个打开的文件模型化为一个流
流的描述:在标准I/O中,流用FILE *来描述:
FILE *本质上是对应保存一个打开文件信息的内存区域首地址
FILE *结构体指针,也称为流指针,文件指针


三个默认打开的文件:标准输入文件    标准输出文件    标准出错文件
对应流指针          stdin           stdout          stderr/*}}}*/

二、格式化输出函数簇printf()/fprintf()/sprintf()

       #include <stdio.h>

       //向标准输出文件 输出格式字符串
       int printf(const char *format, ...);
       //向指定的文件(stdout,stderr通过流指针指定) 输出格式字符串
       int fprintf(FILE *stream, const char *format, ...);
       //向指定的内存区域 输出格式字符串
       int sprintf(char *str, const char *format, ...);

       例:将数字123转化为字符串"123"
       sprintf(buf,"%d",123);

       例:向标准输出 输出"hello world"
       printf("hello world");
       fprintf(stdout,"hello world");

       向标准出错输出 "stderr : hello world"
       fprintf(stderr,"stderr : hello world");

       向数组buf中输出 "string:hello world"
       sprintf(buf,"string:hello world");
练习-1:
       输出上述内容,并要求只显示标准输出的内容,和只显示标准出错的内容

#include <stdio.h>

int main(int argc, const char *argv[])
{

    //printf("hello world\n");
    printf("hello world");

//    fflush(stdout);
   // while(1);
    return 0;
}
#include <stdio.h>

int main(int argc, const char *argv[])
{

    printf("hello world");
    //sleep 1 seconds 
    sleep(1);
    
    //标准输入获得数据导致其他所有行缓冲刷新
    getchar();
    
    while(1);
    return 0;
}
#include <stdio.h>

int main(int argc, const char *argv[])
{

    //printf("hello world\n");
    write(1,"hello world\n",12);
    return 0;
}

三、标准IO缓冲机制


三种类型的缓冲:全缓冲、行缓冲、不缓冲/*{{{*/


全缓冲:特点缓存满时刷新,进行实际的IO写操作。stdio库对一般文件均使用全缓冲,
linux下ext4格式文件系统全缓冲默认大小为4k
    全缓冲刷新条件:
    1.缓冲区满时会刷新;
    2.程序正常结束时会刷新(即调用exit()函数)
    3.程序中调用fflush()函数会强制刷新


行缓冲:特点输入输出时遇'\n'会刷新。 stdio库对与终端相关联的文件使用行缓冲,
典型的有标准输入文件和标准输出文件。linux下行缓冲大小为1k


    行缓冲刷新条件
    1.缓冲区满时会刷新;
    2.程序正常结束时会刷新(即调用exit()函数)
    3.程序中调用fflush()函数会强制刷新;
    4.遇'\n'会刷新
/*{{{*/
    5.当从一个不缓存或行缓存文件获得数据,会造成所有行缓存刷新
/*}}}*/
不缓冲:特点不使用缓冲
出错不会缓冲,典型的标准出错文件不缓冲/*}}}*/


四、标准I/O相关函数
1.打开文件

       #include <stdio.h>/*{{{*/

       FILE *fopen(const char *path, const char *mode);
        功能:打开文件,获得流指针
        参数:
            @path:打开文件的路径,"/home/will/text.txt" ; 字符指针argv[1]
            @mode:打开文件的方式 "r" "r+" "w" "w+" "a" "a+"
            "r"   :只读方式打开,要求文件存在
            "r+"  :读写方式打开,要求文件存在

            "w"   :以只写方式打开,如果文件不存在则创建文件;如果文件存在,则将文件内容删除,长度清0
            "w+"  :以读写方式打开,如果文件不存在则创建文件;如果文件存在,则将文件内容删除,长度清0

            "a"   :以追加写方式打开,如果文件不存在则创建文件
            "a+"   :以读写方式打开,追加写方式,如果文件不存在则创建文件
        返回值:成功返回打开文件对应流指针;失败返回NULL,并且置errno

        例:打开主目录下的test.c,以只读方式打开,
        FILE *fp = fopen("/home/will/test.c","r");

        $./a.out ./test.c 

        FILE *fp = fopen(argv[1],"r");
练习-2:/*{{{*/
       1.以读写方式打开一个文件newfile,文件不存在则创建文件,存在则将文件内容清空
       2.向文件写入"fopen newfile success."
       3.查看newfile文件权限 ls -l/*}}}*/
#include <stdio.h>
#include <errno.h>
#include <string.h>
//argc 命令行参数的个数
//argv 命令参数的对应的字符串指针数组
//$./a.out a1 a2 

int main(int argc, const char *argv[])
{
    int i = 0;

    FILE * fp = NULL; 

    if( argc != 2)
    {
        printf("Usage: %s file\n",argv[0]);
        return -1;
    }
#if 0 
    for(;i < argc;i++)
        printf("argv[%d] = %s\n",i,argv[i]);
#endif
    //./a.out file
    //(fp= fopen(argv[1],"r")==NULL)
    if((fp = fopen(argv[1],"w")) == NULL)
    {
        //printf("fopen fail:%d\n",errno);
        //printf("fopen fail:%s\n",strerror(errno));
        //向指定的流输出信息
        //fprintf(fp,"foprn fail:%s\n",strerror(errno));
        //fprintf(stdout,"fopen fail:%s\n",strerror(errno));
        perror("fopen");
        return -1;
    }


    return 0;
}
#include <stdio.h>


int main(int argc, const char *argv[])
{
	FILE *fp;

	fp = fopen("newfile.txt","w");
	
	return 0;
}




2.关闭打开的文件

       #include <stdio.h>/*{{{*/

       int fclose(FILE *fp);
       功能:关闭指定的流 
       fclose()调用成功返回0,失败返回EOF,并设置errno
    注意:
        不要对已关闭的流在进行文件操作,不要对流进行重复关闭。
        因为:
        关闭流涉及到流对应的堆区FILE结构体空间和缓冲区空间释放,
        所以不能对关闭的流进行任何操作,读写文件,包括关闭(不能重
        复关闭流)/*}}}*/
3.读写文件
1)按字符读写


       int fgetc(FILE *stream);/*{{{*/
       功能:从指定流读入一个字符
       参数:
           @stream:指定读的流
       返回值 :
            成功 返回读到字符ascii码;
            失败 返回EOF,读到文件末尾,出错
       注意,不管是出错还是到达文件尾端,函数都返回同样的值。
       为了区分这两种不同的情况,可以调用ferror()或feof()来具体判断


       int fputc(int c, FILE *stream);
       功能:向指定流写入指定字符
       参数:
            @c:      写入的字符
            @stream: 指定的流
        返回值:成功返回写入字符ascii;失败返回EOF


练习:按字符读写,完成文件拷贝,
    注意源文件打开方式"r" ,目标文件打开方式"w"

#include <stdio.h>

void do_copy(FILE *fp_s,FILE *fp_d)
{
    int c;
    while((c = fgetc(fp_s)) != EOF)
        fputc(c,fp_d);

    return;
 
}
// ./a.out src dest
int main(int argc, const char *argv[])
{
    FILE *fp_s = NULL;
    FILE *fp_d = NULL;

    if( argc != 3)
    {
        fprintf(stdout,"Usage:%s src dest\n",argv[0]);
        return -1;
    }
   // open src file 
    if((fp_s = fopen(argv[1],"r") ) == NULL)
    {
      perror("fopen");
      return -1;
    }
   // open dest file 
    if((fp_d = fopen(argv[2],"w") ) == NULL)
    {
      perror("fopen");
      return -1;
    }

   //loop read write
   do_copy(fp_s,fp_d);

   //close
   fclose(fp_s);
   fclose(fp_d);


    return 0;
}


分别拷贝文本文件和二进制文件
./copy src dest
diff src dest/*}}}*/
#include <stdio.h>
#include <errno.h>
#include <string.h>

int do_copy(FILE *fp_src,FILE *fp_dest)
{
	char buf[4096];
	
	//bug:拷贝是二进制文件
	while(fgets(buf,sizeof(buf),fp_src) != NULL)
	{
		//只会将'\0'之前的字符写入文件
		fputs(buf,fp_dest);	
	}

	return 0;
}

//./a.out  src_file dest_file
int main(int argc, const char *argv[])
{
	FILE *fp_src;
	FILE *fp_dest;

	if(argc < 3)
	{
		fprintf(stderr,"Usage : %s src_file dest_file\n",argv[0]);
		return -1;
	}

	fp_src = fopen(argv[1],"r");
	if(fp_src == NULL){
		fprintf(stderr,"Fail to fopen %s:%s\n",argv[1],strerror(errno));
		return -1;
	}

	fp_dest = fopen(argv[2],"w");
	if(fp_dest == NULL){
		fprintf(stderr,"Fail to fopen %s:%s\n",argv[2],strerror(errno));
		return -1;
	}

	do_copy(fp_src,fp_dest);

	fclose(fp_src);
	fclose(fp_dest);
	
	return 0;
}



2)按行读写

       char *fgets(char *s, int size, FILE *stream);/*{{{*/
       功能:读入一行字符,最多读取size - 1,遇'\n'结束
       参数:
            @s       :读入数据存储区域首地址
            @size    :读入字符个数限制
            @stream  :指定流
        返回值:成功返回s;失败返回 NULL/*}}}*/

        注意:<1>.读入'\n'也会存储,最终会追加'\0'/*{{{*/
             <2>.最多读取size - 1个字符
             <3>.读取结束
                 a.遇到换行符'\n'
                 b.读满size-1个字符
                 c.读到文件末尾/*}}}*/

        
       int fputs(const char *s, FILE *stream);/*{{{*/
       功能:向指定文件输出字符串
       注意:
            若遇到'\0'则写结束,fputs不会将'\0'写到文件中。
练习:用fgets和fputs拷贝文件 /*}}}*/

#include <stdio.h>

int main(int argc, const char *argv[])
{
    char buf[100] = {0};

    while(fgets(buf,sizeof(buf),stdin) != NULL)
        puts(buf);

    return 0;
}
#include <stdio.h>

int main(int argc, const char *argv[])
{
    int c = 0;

    while((c = fgetc(stdin)) != EOF)
          fputc(c,stdout); 
    return 0;
}



3)按对象进行读写fread()/fwrite()
    size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);/*{{{*/


    实际读取对象的个数 fread(读取数据存储位置首地址ptr,一个对象大小size,要求读取对象个数nmemb,指定读取数据的流fp);


    实际要求读取字节数 一个对象大小 * 要求读取对象个数 size * nmemb
    注意:如果读到文件结尾,没有对象可读,返回0,读到结尾,对象个数不够,读取实际个数
   例如:每次按照int型的对象读文件

   int buf[10];
   n = fread(buf,sizeof(int),sizeof(buf)/sizeof(int),fp);
   如果文件大小只有20 bytes,此时n = 5

   如: 文件大小为1025byte
   char buf[1024];
   n = fread(buf,sizeof(char),sizeof(buf),fp);
   第一次: n = 1024;
   第二次: n = 1;
   第三次: n = 0;

size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
实际写入对象的个数 fwrite(写入数据存储位置首地址ptr,一个对象大小size,要求写入对象个数nmemb,指定写入数据的流fp);


    实际写入字节数 一个对象大小 * 实际写入对象个数 size * nmemb




    注意:
        循环读时返回值的判断问题,如果一次读取多个对象,则需要循环到读到0个对象为止。
        否则会少最后一部分!


        对象: 数据类型(基本类型,构造的类型)
        对象的大小 : 数据类型的大小


        注意:fread和fwrite也可以用来读写一般文件,但如果读写数据中没有固定结构,
        要正确进行读写只能按照1个字节作为结构(以一个字节大小为一个对象大小)!否则读写数据会不完整!
        注意读多少数据,写多少数据。


例:

        struct Student
        {
            char name[20];
            char sex;
            float score[3];
        };
        struct Student s[2];
        struct Student stu[3] = {{"Lee",'m',{90.5,89.5,79.5}},
                             {"zhang",'f',{78.0,89.0,100.0}},
                             {"zhao",'f',{100,50,79.5}}};
                             	
        n = fwrite(stu,sizeof(struct Student),3,fp);/*{{{*/
                             	
        n = fread(s,sizeof(struct Student),2,fp);
练习:利用fread()/fwrite()进行文件拷贝,
注意:fread()读到多少内容,fwrite()就写入多少内容!
读写普通文件(其中无固定大小的对象),应该按字节读写
提示:

    char buf[100];
    n = fread(buf,sizeof(char),sizeof(buf),rfp);
    fwrite(buf,sizeof(char),n,wfp);/*}}}*/
4.标准IO中文件定位
定位本质上指修改内核中文件表项中的offset值,同一文件,读写共用一个offset/*{{{*//*{{{*//*}}}*/
读写完成后,offset会增加相应的读写字节数


定位:修改文件表项中的offset
    int fseek(FILE *stream, long offset, int whence);
    功能:定位指定的流,可实现文件中读写起点修改
    参数:
    @stream 指定流
    @offset 相对偏移量,如果offset < 0 向前偏移,offset > 0向后偏移
    @whence 相对偏移起点
    
        SEEK_SET 以文件开头作为相对偏移起点 offset >= 0
        SEEK_CUR 以当前位置作为相对偏移起点 offset 可正可负
        SEEK_END 以文件结尾作为相对偏移起点 offset 可正可负


    返回值:成功返回0,失败返回-1,并设置errno
    
获取当前offset值

long ftell(FILE *stream);
    功能:获取当前文件位置偏移量
    返回值:成功返回当前offset,失败返回-1L,并置errno
将文件位置指示器值置0,即定位到文件开头

    void rewind(FILE *stream);
例:

    1.定位到文件开头
    A.fseek(fp,0,SEEK_SET);
    B.rewind(fp);

    2.定位到当前位置
    fseek(fp,0,SEEK_CUR);

    3.定位到结尾位置,向前10字节
    fseek(fp,-10,SEEK_END);

    4.从开头写了n个字节数据,想将n个字节数据读出来
    n = fwrite(buf,1,100,fp);
    //需要将offset置为写之前的值!
    fseek(fp,-n,SEEK_CUR);
    /*fseek(fp,0,SEEK_SET);*/
    fread(buf,1,n,fp);
练习:
    计算一个指定文件大小
    fseek定位到文件结尾,ftell求位置/*}}}*//*}}}*/

#include <head.h>

struct student 
{
	char name[15];
	int id;
	int score;
};

//./a.out  filename
int main(int argc, const char *argv[])
{
	int n;
	int fd;
	struct student stu2;
	struct student stu1 = {"大明",2,50};

	if(argc < 2)
	{
		fprintf(stderr,"Usage : %s filename\n",argv[0]);
		return -1;
	}
	
	fd = open(argv[1],O_RDWR | O_CREAT | O_TRUNC,0666);
	if(fd < 0){
		handler_error(argv[1]);
	}
	
	write(fd,&stu1,sizeof(stu1));
	
	lseek(fd,0,SEEK_SET);

	n = read(fd,&stu2,sizeof(stu2));	

	printf("n = %d\n",n);
	printf("name:%s,id:%d,score:%d\n",stu2.name,stu2.id,stu2.id);
	
	close(fd);

	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值