IO输入输出及标准IO函数

【1】什么是IO

#include <stdio.h>

input:输入,从外部存储设备输入到内存中

output: 输出,从内存输出到外部存储设备。

> 存储设备:
>
> ​    外部存储设备,硬盘
>
> ​    内存:SDROM DDR4;

总结:数据从外部存储设备到内存,内存到外部存储设备的流动

【2】IO分类

1.文件IO

文件IO:由操作系统提供,与操作系统绑定,又称之为系统调用

|      | windows    | linux |
| ---- | ---------- | ----- |
|      | file_read  | read  |
|      | file_write | write |

注意:

1. 文件IO的复用性,可移植性较低
2. 文件IO涉及到cpu从用户空间切换到内核空间,C代码调用汇编语言等等操作,是一个**耗时的操作**,所以应该尽量减少文件IO的使用。

2.标准IO

标准IO:根据ANSI标准对文件IO进行二次封装,
printf();
if(OS == windows)
{
    file_write;
}
else if(OS == Linux)
{
     write;
}

作用:

1. 提高代码的复用性和可移植性;

2. 提高了输入输出的效率;

   设置了一个缓冲区,缓冲区满或者满足一定条件后,调用文件IO,陷入内核,由内核完成对文件的输入输出,大大减少了对文件IO的使用。

2. 标准IO

【1】流和流指针

流(stream):将数据一个一个移入或者移出缓冲区的形式,称之为字节流;

流指针(FILE*):每打开一个文件,都会在内存中申请一片空间(缓冲区);

​                            管理这片空间的变量都存储在FILE结构体中,FILE结构体由系统定义好了,我们使用即可;

1. FILE结构体成员

> vi -t指令         查找系统定义好的数据类型 和 变量 宏
>
> 1)sudo apt-get install ctags
>
> 2)cd  /usr/include/     如果只需要查看标准库中的内容
>
> 3)sudo ctags -R
>
> 4)在家目录的.vimrc中添加 set tags+=/usr/include/tags
>
> 追代码:
>
> ​        ctrl + 鼠标左键
>
> ​        或者 左键选中要追的代码 ctrl + ]
>
> ```c
> $ vi -t FILE
>     typedef struct _IO_FILE FILE; 
> ctrl + 鼠标左键点击 _IO_FILE
> ```

```c
struct _IO_FILE {
  char* _IO_buf_base;   /* Start of reserve area. */     缓冲区的起始地址
  char* _IO_buf_end;    /* End of reserve area. */         缓冲区的结束地址

  int _fileno;         文件描述符
}
```

 2. 标准输入输出流

在main函数启动之前。系统会默认打开三个流指针;

```
stdin     标准输入流指针          从终端读取数据
stdout     标准输出流指针             将数据输出到终端上
stderr     标准错误输出流指针     
```

3. man手册

$man man     查看man手册

