C 文件IO

标准IO和文件IO的区别

  • 标准IO是c库函数,文件IO是系统调用接口
  • 标准IO自带缓冲区,文件IO没有缓冲
  • 标准IO的实现还是要依赖文件IO

缓冲区

  1. 全缓冲:当缓冲区满了或者特定的情况下才会刷新
  2. 行缓冲:当缓冲区中有了换行\n就会刷新
  3. 无缓冲:任何东西进入缓冲区马上被刷新走
  • 默认:程序结束会刷新缓冲区
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main()
{

    char buff[1024];

    memset( buff, '\0', sizeof( buff ));

    fprintf(stdout, "启用全缓冲\n");
    setvbuf(stdout, NULL, _IOFBF, 1024);

    fprintf(stdout, "这里是 runoob.com\n");
    fprintf(stdout, "该输出将保存到 buff\n");
    fflush( stdout );

   
    fprintf(stdout, "这将在编程时出现\n");
    fprintf(stdout, "最后休眠五秒钟\n");

    sleep(5);

    return(0);
}

setvbuf

int setvbuf(FILE *stream, char *buf, int mode, size_t size);
//功能:可以更改缓冲区类型(缓冲区可以人为指定)
//返回值:成功返回0,失败返回非0
stream:流,表示要更改缓冲类型的流
buf:缓冲区地址
mode:要改成的缓冲区类型
	_IONBF:无缓冲
	_IOLBF:行缓冲
	_IOFBF:全缓冲
size:要更改buf的大小(单位字节)

perror、errno、stderr

  • errno:错误号,用于内核调用完函数后设置调用结果的编号
  • perror:输出对应错误号的信息
  • stderr:标准出错流 – 无缓冲

标准流

标准输入流:stdin – 0

标准输出流:stdout – 1

标准出错流:stderr – 2

每打开一个终端,都会默认打开这三个流

fflush

int fflush(FILE *stream);
//功能:强制刷新一个流(Linux下不能刷新stdin)
//返回值:成功返回0,失败返回EOF
stream:要刷新的流

标准IO流

打开文件 — fopen()

#include <stdio.h>
FILE *fopen(const char *pathname,const char *mode);
//功能:
 //让内核打开一个文件并且标记它
//返回值:
	//成功返回流指针,失败返回NULL
    //pathname:要打开的文件名(包含路径)
    //mode:打开文件的方式(只读、只写...)
//注:如果操作二进制文件,那么打开文件时的方式可以加上b,但是Linux下不区分二进制流和文本流

freopen()

freopen函数则用于关闭一个已经打开的文件,并打开一个新的文件。它需要三个参数:第一个参数是一个字符串,指定要打开的新文件的路径;第二个参数是一个字符串,指定新文件的打开模式;第三个参数是一个已经打开的FILE指针,指向要关闭的文件。

freopen函数常常用于重定向标准输入、输出或错误流。例如,你可以用freopenstdout重定向到一个文件,这样printf就会将输出写入文件,而不是终端。

#include <stdio.h>

int main() {
    FILE* stream = freopen("output.txt", "w", stdout);
    if (stream == NULL) {
        // Handle error
    }
    printf("This will be written to output.txt\n");
    fclose(stream);
    return 0;
}
在这个例子中,`freopen`函数打开了一个名为"output.txt"的文件,并将`stdout`流重定向到这个文件。这样,后续的`printf`调用会将输出写入这个文件,而不是终端。
注意,你应该检查`freopen`的返回值,以确保文件打开成功。在完成文件写入后,你也应该使用`fclose`关闭文件。

关闭流 — fclose()

#include <stdio.h>
int fclose(FILE *stream)
//功能:关闭已经打开的流
//返回值:成功返回0,失败返回EOF
stream:要关闭的流

按字符读 — fgetc()

#include <stdio.h>
int fgetc(FILE *stream);
//功能:从流里面读取一个字符
//返回值:成功返回读到的字符,失败返回EOF
stream:要读的流

按字符写 — fputc()

#include <stdio.h>
int fputc(int c, FILE *stream);
//功能:往流里面写一个字符
//返回值:成功返回写入的字符,失败返回EOF
//fputc 会将参数c 转为unsigned char 后写入参数stream 指定的文件中。
c:要写的字符
stream:要写入的流

按行读 ---- fgets()

#include <stdio.h>
char *fgets(char *s, int size, FILE *stream);
//功能:读取一行的内容
//返回值:成功返回s的地址,失败返回NULL
s:要把内容读到哪里去(缓冲区)
size:预计要读的字节数
stream:从哪个流读取
//注:fgets是表示遇到了换行符才终止,如果size比一行的数据大,就读完所有内容。size如果比一行数据小,只读size-1个,追加一个'\0',表示没读完。

