第十九篇,Linux标准IO接口,文件指针,标准IO函数和重定位问题详细说明。

一、标准IO。
1、标准IO函数接口有什么特点?
标准IO接口都是属于标准C库中的函数,所以标准IO接口都是属于第三手册的。
系统IO是专门用于访问设备文件,例如:lcd液晶设备、触摸屏...
标准IO是专门用于访问普通文件,例如:图片、文本文档...

2、如何使用标准IO接口来访问关闭文件?
1)访问文件?  -->  fopen()   -->  man 3 fopen
功能: stream open functions
    //打开一个流的函数

   #include <stdio.h>

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

参数:
    path:想打开的那个文件的路径
    mode:操作文件的权限
 
   r      Open text file for reading.  The stream is positioned at the beginning of the file.
      //以只读的方式来打开文件,文件的定位在文件的开头。

   r+     Open for reading and writing.  The stream is positioned at the beginning of the file.
      //以可读可写方式来打开文件,文件的定位在文件的开头。

   w      Truncate  file to zero length or create text file for writing.  The stream is positioned at
          the beginning of the file.
      //以只写方式来打开文件,如果文件存在,则清空,如果文件不存在,则创建,文件的定位在文件的开头。

   w+     Open for reading and writing.  The file is created if it does not exist,  otherwise  it  is
          truncated.  The stream is positioned at the beginning of the file.
      //以可读可写方式来打开文件,如果文件存在,则清空,如果文件不存在,则创建,文件的定位在文件的开头。

   a      Open  for  appending  (writing  at end of file).  The file is created if it does not exist.    
          The stream is positioned at the end of the file.
      //以只写并且追加的方式打开文件,如果文件不存在,则创建,文件的定位在文件的末尾。

   a+     Open for reading and appending (writing at end of file).  The file is created  if  it  does
          not exist.  The initial file position for reading is at the beginning of the file, but out‐
          put is always appended to the end of the file.
      //以可读可写并且追加的方式来打开文件,如果文件不存在,则创建,如果是读操作,则文件定位在开头,如果写操作,则文件的定位在末尾。

返回值:
    成功:文件流指针  FILE*
    失败:NULL


2)关闭文件。  --->  fclose()   --> man 3 fclose
功能: fclose - close a stream
    //关闭一个流

   #include <stdio.h>

  int fclose(FILE *stream);

参数:
    stream:需要关闭的那个文件的文件流指针
    
返回值:
    成功:0
    失败:EOF

#define EOF -1


    练习1: 如果使用fopen函数接口来打开一个文件,会不会占用一个文件描述符?

        3 = open("xxx");
        FILE* p = fopen();
        ? = open();

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>   //open
#include <unistd.h>

int main(int argc,char *argv[])
{
    int fd1 = open("ggy.txt",O_RDWR);
    printf("fd1 = %d\n",fd1);
    
    FILE *fp = fopen("aaa.txt","r");  //会占用一个文件描述符   //fp 对应文件描述符 4
    
    int fd2 = open("kkk.txt",O_RDWR);
    printf("fd2 = %d\n",fd2);
    
    close(fd1);
    close(fd2);
    fclose(fp);
    
    return 0;
}

    练习2: 自己写代码,测试fopen函数的第二个参数。
        测试一下,会不会创建/清空。

二、关于默认打开的三个文件的文件指针。
头文件路径:/usr/include/unistd.h

//标准文件描述符
/* Standard file descriptors.  */
#define    STDIN_FILENO    0    //标准输入文件描述符
#define    STDOUT_FILENO    1    //标准输出文件描述符
#define    STDERR_FILENO    2    //标准出错文件描述符

头文件路径:/usr/include/stdio.h

typedef struct _IO_FILE FILE;  //  FILE  等价于  struct _IO_FILE

/* Standard streams.  */
extern struct _IO_FILE *stdin;        //标准输入的文件指针
extern struct _IO_FILE *stdout;        //标准输出的文件指针
extern struct _IO_FILE *stderr;        //标准出错的文件指针

