Linux标准I/O编程

文件基础

概念:一组相关数据的有序集合

文件有文件名和路径

Linux支持不同的文件类型:

  • 常规文件          r,如:文本文件、二进制文件
  • 目录文件          d
  • 字符设备文件   c
  • 块设备文件      b
  • 管道文件          p
  • 套接字文件      s
  • 符号链接文件   l

注意:操作系统不同,所支持的文件类型也不同


标准I/O-介绍

标准I/O由ANSI C标准定义

主流操作系统上都实现了C库

作用:标准I/O通过缓冲机制减少系统调用,实现更高的效率,移植性好

  • 没有操作系统直接访问计算机资源,不安全
  • 有操作系统,程序要访问硬件资源,需要通过操作系统提供的系统调用接口访问。
  • 不同的操作系统提供的系统调用接口各不相同,Linux的系统调用大致分为:进程控制、进程间通信、文件系统控制、存储管理、网络管理、套接字控制、用户管理

解释:标准I/O函数在执行时用到系统调用,Linux必须从用户态切换到内核态,处理相应的请求,然后再返回到用户态。如果频繁的执行系统调用会增加系统的开销。为了避免这种情况,标准I/O使用时再用户空间创建缓冲区,读写时先操作缓冲区,在合适的时机再通过系统调用访问实际的文件,从而减少了使用系统调用的次数。


标准I/O-流

FILE

  • 标准IO用一个结构体类型来存放打开的文件的相关信息
  • 标准I/O的所有操作都是围绕FILE来进行

流(stream)

FILE又被称为流

文本流/二进制流

不同操作系统对流的定义不同:

Windows

  • 二进制流:换行符<-->'\n'
  •    文本流:换行符<-->'\r''\n'

Linux

  • 换行符<-->'\n'