按行写 — fputs()

#include <stdio.h>
int fputs(const char *s, FILE *stream);
//功能:往一个流里面写一行数据
//返回值:成功返回非负数,失败返回EOF
s:要写入的内容所在的缓冲区
stream:要写入的流

//注:fputs和puts不一样,puts会自动添加一个换行,fputs不添加

按对象读 — fread()

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
//功能:从流里面读取内容,内容分了组(对象)
//返回值:成功返回实际读到的对象个数,失败返回EOF
ptr:要把读到内容放在哪个缓冲区
size:每个对象的字节大小
nmemb:预计要读多少个对象
stream:要读的流
//注:size * nmemb不能超过缓冲区(ptr)大小

按对象写 — fwrite()

size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
//功能:把缓冲区的内容写入到流里面
//返回值:成功返回实际写入的对象个数,失败返回EOF
ptr:要写入的数据所在的缓冲区
size:每个对象的字节大小
nmemb:预计要写入的对象个数
stream:要写入的流

重定向 — freopen()

FILE *freopen(const char *pathname, const char *mode, FILE *stream);
//功能:打开一个文件并且会产生一个流,stream所表示的流会被替代
//返回值:成功返回流指针,失败返回NULL
pathname:要打开的文件
mode:打开文件的方式
stream:要重定向的流

定位流

fseek

int fseek(FILE *stream, long offset, int whence);

//功能:fseek()用来移动文件流的读写位置。参数stream为已打开的文件指针, 参数offset为根据参数whence来移动读写位置的位移数。
//参数:
whence为下列其中一种:
SEEK_SET:文件开头
        The offset is set to offset bytes.
SEEK_CUR:当前位置
        The offset is set to its current location plus offset bytes.
SEEK_END:文件末尾
        The offset is set to the size of the file plus offset bytes.
SEEK_END将读写位置指向文件尾后再增加offset个位移量。
当whence值为SEEK_CUR 或SEEK_END时, 参数offset允许负值的出现。
//返回值
        当调用成功时则返回0, 若有错误则返回-1, errno会存放错误代码。

ftell

long ftell(FILE *stream);
//功能:检索当前光标位置
//返回值:成功返回位置值,失败返回-1

rewind

void rewind(FILE *stream);
//功能:返回文件的开头
//和(void) fseek(stream, 0L, SEEK_SET)功能一样

文件IO

  • 不带缓冲
  1. 不带缓冲指的是每个read和write都调用内核中的相应系统调用
  2. 不带缓冲的I/O函数不是ANSI C的组成部分,但是是POSIX和XPG3的组成部分
  3. 通过文件描述符来访问文件

文件描述符

  1. 是非负整数(共1024个:0-1023)但是0、1、2被系统占用,用户的第一个从3开始
  2. 对于内核而言,所有打开文件都由文件描述符引用。当打开一个现存文件或创建一个新文件时,内核向进程返回一个文件描述符。
    1.
  3. 文件io:符合常量: 幻数0、1、2应被代换成符号常数STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO。这些常数都定义在头文件<unistd.h>中。
    1. 标准io是流: 0 :stdin 1: stdout 2: stderr

open

//功能: open, creat - open and possibly create a file or device

//头文件:
       #include <sys/types.h>
       #include <sys/stat.h>
       #include <fcntl.h>
//函数原型:
       int open(const char *pathname, int flags); //
       int open(const char *pathname, int flags, mode_t mode);

       int creat(const char *pathname, mode_t mode);
//参数说明:
	const char *pathname:被打开的文件名(可包括路径名)
     int flags:(读写三个参数互斥,只允许选择一个)
            O_RDONLY:只读方式打开文件。:read only
            O_WRONLY:只写方式打开文件。 :write only
            O_RDWR: 可读写方式打开文件。:read write
		
            //以下参数和读写参数可以用 '|' 连接使用
            O_CREAT:如果该文件不存在,就创建一个新的文件,并用第三的参数为其设置权限。
            O_EXCL:如果使用O_CREAT时文件存在,则可返回错误消息。这一参数可测试文件是否存在。
            O_NOCTTY:使用本参数时,如文件为终端,那么终端不可以作为调用open()系统调用的那个进程的控制终端
             O_APPEND:以添加方式打开文件,所以对文件的写操作都在文件的末尾进行。
            O_TRUNC:如文件已经存在,那么打开文件时先删除文件中原有数据。(截断式打开:如果文件有内容,先清空文件内容,再打开)

    //mode_t mode:文件权限(只有用到O_CREAT 创建文件的时候才有用)
            0777
            0664
    //返回值:
            成功:返回对应的文件描述符
            失败:-1