等价于
extern FILE *stdin;  --> 对应文件描述符0        
extern FILE *stdout; --> 对应文件描述符1         
extern FILE *stderr; --> 对应文件描述符2         

/* C89/C99 say they're macros.  Make them happy.  */
#define stdin stdin    --> 前面stdin是宏定义   后面stdin是变量  (好处:这样做可以让文件指针变得跟文件描述符一样,都是宏定义)
#define stdout stdout
#define stderr stderr


总结

        文件描述符              文件指针
标准输入        STDIN_FILENO            stdin
标准输出        STDOUT_FILENO           stdout
标准出错        STDERR_FILENO           stderr


三、标准IO的读写函数。
1、读取文件。   -->  fread()   --> man 3 fread

    #include <stdio.h>

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

参数:
    ptr:    数据缓冲区
    size:   每一块的字节数 
    nmemb:  想读取的块数   
    stream: 文件指针

返回值:
    成功: 已经读取成功的块数
    失败: 小于你想读取的块数/0

文件总字节数        30                                    24                                     28
     3块 每一块是10个字节   --> 3          3块 每一块是10个字节   --> 2           3块 每一块是10个字节  -> 2
         2块 每一块是15个字节   --> 2          2块 每一块是15个字节   --> 1           2块 每一块是15个字节  -> 1
     5块 每一块是10个字节   --> 3          5块 每一块是10个字节   --> 2           5块 每一块是10个字节  -> 2
     1块 每一块是30个字节   --> 1          1块 每一块是30个字节   --> 0           1块 每一块是30个字节  -> 0 


结论:一定要读取到那一块字节数够你想读取的块数的字节数时,才能算一块。
      如果不够,就不能算一块。


2、写入文件。   -->  fwrite()   --> man 3 fwrite

    #include <stdio.h>

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

参数:
    ptr:  数据缓冲区
    size: 每一个块的字节数
    nmemb:想写入的块数
    stream:文件指针

返回值:
    成功:已经写入的块数
    失败:小于想写入的块数/0

例如:
数据缓冲区:char buf[30] = "helloworldhelloworldappletree"

  3块   每一块10个字节

返回值:3
文件内容:helloworldhelloworldappletree + 空格

  2块   每一块15个字节

返回值:2
文件内容:helloworldhelloworldappletree + 空格

  5块   每一块10个字节

返回值:5
文件内容:helloworldhelloworldappletree + 空格 + 20个乱码

  5块   每一块6个字节

返回值:5
文件内容:helloworldhelloworldappletree + 空格

  5块   每一块5个字节

返回值:5
文件内容:helloworldhelloworldapple

  1块   每一块20个字节

返回值:1
文件内容:helloworldhelloworld

结论:fwrite()你想写多少块,返回值就是多少,如果内容不够,来乱码来凑。


    练习1: 先读、再读?   --> 继续读
    练习2: 先写、再写?   --> 继续写
    练习3: 先读、再写?   --> 继续写
    练习4: 先写、再读?   --> 继续读


四、关于重新定位的问题。
1、重新定位文件指针。    -->  fseek()   --> man 3 fseek
功能: reposition a stream
    //重新定位流

   #include <stdio.h>

  int fseek(FILE *stream, long offset, int whence);
 
参数:
    stream:文件指针
    offset:需要偏移的字节数
    whence:基准点
        SEEK_SET   --> 相对于开头
        SEEK_CUR   --> 相对于当前位置
        SEEK_END   --> 相对于末尾

返回值:
    成功:0    (lseek是返回距离开头多远,fseek不是)
    失败:-1

2、获取当前位置距离开头偏移量。   --> ftell()  --> man 3 ftell

   #include <stdio.h>

  long ftell(FILE *stream);

参数:
    stream: 文件指针

返回值:
    成功:距离开头的偏移量
    失败:-1

3、重置文件指针。   --> rewind()   --> man 3 rewind
功能:只要在代码中调用了rewind,那么就会将文件指针移动到文件的开头。

   #include <stdio.h>

   void rewind(FILE *stream);

参数:
    stream:文件指针

返回值:无


  rewind(fp)   is equivalent to   fseek(fp, 0, SEEK_SET)
          //等价于

