Linux:基础IO(cIO库函数详细介绍)(IO系统调用接口详细介绍)(两者关系:文件描述符和文件指针)

目录

c系统中的库函数:

fopen:打开文件

fclose:关闭文件

fwrite:向文件写入一个数据块

fread:读写

fprintf:格式化输出到一个流/文件中

fseek:移动/跳转 到当前 读取/写入位置

fgets:获取字符串

fput:把字符串写入到指定的流( stream) 中,但不包括空字符。

rewind改变内部指针

代码演示

IO系统调用接口

open

close

read

write

lseek

代码演示:

系统调用和库函数的关系

两者关系

文件描述符fd

文件指针


c系统中的库函数:

在c中,文件操作都是由库函数实现的,头文件都为<stdio.h>主要是分为读和写两张操作

fopen:打开文件

函数原型: FILE * fopen(const char *path,cost char *mode)
作用:打开一个文件,返回指向该文件的指针
参数说明:

                 第一个参数为打开文件的路径及文件名
                 第二个参数表示打开的方式
打开方式:

  • r:只读方式打开,文件必须存在
  • r+:可读写,必须存在
  • rb+:打开二进制文件,可以读写
  • rt+:打开文本文件,可读写
  • w:只写,文件存在则文件长度清0,文件不存在则建立该文件
  • w+:可读写,文件存在则文件长度清0,文件不存在则建立该文件
  •  上述如r、w、a在其后都可以加一个b,表示以二进制形式打开文件

返回值:文件打开了返回一个指向该打开文件的指针(FILE结构),打开失败返回NULL,并且将错误代码存在errno


fclose:关闭文件

函数原型:int fclose(FILE *stream)

功能:关闭一个文件流,使用fclose就可以把缓冲区内最后剩余的数据输出到磁盘文件中,并释放文件指针和有关的缓冲区


fwrite:向文件写入一个数据块

函数原型:size_t fwrite(const void* buffer, size_t size, size_t count, FILE* stream);

返回值:返回实际写入的数据块数目

参数说明:

  •    buffer:是一个指针,对fwrite来说,是要获取数据的地址;
  •    size:要写入内容的单字节数;
  •    count:要进行写入size字节的个数;
  •    stream:目标文件指针;

返回值:实际写入的数据项个数count。


fread:读写

函数原型:    size_t fread(void* buff,size_t size,size_t count,FILE* stream)
参数说明:  从文件中读入数据到指定的地址中

  •     buff:   接收数据的的指针(buff),就是数据的储存地址
  •     size:单个元素的大小(单位为字节)
  •     count: 参数的元素个数     
  •     stream:数据的文件指针,指向文件内部数据

返回值:读取的总数据元素个数


fprintf:格式化输出到一个流/文件中

函数原型:  int fprintf( FILE *stream, const char *format, [ argument ]...)
                   fprintf()函数根据指定的格式(format)向输出流(stream)写入数据(argument)
参数说明:

  •     stream:文件指针
  •     format:输出格式
  •     第三个:附加参数列表

规定符:

  • %d, %i 十进制有符号整数
  • %u 十进制无符号整数
  • %f 浮点数
  • %s 字符串
  • %c 单个字符
  • %p指针的值  

fseek:移动/跳转 到当前 读取/写入位置

函数原型:int fseek(FILE *stream,long offset,int framewhere)

作用:重定位文件内部的指针

参数是说明:

  • 第一个为文件指针,
  • 第二个是指针的偏移量,
  • 第三个是指针偏移起始位置

返回值:重定位成功返回0,否则返回非零值
需要注意的是该函数不是重定位文件指针,而是重定位文件内部的指针,让指向文件内部数据的指针移到文件中我们感兴趣的数据上,重定位主要是这个目的。
说明:执行成功,则stream指向以fromwhere为基准,偏移offset个字节的位置。执行失败(比方说offset偏移的位置超出了文件大小),则保留原来stream的位置不变
起始点有三个常量值:

  •   SEEK_SET 0   文件开头
  •   SEEK_CUR 1   文件当前位置
  •   SEEK_END 2   文件末尾

fgets:获取字符串

函数原型:char *fgets(char *str, int num, FILE *fp)
返回值:返回第一个参数buf

参数说明:

  •          str: 保存从文件读取出来的字符串      
  •          fp: 待读文件的文件指针
  •          num: 表示从文件中读出的字符串不超过 n-1个字符。在读入的最后一个字符后加上串结束标志'\0'

fput:把字符串写入到指定的流( stream) 中,但不包括空字符。