标准I/O-流的缓冲类型

  1. 全缓冲:默认情况下打开的就是全缓冲,当缓冲区无数据、无空间或执行flush操作时才执行I/O操作。
  2. 行缓冲:当输入或输出时遇到换行符(‘\n')时,进行IO操作;当流和一个终端关联时,标准输入输出流是典型的行缓冲。注意:我们在打印调试时一定要加上换行符,否则,打印的信息仍存放在缓冲区中。
  3. 无缓冲:数据直接写入文件,流不进行缓冲。标准出错流是不带缓冲的,出错信息可以立刻显示在终端上。

标准I/O概述http://www.proedu.com.cn:8800/web/shareVideo/index.action?id=1012531&ajax=1

标准I/O预定义3个流,程序运行时自动打开

标准输入流0STDIN_FILENOstdin
标准输出流1STDOUT_FILENOstdout
标准错误流2STDERR_FILENOstderr

标准I/O-打开流

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

成功返回流指针,失败时返回NULL。

标准I/O-fopen-mode参数

 

 fopen示例

#include<stdio.h>
int main(int argc,char *argv[])
{
   FILE *fp;
   if((fp=fopen("test.txt","r+")==NULL){
   printf("fopen error\n");
   return -1;
}
...
return 0;
}

标准I/O-fopen-新建文件权限

  • fopen()创建的文件访问权限是0666(rw-rw-rw-)
  • Linux系统中umask设定会影响文件的访问权限,其规则是(0666&~umask).umask 0022,运算后644
  • 用户可以通过umask函数修改相关设定。
  • 如果希望umask不影响文件访问权限,该如何设定?0

 

流的打开与关闭http://www.proedu.com.cn:8800/web/shareVideo/index.action?id=1012532&ajax=1

标准I/O-处理错误信息

extern int errno;

void perror(const  char *s);

char *strerror(int errno);
  •  errno存放错误号
  • perror先输出字符串s,在输出错误号 对应的错误信息。
  • strerror根据错误号返回错误信息

 标准I/O-处理错误信息-示例1

#include<stdio.h>
int main(int argc,char *argv[])
{
   FILE *fp;
   if((fp=fopen("test.txt",r+)==NULL)
{
   perror("fopen");
   return -1;
}
...
fopen:No such file or directory

 标准I/O-处理错误信息-示例2

#include<stdio.h>
#include<string.h>
#include<errno.h>
int main(int argc,char *argv[])
{
   FILE *fp;
   if((fp=fopen("test.txt",r+)==NULL)
{
   printf("fopen:%s\n",strerror(errno));
   return -1;
}
...
fopen:No such file or directory

 

错误处理http://www.proedu.com.cn:8800/web/shareVideo/index.action?id=1012533&ajax=1

 

标准I/O-关闭流

int fclose(FILE *stream);
  • fclose()调用成功返回0,失败返回EOF并设置errno;
  • 流关闭时自动刷新缓冲中的数据并释放缓冲区;
  • 当一个程序正常终止时,所有打开的流都会被关闭;
  • 流一旦关闭后就不能执行任何操作。

标准I/O-思考和练习

程序中能够打开的文件或流的个数有限制,如何测试?

思路:循环打开流,成功则计数器加一,直到出错为止。

答案:1021+stdin+stdout+stderr=1024

/*************************************************************************
	> File Name: filebyte.c
	> Author: 徐维龙
	> Mail: 3190217171@qq.com
	> Created Time: 2020年02月22日 星期六 20时1分29秒
 ************************************************************************/
#include<stdio.h>
int main(int argc, const char *argv[])
{
    int num=0;
    FILE *fp;
    while(1)
    {
        if((fp=fopen("1.txt","a+"))!=NULL)
        {
            num++;
        }
        else
        {
            perror("fopen");
            break;
        }
    }
    printf("The IO stream max is %d\n",num);
    fclose(fp);
    return 0;
}

标准I/O-流的读写

标准I/O-流的读-按字符输入

int getc(FILE *stream);
int fgetc(FILE *stream);
int getchar(void);

成功返回读取的字符,若到文件结尾或出错时返回EOF

getchar()等同于fgetc(stdin).

标准I/O-fgetc-示例1

int ch;
ch=fgetc(stdin);
printf("%c\n",ch);

标准I/O-fgetc-示例2:统计文件大小

/*************************************************************************
	> File Name: filebyte.c
	> Author: 徐维龙
	> Mail: 3190217171@qq.com
	> Created Time: 2020年02月22日 星期六 20时19分22秒
 ************************************************************************/

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
    FILE *fp;
    int ch, count=0;
    if((fp=fopen(argv[1],"r"))==NULL)
    {
        perror("fopen");
        return -1;
    }
    while((ch=fgetc(fp))!=EOF)
    {
        count++;
    }
    printf("total %d bytes\n",count);
    return 0;
}

标准I/O-流的写-按字符输出

int putc(int c,FILE * stream);
int fputc(int c,FILE * stream);
int putchar(int c);

成功返回写入的字符;出错返回EOF;

putchar(c)等同于fputc(c,stdout);

标准I/O-fputc-示例1:向文件中输入a-z

/*************************************************************************
	> File Name: a_zfile.c
	> Author: 徐维龙
	> Mail: 3190217171@qq.com
	> Created Time: 2020年02月22日 星期六 20时37分28秒
 ************************************************************************/

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
    int ch;
    FILE *fp;
    if((fp=fopen(argv[1],"w"))==NULL)
    {
        perror("fopen");
        return -1;
    }
    for(ch='a';ch<='z';ch++)
    {
        fputc(ch,fp);
    }
    return 0;
}

标准I/O-fputc-示例2:实现文件复制

/*************************************************************************
	> File Name: mycp.c
	> Author: 徐维龙
	> Mail: 3190217171@qq.com
	> Created Time: 2020年02月22日 星期六 21时30分20秒
 ************************************************************************/

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
    int ch;
    FILE *fps,*fpd;
    if(argc<3)
    {
        printf("%s srcfile desfile\n",argv[0]);
    }
    if((fps=fopen(argv[1],"r"))==NULL)
    {
        perror("fopen src");
        return -1;
    }
    if((fpd=fopen(argv[2],"w"))==NULL)
    {
        perror("fopen des");
        return -1;
    }
    while((ch=fgetc(fps))!=EOF)
    {
        fputc(ch,fpd);
    }
    fclose(fps);
    fclose(fpd);
    return 0;
}

流的读写http://www.proedu.com.cn:8800/web/shareVideo/index.action?id=1012534&ajax=1

标准I/O-按行输入

char * gets(char *s);//不建议使用,容易造成缓冲区溢出
char * fgets(char * s,int size,FILE * stream);

成功返回s,失败或到达文件末尾:NULL。

fgets遇到‘\n'或已输入size-1个字符时返回,总是包含'\0',不能保证每次都能读出一行。

标准I/O-fgets-示例

#define N 6

char buf[N];
fgets(buf,N,stdin);
printf("%s",buf);

假设键盘输入分别是:abcd<回车>      abcdef<回车>,buf中的内容是?

标准I/O-按行输出

int puts(const char *s);
int fputs(const char *s,FILE *stream);

成功返回s,失败:NULL

puts将缓冲区s的字符串输出到stdout,并追加'\n'

标准I/O-fputs-示例

puts("hello world");
FILE *fp;
char  buf[]="hello world";
if((fp=fopen(argv[1],"a"))==NULL)
{
  perror("fopen");
  return -1;
}
  fputs(buf,fp);

注意:输出的字符串中可以包含'\n',也可以不包含

标准I/O-思考和练习

如何统计一个文本文件包含多少行?

1.fgetc? 效率低

/*************************************************************************
	> File Name: fgtcline.c
	> Author: 徐维龙
	> Mail: 3190217171@qq.com
	> Created Time: 2020年02月23日 星期日 11时11分45秒
 ************************************************************************/

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
    FILE *fp;
    int ch,num=0;
    if(argc<2)
    {
        printf("usage : %s <file>\n",argv[0]);
        return -1;
    }
    if((fp=fopen(argv[1],"r"))==NULL)
    {
        perror("fopen");
        return -1;
    }
    while((ch=fgetc(fp))!=EOF)
    {
        if(ch=='\n')//疑问:此处换成if(fgetc(fp)=='\n'),测试结果不对
        {
            num++;
        }
    }
    printf("the line of %s is %d\n",argv[1],num);
    return 0;
}

 因为fgetc(fp)在一次循环中执行了2次。

2.fgets?如何判断读取了一行?

buf[strlen[buf]-1]?='\n'

/*************************************************************************
	> File Name: fileline.c
	> Author: 徐维龙
	> Mail: 3190217171@qq.com
	> Created Time: 2020年02月23日 星期日 10时50分57秒
 ************************************************************************/

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include<string.h>
int main(int argc, char *argv[])
{
    FILE *fp;
    char buf[128];
    int num=0;
    if(argc<2)
    {
        printf("Usage : %s <fiel>\n",argv[0]);
        return -1;
    }
    if((fp=fopen(argv[1],"r"))==NULL)
    {
        perror("fopen");
        return -1;
    }
    while(fgets(buf,128,fp)!=NULL)
    {
        if(buf[strlen(buf)-1]=='\n')
        num++;
    }
    printf("the line of %s is %d\n",argv[1],num);

    return 0;
}

标准I/O-按对象读写

size_t fread(void *ptr,size_t size,size_t nmemb,FILE *stream);

size_t fwrite(const void *ptr,size_t size,size_t nmemb,FILE *stream);

size:读取的每个记录的大小;nmemb:读取的记录数

成功返回实际读取到的nmemb数目,失败返回EOF

既可以读写文本文件,也可以读写数据文件。

int s[10];
if(fread(s,sizeof(int),10,fp)<0)
{
    perror("fread");
    return -1;
}
struct student
{
    int no;
    char name[8];
    float score;
}s[]={{1,"zhang",97},{2,"wang",99}};
fwrite(s,sizeof(student),2,fp);

标准I/O-思考和练习

如何利用fread、fwrite实现文件的复制?

/*************************************************************************
	> File Name: freadwritecp.c
	> Author: 徐维龙
	> Mail: 3190217171@qq.com
	> Created Time: 2020年02月23日 星期日 13时43分39秒
 ************************************************************************/

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
    FILE *fps,*fpd;
    char ch[128];
    int n;
    if(argc<3)
    {
        printf("Usage : %s<src_file> <des_file>\n",argv[0]);
        return -1;
    }
    if((fps=fopen(argv[1],"r"))==NULL)
    {
        perror("fopen src:");
        return -1;
    }
    if((fpd=fopen(argv[2],"w"))==NULL)
    {
        perror("fopen des:");
        return -1;
    }
    while((n=fread(ch,1,128,fps))>0)
    {
        fwrite(ch,1,n,fpd);
    }
    fclose(fps);
    fclose(fpd);
    return 0;
}

标准I/O-刷新流

什么情况会刷新流?

  1. 缓冲区满或遇到’\n'
  2. 流关闭
  3. fflush
int fflush(FILE *fp);

成功:0,失败:EOF

Linux下只能刷新输出缓冲区

/*************************************************************************
	> File Name: fflush.c
	> Author: 徐维龙
	> Mail: 3190217171@qq.com
	> Created Time: 2020年02月23日 星期日 15时00分01秒
 ************************************************************************/

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
 
    FILE *fp;
    if((fp=fopen("tetx.txt","w"))==NULL)
    {
        perror("fopen:");
        return -1;
    }
    fputc('a',fp);
    fflush(fp);
    while(1);
    return 0;
}