```c
       1   可执行程序或 shell 命令
       2   系统调用(内核提供的函数)     文件IO
       3   库调用(程序库中的函数)      标准IO
           
man 手册默认会从第一卷开始查找,

【2】标准IO函数

1. 常见的标准IO函数
fopen / fclose     打开/关闭一个文件
fprintf / fscanf
fputc     / fgetc
fgets     / fputs
fwrite  / fread
fseek     修改文件偏移量

 1)fopen
功能:打开一个文件;
头文件:
       #include <stdio.h>
原型:
       FILE *fopen(const char *pathname, const char *mode);
参数:
    char *pathname:指定要打开的文件路径+文件名;
    char *mode:打开方式;
       r      以读的方式打开文件;
                如果文件不存在,则打开失败;

       r+     以读写的方式打开文件;
                如果文件不存在,则打开失败;

       w      以写的方式打开文件;
                   如果文件不存在则创建文件;
                   如果文件存在则清空文件;

       w+     以读写的方式打开文件;
                   如果文件不存在则创建文件;
                   如果文件存在则清空文件;

       a      以写的方式打开文件;
                   如果文件不存在则创建文件;
                   如果文件存在则以追加的方式写;

       a+     以读写的方式打开文件;
                   如果文件不存在则创建文件;
                   如果文件存在则以追加的方式写;
返回值:
     成功,返回流指针;
     失败,返回NULL,并设置错误码(errno);

例子:
    FILE* fp = fopen("./1.txt", "r");  

 2)perror
功能:根据错误码errno,显示不同的错误信息;
头文件:
       #include <stdio.h>
原型:
       void perror(const char *s);
参数:
    char *s:用于提示的字符串;

#include<errno.h>
errno:本质上是一个全局变量,对应了各种错误,perror就是根据errno打印错误信息的
cd /usr/include/asm-generic/
    
例子:
     perror("fopen");  

 3)fclose
功能:关闭文件,销毁缓冲区;
头文件:
       #include <stdio.h>
原型:
       int fclose(FILE *stream);
参数:
    FILE *stream:指定要关闭的流指针;
返回值:
    成功,返回0;
    失败,返回EOF(-1),更新errno;

4)fprintf
功能:将数据格式化输入到文件中;
头文件:
       #include <stdio.h>
原型:
       int printf(const char *format, ...);
       int fprintf(FILE *stream, const char *format, ...);
参数:
    FILE *stream:指定要输出到的流指针;
    char *format:格式化;
    ...:不定参数;
返回值:
    成功,返回被打印的字节个数; >0;
    失败,返回负数;

 5)fscanf
功能:将文件中的数据格式化输入到内存中;
头文件:
       #include <stdio.h>
原型:
       int scanf(const char *format, ...);
       int fscanf(FILE *stream, const char *format, ...);
参数:
    FILE *stream:指定要输入的流指针;
    char *format:格式化;
    ...:不定参数;
返回值:
    成功,返回获取到的数据个数;
    失败或者读取到文件结尾,返回EOF;

6)fputc
功能:将单个字符输出到文件中;
头文件:
       #include <stdio.h>
原型:
       int putchar(int c);
       int fputc(int c, FILE *stream);
参数:
    int c:指定要输出的字符,可以填字符类型,也可以填对应的整型;
    FILE *stream:流指针;
返回值:
    成功,返回输出的字符对应的整型;
    失败,返回EOF;

 7)fgetc
功能:从文件中获取单个字符;
头文件:
       #include <stdio.h>
原型:
       int getchar(void);
       int fgetc(FILE *stream);
参数:
    FILE *stream:流指针;
返回值:
    成功,返回获取到的字符;
    失败或者读取到文件结尾,返回EOF;

8)缓冲区

标准IO中所有的数据都输存储在缓冲区中,然后输出到硬件中

 i. 全缓冲

**操作对象**

​        对普通文件进行操作(调用fopen函数打开的文件),通过FILE* fp流指针维护该缓冲区;

**大小**

​        4K = 4096byte = 4*1024;
    //由于系统优化,当打开文件,但是不操作的时候,不会真正的申请缓冲区
    fputc('a', fp);
    printf("%ld\n", fp->_IO_buf_end - fp->_IO_buf_base); //4096    

**刷新缓冲区的方式**

1. 缓冲区满

2. fflush函数强制刷新缓冲区
          #include <stdio.h>
          int fflush(FILE *stream);

3. 关闭流指针 fclose

4. 主函数调用retrun退出

5. 调用exit函数
          #include <stdlib.h>
          void exit(int status);
   目前只要理解该函数能退出进程;
   int status:随便填一个整型即可;
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main(int argc, const char *argv[])
{
    FILE* fp = fopen("./1.txt", "w");
    if(NULL == fp)
    {
        perror("fopen");
        return -1;
    }

    /*
    
    //由于系统优化,当打开文件,但是不操作的时候,不会真正的申请缓冲区
    fputc('a', fp);
    printf("%ld\n", fp->_IO_buf_end - fp->_IO_buf_base); //4096
*/

    /*
    //缓冲区满
    int i = 0;
    while(i<4096+1)
    {
        fputc('a', fp);
        i++;
    }
    */


    /*
    //fflush强制刷新缓冲区
    fputc('b', fp);
    fflush(fp);
    */


    /*
    //关闭流指针
    fputc('c', fp);
    fclose(fp);
    */

    /*
    fputc('d', fp);
    return 0;
    */

    fputc('e', fp);
    exit(0);

    while(1)
        sleep(1);

    fclose(fp);


    return 0;
}
 ii.行缓冲

**操作对象**

​    标准输入(stdin),标准输出(stdout)

**大小**

​    1K = 1024byte

```c
printf("%ld\n", stdout->_IO_buf_end - stdout->_IO_buf_base);
```

**刷新缓冲区**

1. 缓冲区满

2. fflush函数强制刷新缓冲区

   ```c
          #include <stdio.h>
          int fflush(FILE *stream);
   ```

3. 关闭流指针 fclose

4. 主函数调用retrun退出

5. 调用exit函数

   ```c
          #include <stdlib.h>
          void exit(int status);
   目前只要理解该函数能退出进程;
   int status:随便填一个整型即可;
   ```

6. 遇到'\n'字符

7. 遇到输入函数,例如scanf fgets fscanf fgetc

   

iii.无缓冲

**操作对象**

​    标准错误输出(stderr)

**大小**

​    0, perror调用的就是stderr流指针

9)fputs
功能:将字符串输出到文件中;
头文件:
       #include <stdio.h>
原型:
       int puts(const char *s);
       int fputs(const char *s, FILE *stream);
参数:
    char *s:指定要输出的字符串的首地址;
    FILE *stream:指定要输出到哪个流指针对应的缓冲区中;
返回值:
    成功,返回非负数;
    失败,返回EOF;

例子:
    //打开文件:以写的方式打开
    FILE* fp = fopen("./fputs.abc", "w");
    if(NULL == fp) 
    {   
        perror("fopen");
        return -1; 
    }                                                     

    fputs(str, fp);

10)fgets
功能:从指定文件中获取字符串,包括空格;
头文件:
       #include <stdio.h>
原型:
       char *gets(char *s);
       char *fgets(char *s, int size, FILE *stream);
参数:
    char *s:存储获取到的字符串;
    int size:指定要获取的字符串大小;最多读取size-1个字节,因为要保留'\0'位,fgets会自动填充最后的'\0'位;
                注意:fgets只能识别到'\n';
                假设一行有10个字节,fgets读取20个字节,实际读取10个字节,一次不会读取到下一行;
    FILE *stream:指定要从哪个流指针的缓冲区中获取;
返回值:
    成功,返回s,即字符串首地址;
    失败或者读取到文件结尾,返回NULL;


版权声明:本文为xyx340521原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。