函数原型: int  fputs(char * str,FILE * stream)
参数说明:
     str:这是一个数组,包含了要写入的以空字符终止的字符序列。
     stream:指向 FILE 对象的指针,该 FILE 对象标识了要被写入字符串的流
返回值:该函数返回一个非负值,如果发生错误则返回 EOF(-1)。

ftell:用于得到文件位置指针当前位置相对于文件首的偏移字节数
函数原型:long ftell(FILE *stream)
函数功能:
使用fseek函数后再调用函数ftell()就能非常容易地确定文件的当前位置。


rewind改变内部指针

函数原型: void rewind(FILE *stream);
函数功能:将文件内部的指针重新指向一个流的开头系统调用
注意: 不是文件指针而是文件内部的位置指针,随着对文件的读写文件的位置指针(指向当前读写字节)向后移动。而文件指针是指向整个文件,如果不重新赋值文件指针不会改变。


代码演示

#include<stdio.h>
#include<string.h>
#include<errno.h>
int main()
{
    FILE* fp = fopen("test","w");
    if(!fp)
    {   
        perror("fopen");
        return -1; 
    }   

    const char* tmp = "WJF-handsome\n";
   
    fwrite(tmp ,1,strlen(tmp),fp);
    
    fclose(fp);
    return 0;  
}

 

输出方式

#include<stdio.h>
#include<string.h>
#include<errno.h>
int main()
{
    const char* tmp = "hello f\n";

    fwrite(tmp,strlen(tmp),1,stdout);
  
    printf("hello p\n");

    fprintf(stdout,"hello fp\n");
  return 0;
}

 

 

IO系统调用接口

头文件:#include <sys/types.h>    #include <sys/stat.h>    #include <fcntl.h>

open

函数原型:

    int open(const char * pathname, int flags);
    int open(const char * pathname, int flags, mode_t mode);
参数说明:
pathname:打开路径
flags:打开方式
             O_RDONLY 以只读方式打开文件
             O_WRONLY 以只写方式打开文件
             O_RDWR 以可读写方式打开文件.

             上述三种旗标是互斥的, 也就是不可同时使用, 但可与下列的旗标利用OR(|)运算符组合.

  •  O_CREAT 若欲打开的文件不存在则自动建立该文件.
  •  O_EXCL 如果O_CREAT 也被设置, 此指令会去检查文件是否存在. 文件若不存在则建立该文件, 否则将导致打开文件错误. 此外, 若                         
  • O_CREAT 与O_EXCL 同时设置, 并且欲打开的文件为符号连接, 则会打开文件失败.
  • O_NOCTTY 如果欲打开的文件为终端机设备时, 则不会将该终端机当成进程控制终端机.
  • O_TRUNC 若文件存在并且以可写的方式打开时, 此旗标会令文件长度清为0, 而原来存于该文件的资料也会消失.
  • O_APPEND 当读写文件时会从文件尾开始移动, 也就是所写入的数据会以附加的方式加入到文件后面.
  • O_NONBLOCK 以不可阻断的方式打开文件, 也就是无论有无数据读取或等待, 都会立即返回进程之中.
  • O_NDELAY 同O_NONBLOCK.
  • O_SYNC 以同步的方式打开文件.
  • O_NOFOLLOW 如果参数pathname 所指的文件为一符号连接, 则会令打开文件失败.
  • O_DIRECTORY 如果参数pathname 所指的文件并非为一目录, 则会令打开文件失败

mode:权限

参数mode 则有下列数种组合, 只有在建立新文件时才会生效, 此外真正建文件时的权限会受到umask 值所影响, 因此该文件权限应该为 (mode-umaks).

  • S_IRWXU00700 权限, 代表该文件所有者具有可读、可写及可执行的权限.
  • S_IRUSR 或S_IREAD, 00400 权限, 代表该文件所有者具有可读取的权限.
  • S_IWUSR 或S_IWRITE, 00200 权限, 代表该文件所有者具有可写入的权限.
  • S_IXUSR 或S_IEXEC, 00100 权限, 代表该文件所有者具有可执行的权限.
  • S_IRWXG 00070 权限, 代表该文件用户组具有可读、可写及可执行的权限.
  • S_IRGRP 00040 权限, 代表该文件用户组具有可读的权限.
  • S_IWGRP 00020 权限, 代表该文件用户组具有可写入的权限.
  • S_IXGRP 00010 权限, 代表该文件用户组具有可执行的权限.
  • S_IRWXO 00007 权限, 代表其他用户具有可读、可写及可执行的权限.
  • S_IROTH 00004 权限, 代表其他用户具有可读的权限
  • S_IWOTH 00002 权限, 代表其他用户具有可写入的权限.
  • S_IXOTH 00001 权限, 代表其他用户具有可执行的权限.