定位流-ftell/fseek/rewind

#include<stdio.h>
long tell(FILE *stream);
int fseek(FILE *stream,long offset,int whence);
void rewind(FILE *stream);

 ftell成功返回流的当前读写位置,出错返回EOF

fseek()定位一个流,成功返回0,失败返回EOF

whence基准值,参数:SEEK_SET/SEEK_CUR/SEEK_END

offset参数:偏移量,可正可负

rewind()将流定位到文件的开始位置

读写流时,当前读写位置自动后移

定位流-示例一

在文件末尾追加字符't'

FILE *fp=fopen("text.txt","r+");
fseek(fp,0,SEEK_END);
fputc('t',fp);

定位流-示例二

获取文件长度

/*************************************************************************
	> File Name: filelongfseek.c
	> Author: 徐维龙
	> Mail: 3190217171@qq.com
	> Created Time: 2020年02月23日 星期日 15时27分59秒
 ************************************************************************/

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
    FILE *fp;
    if((fp=fopen("text.txt","r+"))==NULL)
    {
        perror("fopen");
        return -1;
    }
    fseek(fp,0,SEEK_END);
    printf("length of file is %ld\n",ftell(fp));
    return 0;
}

标准I/O-判断流是否出错和结束

include<stdio.h>
int ferror(FILE *stream);
int feof(FILE *stream);