#include <stdio.h>

int main(int argc,char *argv[])
{
    //helloworldhelloworldhelloworld
    
    FILE *fp = fopen("1.txt","r+");
    if(fp == NULL)
        printf("fopen error!\n");
    
    int ret = fseek(fp,4,SEEK_SET);
    printf("ret = %d\n",ret);  //0
    
    char buf[50] = {0};
    fread(buf,5,2,fp);
    printf("buf = %s\n",buf); //oworldhell
    
    long n = ftell(fp);
    printf("n = %ld\n",n);//14
    
    rewind(fp);
    n = ftell(fp);
    printf("n = %ld\n",n); //0
    
    char bufx[10] = "apple";
    fwrite(bufx,10,1,fp);  //文件内容:apple     helloworldhelloworld
    
    fclose(fp);
    
    return 0;
}


五、printf()输出问题。
printf()输出对象默认是标准输出,标准输出是有缓冲区的,也就是说,这些缓冲区需要特定的条件才能输出数据。
printf()是一个行缓冲区的函数,就是遇到'\n'才会将之前在缓冲区中的数据全部输出。

例子1:
int main(int argc,char *argv[])
{
    printf("helloworld");
    printf("yueqian");
    while(1);
    return 0;
}

运行结果:不会输出任何内容。
分析:因为缓冲区中没有遇到'\n',所以所有的内容都不会输出。

例子2:
int main(int argc,char *argv[])
{
    printf("helloworld");
    printf("yueqian");
    return 0;
}

运行结果:输出helloworldyueqian
分析:程序退出时,会刷新标准输出的缓冲区。

例子3:
int main(int argc,char *argv[])
{
    printf("helloworld");
    printf("yueqian\n");
    printf("appletree");
    while(1);
    return 0;
}

运行结果:输出helloworldyueqian
分析:遇到'\n',就会把'\n'之前的数据全部打印出来。

例子4:
int main(int argc,char *argv[])
{
    while(1)
    {
        printf("hello");
        usleep(100000);  //0.1s
    }
}

运行结果:等到缓冲区满了,就会将缓冲区中全部的数据输出。
结论:printf如果缓冲区满了,即使程序没有结束,也没有\n,也是会全部输出的。

例子5:
int main(int argc,char *argv[])
{
    int x;
    printf("helloworld");
    printf("yueqian");
    
    scanf("%d",&x);

    while(1);
}

运行结果:输出helloworldyueqian
分析:键盘输入函数接口可以刷新标准输出缓冲区。


例子6:主动刷新缓冲区    -->  fflush()   --> man 3 fflush
功能: fflush - flush a stream
        //刷新一个流

      #include <stdio.h>

     int fflush(FILE *stream);

参数:
    stream:文件指针

返回值:
    成功:0
    失败:EOF

int main(int argc,char *argv[])
{
    printf("helloworld");
    printf("yueqian");
    fflush(stdout)

    while(1);
}

运行结果:输出helloworldyueqian
分析:调用fflush()可以主动刷新缓冲区。


例子7: 修改printf的输出对象,让输出的内容不要放在标准输出的缓冲区,而是要放在标准出错这个对象上,
        这样你想输出什么,就会马上输出什么,不需要任何的条件。

修改标准输出对象:fprintf()   --> man 3 fprintf

     #include <stdio.h>

    int fprintf(FILE *stream, const char *format, ...);

参数:
    stream:文件指针
    format:输出的内容

   
   printf("hello");   --> 因为printf默认对象是标准输出,所以这句话含义是把"hello"放在标准输出缓冲区中。

   等价于  

   fprintf(stdout,"hello");  --> 指定把"hello"放在标准输出缓冲区中


   fprintf(stderr,"hello");


#include <stdio.h>

int main(int argc,char *argv[])
{
    fprintf(stderr,"hello");
    while(1);
}

六、标准IO函数。
sprintf()   getchar()  getc()   fgetc()   putchar()  putc()  fputc()   gets()  fgets()

1、sprintf()    --> 保存一个字符串到一个缓冲区中  作用:拼接字符串   -->  man 3 sprintf

     #include <stdio.h>

    int sprintf(char *str, const char *format, ...);