返回值:若所有欲核查的权限都通过了检查则返回0 值, 表示成功, 只要有一个权限被禁止则返回-1.

close

函数原型:

int close(int fildes);

作用:close系统调用用于“关闭”一个文件,close调用终止一个文件描述符fildes以其文件之间的关联。文件描述符被释放,并能够重新使用。

返回值:close成功返回1,出错返回-1

read

函数原型:

size_t read(int fildes, void *buf, size_t nbytes);

参数说明:

  • fildes:文件描述符,标识要读取的文件。如果为0,则从标准输入读数据。类似于scanf()的功能。
  • *buf:缓冲区,用来存储读入的数据。
  • nbytes:要读取的字符数。
  • 返回值:size_t返回成功读取的字符数,它可能会小于请求的字节数。
     

write

函数原型:

size_t write(int fildes, const void *buf, size_t nbytes);

参数说明:

  • fildes:文件描述符,标识了要写入的目标文件。例如:fildes的值为1,就像标准输出写数据,也就是在显示屏上显示数据;如果为 2 ,则想标注错误写数据。
  • *buf:待写入的文件,是一个字符串指针。
  • nbytes:要写入的字符数。
  • 函数返回值:size_t  返回成功写入文件的字符数。
     

lseek

函数原型:

off_t lseek(int fd, off_t offset, int whence)

参数说明:

  • fd是打开的文件描述符
  • offset是与参考偏移的位置
  • whence是文件参考的位置

它一共有三个位置

  • SEEK_SET 文件开始,如果使用此偏移,那么文件定位到offset的位置
  • SEEK_CUR 文件读写指针的当前位置
  • SEEK_END 文件结尾

lseek返回值是文件读写指针移动之后的位置,-1表示失败

a,取得文件当前偏移位置可以如下: f_offset = lseek(fd, 0, SEEK_CUR);

b,文件大小可以通过下面: f_len = lseek(fd, 0, SEEK_END);

c,使用lseek函数移动好读写指针之后,使用read,write即可往文件里面读写数据。
 

代码演示:

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

int main()
{
    int fd = open("test",O_RDONLY);
    if(fd < 0)
    {   
        perror("open");
        return -1; 
    }   

    char* buf[1024];
    
    ssize_t s = read(fd,buf,12);//1从哪读2放在那3读多少
   
    printf("%s\n",buf);
    
    close(fd);
   
    return 0;
}

 

系统调用和库函数的关系

两者关系

f开头的函数,都是对系统调用的封装,方便二次开发

文件描述符fd

文件描述符实质就是一个整数,当做文件的ID,在系统中标识文件

一个进程要对所有打开的文件进行管理,先将文件描述起来,然后组织进行管理,进程对文件进行描述的结构叫:file(struct file)

进程使用了一个结构体来组织这些描述符,而文件描述符就是这结构体数组下标

Linux进程默认会打开三个文件描述符:

  • 标准输入0
  • 标准输出1
  • 标准错误2
  • 0,1,2一般对应的物理设备有:键盘,显示器,显示器
stdin stdout stderr FILE* 文件流指针
0 1 2 int 文件描述符

每个进程在PCB(Process Control Block)中保存着一份文件描述符表,文件描述符就是这个表的索引,每个表项都有一个指向已打开文件的指针。

文件指针

C语言中使用文件指针做为I/O的句柄。文件指针指向进程用户区中的一个被称为FILE结构的数据结构。

FILE结构包括一个缓冲区和一个文件描述符。(库函数是系统调用的封装,所以文件流指针包含文件描述符)

而文件描述符是文件描述符表的一个索引,因此从某种意义上说文件指针就是句柄的句柄(在Windows系统上,文件描述符被称作文件句柄)。

当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件,于是就有了file结构体

表示一个已经打开的文件对象,而进程执行open系统调用,所有必须要让进程和文件关联起来,

每个进程都有一个*file指针,指向一张表files_struct ,这个最重要的就是包含一个指针数组,

每个元素都是一个指向打开文件的指针。

所以本质上文件描述符就是该数组的下标,只要拿着文件描述符,就能找到对应文件

 

 

 

 

猜你喜欢

转载自blog.csdn.net/W_J_F_/article/details/83622564