💎流和FILE对象

之前的IO函数都是围绕文件描述符的,并且是系统调用。标准IO库中封装了IO函数,这些函数是围绕流进行的。当用标准IO库打开一个文件或者创建一个文件时,我们已经使一个流和文件关联。

流的定向

对于ASCII字符集,一个字符一个字节,对于国际字符集,一个字符多个字节。
标准IO文件流可以用于单字节或者多字节的字符集,流的定向决定读写时是用单字节还是多字节的,在流被创建时,没有定向。当使用多字节IO函数,流被定向为宽定向的,反之则是字节定向的。
而流的定向可以被函数设置

  1. #include<stdio.h>
  2. #include<wchar.h>
  3. int fwide(FILE* fp,int mode);
  4. //若流是宽定向,返回正值,字节定向返回负值,为定向返回0
  • mode参数的正负表示fwide试图将流改变成相应的定向
  • 但是fwide并不能改变已定向流的定向

    预定义的流

    进程一般预定义了三个流:

  • 标准输入:引用的文件和与STDIN_FILENO一样

  • 标准输出:STDOUT_FILENO
  • 标准错误:STDERR_FILENO

通过文件指针stdin、stdout、stderr引用

缓冲

标准IO库提供缓冲的目的是尽可能减少使用read和write调用的次数。此外也会对每个流自动的进行缓冲管理。 :::info why需要尽可能减少read和write的调用次数?
read和write函数实际上是在执行系统调用,系统调用通过中断进入内核态,这期间会发生用户栈和内核栈的切换,耗费较多时间。 ::: 标准IO提供了以下3种类型的缓冲:

  • 全缓冲:在填满标准IO缓冲区后才进行实际的IO操作
  • 行缓冲:在输入和输出中遇到换行符的时候执行IO操作
  • 不带缓冲,例如对于字符

    当标准输入输出连接到终端是,他们是行缓冲的,行缓冲长度是1024,(输入输出的长度并不限制再1024),如果超出行缓冲的长度,会多次调用系统调用write 当将流定向到普通文件时,是全缓冲的。

修改缓冲

对于任意一个给定的流,如果不喜欢系统默认的缓冲,可以修改缓冲类型

  1. #include <stdio.h>
  2. void setbuf(FILE *stream, char *buf);
  3. void setbuffer(FILE *stream, char *buf, size_t size);
  4. void setlinebuf(FILE *stream);
  5. int setvbuf(FILE *stream, char *buf, int mode, size_t size);

buf:缓冲区,大小为BUFSIZ,此后该流应该是全缓冲的。
setvbuf可以指定缓冲区及其长度,通过mode参数实现:

  • _IOFBF:全缓冲
  • _IOLBF:行缓冲
  • _IONBF:不带缓冲

    打开流

    1. #include <stdio.h>
    2. FILE *fopen(const char *pathname, const char *mode);
    3. FILE *fdopen(int fd, const char *mode);
    4. FILE *freopen(const char *pathname, const char *mode, FILE *stream);
  • fopen函数打开路径名为pathname的一个指定文件

  • freopen再指定流上打开一个文件,如果流已经被定向了,那么会清除该定向
  • fdopen取一个已有的文件描述符,并让标准IO流和该文件描述符结合。

mode参数可以选择对该流的读写方式
image.png
关闭流:

  1. int fclose(FILE* fp);

关闭流会自动flush缓冲区。

读写流

一旦打开了流可以从三种不同类型的非格式化IO重疾险你选择:

  • 一次一个字符的IO
  • 一次一行的IO
  • 直接IO:每次IO操作读写某种数量的对象,每个对象具有指定长度 :::info 实际上这种还是从标准IO自带的缓冲中读写? :::

    输入函数

    一次读一个字符

    1. #include <stdio.h>
    2. int fgetc(FILE *stream);
    3. char *fgets(char *s, int size, FILE *stream);
    4. int getc(FILE *stream);
    5. int getchar(void);
    6. int ungetc(int c, FILE *stream);
    若成功返回下一个字符, 若到达文件末尾返回EOF
    从流中读取数据后,可以调用ungetc将字符再压会流中.

    每次一行IO

    ```cpp

    include

int fgetc(FILE *stream);

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

int getc(FILE *stream);

int getchar(void);

int ungetc(int c, FILE *stream);

  1. <a name="zhSEa"></a>
  2. ## 输出函数
  3. ```cpp
  4. #include <stdio.h>
  5. int fputc(int c, FILE *stream);
  6. int fputs(const char *s, FILE *stream);
  7. int putc(int c, FILE *stream);
  8. int putchar(int c);
  9. int puts(const char *s);

读取末尾和error

  1. #include <stdio.h>
  2. void clearerr(FILE *stream);
  3. int feof(FILE *stream);
  4. int ferror(FILE *stream);
  5. int fileno(FILE *stream);

二进制IO

如果进行二进制IO操作,我们更愿意一次读或者写一个完整的结构。

  • 如果用getc和putc需要循环整个结构
  • 如果用fputs或者fgets那么遇到null字节会终止

因此需要二进制IO

  1. #include <stdio.h>
  2. size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
  3. size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

size为要读写的结构的长度
nmemb为想要操作的元素的个数

  1. float data[10];
  2. if(fwrite(&data[2],sizeof(float),4,fp)!=4)
  3. err_sys("fwrite error!");
  4. //--------------------------------------
  5. struct{
  6. int a;
  7. long b;
  8. char c;
  9. }item;
  10. fwrite(&item,sizeof(item),1,fp);

返回值是读写对象的个数,如果到了文件末尾返回值可能小于期望值,需要ferror和feof判断哪种情况。

格式化IO

格式化输出

  1. #include <stdio.h>
  2. int printf(const char *format, ...);
  3. int fprintf(FILE *stream, const char *format, ...);
  4. int dprintf(int fd, const char *format, ...);
  5. int sprintf(char *str, const char *format, ...);
  6. int snprintf(char *str, size_t size, const char *format, ...);
  7. #include <stdarg.h>
  8. int vprintf(const char *format, va_list ap);
  9. int vfprintf(FILE *stream, const char *format, va_list ap);
  10. int vdprintf(int fd, const char *format, va_list ap);
  11. int vsprintf(char *str, const char *format, va_list ap);
  12. int vsnprintf(char *str, size_t size, const char *format, va_list ap);

格式化输入

  1. #include <stdio.h>
  2. int scanf(const char *format, ...);
  3. int fscanf(FILE *stream, const char *format, ...);
  4. int sscanf(const char *str, const char *format, ...);
  5. #include <stdarg.h>
  6. int vscanf(const char *format, va_list ap);
  7. int vsscanf(const char *str, const char *format, va_list ap);
  8. int vfscanf(FILE *stream, const char *format, va_list ap);

实现细节