参数:
    str: 数据缓冲区
    format:输出的内容

  int a = 10;
  printf("a = %d\n",a);   --> 将a的值放置到%d的位置,然后"a = 10\n"这句话输出到标准输出缓冲区上。

  int a = 10;
  fprintf(stderr,"a = %d\n",a);   --> 将a的值放置到%d的位置,然后"a = 10\n"这句话输出到标准出错上。

  int a = 10;
  char buf[100] = {0};
  sprintf(buf,"a = %d\n",a);   --> 将a的值放置到%d的位置,然后"a = 10\n"这句话保存到数组buf中。


2、getchar()   --> 从标准输入缓冲区中获取一个字符   作用:清空缓冲区    --> man 3 getchar

    #include <stdio.h>

   int getchar(void);

参数:无
返回值:
    成功:获取到的字符
    失败:EOF


注意:
每次scanf()之后,都要跟这句话
while(getchar() != '\n');
就可以清空缓冲区。

3、getc()    --> 可以去任意一个文件指针中获取一个字符   -->  man 3 getc

   getchar()  固定去标准输入缓冲区中获取字符
   getc()     除了可以去标准输入缓冲区中获取字符,还可以去其他的文件指针中获取字符


   #include <stdio.h>

  int getc(FILE *stream);

参数:
    stream:需要获取字符的文件指针

返回值:
    成功:获取到的字符
    失败:EOF

#include <stdio.h>

int main(int argc,char *argv[])
{
    //getchar();  //指定从标准输入获取
    
    //int a = getc(stdin);
    //printf("a = %c\n",a);
    
    FILE *fp = fopen("1.txt","r+");
    int a = getc(fp);
    printf("a = %c\n",a);  //a
    
    a = getc(fp);
    printf("a = %c\n",a);  //p
    return 0;
}

4、fgetc()   等价于  getc

   #include <stdio.h>

   int fgetc(FILE *stream);

参数:
    stream:需要获取字符的文件指针

返回值:
    成功:获取到的字符
    失败:EOF


#include <stdio.h>

int main(int argc,char *argv[])
{
    //getchar();  //指定从标准输入获取
    
    //int a = getc(stdin);
    //printf("a = %c\n",a);
    
    FILE *fp = fopen("1.txt","r+");
    int a = fgetc(fp);
    printf("a = %c\n",a);  //a
    
    a = fgetc(fp);
    printf("a = %c\n",a);  //p
    return 0;
}

5、putchar()   --> 把一个字符放置到标准输出的缓冲区中

    #include <stdio.h>

   int putchar(int c);

参数:
    c: 需要操作的字符

返回值:
    成功:存放的字符
    失败:EOF 


#include <stdio.h>

int main()
{
    //printf("x");
    
    int c = 'x';
    putchar(c);
    printf("\n");
    while(1);

    return 0;
}

6、putc()  fputc()    --> 把一个字符放置到任何一个文件指针中   -->  man 3 putc

    #include <stdio.h>

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

参数:
    c:需要处理的字符
    stream:文件指针

返回值:    
    返回值:
    成功:存放的字符
    失败:EOF     


#include <stdio.h>
int main()
{
    FILE *fp = fopen("2.txt","w");
    
    putc('x',fp);
    fputc('y',fp);
    
    fclose(fp);
}


7、  fgets()    --->  获取一个字符串到一个缓冲区中   -->  man 3 fgets

    #include <stdio.h>

   char *fgets(char *s, int size, FILE *stream);

参数:
    s: 缓冲区
    size: 最多获取的字符个数
    stream: 文件指针

返回值:
    成功:第一个参数s
    失败:NULL

#include <stdio.h>

int main(int argc,char *argv[])
{
    char s[10];
    //gets(s);  //由于没有限制大小,所以在linux已经不再使用。
    //printf("s = %s\n",s);
    
    fgets(s,sizeof(s),stdin);
    printf("s = %s",s);
    return 0;
}
 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

肖爱Kun

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

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

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

打赏作者

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

抵扣说明:

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

余额充值