ferror()返回1表示流出错;否则返回0

feof()返回1表示文件已到末尾;否则返回0

标准I/O-格式化输出

#include<stdio.h>
int printf(const char *fmt,...);
int fprintf(FILE *stream,const char *fmt,...);
int sprintf(char *buf,const char *fmt,...);

成功:输出字符数(sprintf返回存入数组中的字符数)

失败:EOF

标准I/O-格式化输出-示例

以指定格式“年-月-日”分别写入文件和缓冲区

int year,month,date;
FILE *fp;
char buf[64];
year=2014;month=10;date=26;
fp=fopen("text.txt","a+");
fprintf(fp,"%d-%d-%d\n",year,month,date);
sprintf(buf,"%d-%d-%d\n",year,month,date);

 标准I/O-思考和练习

 

/*************************************************************************
	> File Name: time.c
	> Author: 徐维龙
	> Mail: 3190217171@qq.com
	> Created Time: 2020年02月23日 星期日 16时18分08秒
 ************************************************************************/

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
int main(int argc, char *argv[])
{
    FILE *fp;
    char buf[64];
    int line =0;
    time_t t;
    struct tm *tp;

    if((fp=fopen("time.txt","a+"))==NULL)
    {
        perror("fopne");
        return -1;
    }
    while(fgets(buf,64,fp)!=NULL)
    {
        if(buf[strlen(buf)-1]=='\n');
        line++;
    }
    while(1)
    {
        time(&t);
        tp=localtime(&t);
      
        fprintf(fp,"%02d、%04d-%02d-%02d %02d:%02d:%02d\n",++line,tp->tm_year+1970,\
                tp->tm_mon+1,tp->tm_mday,tp->tm_hour, tp->tm_min,tp->tm_sec);
        fflush(fp);
        sleep(1);
    }
    fclose(fp);
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

神圣雅诗人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值