close

//功能:close - close a file descriptor

//头文件:
       #include <unistd.h>
//函数原型:
       int close(int fd);
//参数说明:
     	int fd:已经打开的要关闭的文件  
//返回值: 
        成功:0
        失败:-1 并设置错误码(errno)

read

//功能:
		read - read from a file descriptor
//头文件:
		#include <unistd.h>
//函数原型:
       	ssize_t read(int fd, void *buf, size_t count);
//参数说明:
		int fd:已经打开的可读文件
   		void *buf:用于保存读取到的数据---> 注意,可以使用结构体
    	size_t count:每次读取的字节数 
 //返回值:
        -1:失败
        0:读取到文件末尾或文件为空(在网络通信里面表示客户端退出)
        大于0:成功读取到数据

write

//功能:
	write - write to a file descriptor
//头文件:
       #include <unistd.h>
//函数原型:
       ssize_t write(int fd, const void *buf, size_t count);
//参数说明:
		int fd:已经打开的可写文件
   		const void *buf:用于写入的数据
    	size_t count:每次写入的字节数 
//返回值:
        -1:失败
        大于0:成功写入的数据

㊙️注意:read和write在用户与内核交界处会复制一份数据,所以他们值相同,但是不是同一份数据

lseek

//功能:lseek - reposition read/write file offset
//头文件:
       #include <sys/types.h>
       #include <unistd.h>
//函数原型:
       off_t lseek(int fd, off_t offset, int whence);
//参数说明:
	int fd: 操作的文件
    off_t offset:偏移量       (+n  向后偏移   -n  向前偏移   0  不偏移)
    int whence:参考的基准位置 
       SEEK_SET:文件开头
         The offset is set to offset bytes.
       SEEK_CUR:当前位置
         The offset is set to its current location plus offset bytes.
       SEEK_END:文件末尾
         The offset is set to the size of the file plus offset bytes.

//返回值:
        文件的当前光标位置(按字节数计算)

文件io 和标准io 的区别

文件io 标准io
缓冲
高低级 linux系统io、低级io 高级io
操作对象 文件描述符
能否打开设备文件 可以 不可以
API open close read write sleek fopen fclose fread fwrite fseek fprintf

目录操作

opendir

#include <sys/types.h>
#include <dirent.h>
DIR *opendir(const char *name);
DIR *fdopendir(int fd);
//返回值:成功返回一个目录流的指针,失败返回NULL

readdir

#include <dirent.h>
struct dirent *readdir(DIR *dirp);
//返回值结构体
struct dirent {
    ino_t          d_ino;       /* Inode number */
    off_t          d_off;       /* Not an offset; see below */
    unsigned short d_reclen;    /* Length of this record */
    unsigned char  d_type;      /* Type of file; not supported
                                   by all filesystem types */
    char           d_name[256]; /* Null-terminated filename */
};
//注意是随机读一个,要打印该目录全部文件要用循坏去判断,例子如下

closedir

#include <sys/types.h>
#include <dirent.h>
int closedir(DIR *dirp);
//返回值,成功返回0,失败返回-1

文件属性

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *pathname, struct stat *statbuf);
int fstat(int fd, struct stat *statbuf);
int lstat(const char *pathname, struct stat *statbuf);
//结构体
struct stat {
    dev_t     st_dev;         /* ID of device containing file */
    ino_t     st_ino;         /* Inode number */
    mode_t    st_mode;        /* File type and mode */
    nlink_t   st_nlink;       /* Number of hard links */
    uid_t     st_uid;         /* User ID of owner */
    gid_t     st_gid;         /* Group ID of owner */
    dev_t     st_rdev;        /* Device ID (if special file) */
    off_t     st_size;        /* Total size, in bytes */
    blksize_t st_blksize;     /* Block size for filesystem I/O */
    blkcnt_t  st_blocks;      /* Number of 512B blocks allocated */
    /* Since Linux 2.6, the kernel supports nanosecond
                  precision for the following timestamp fields.
                  For the details before Linux 2.6, see NOTES. */

    struct timespec st_atim;  /* Time of last access */
    struct timespec st_mtim;  /* Time of last modification */
    struct timespec st_ctim;  /* Time of last status change */

    #define st_atime st_atim.tv_sec      /* Backward compatibility */
    #define st_mtime st_mtim.tv_
    
    
    #define st_ctime st_ctim.tv_sec
};
//lstat和stat功能类似,但是lstat可以获取到链接文件本身文件的属性
//返回值,成功返回0,失败返回-1
//参数: 
	pathname 文件名