Linux系统编程之文件IO

Linux系统编程之文件IO

文件IO第一天

标准IO
打开文件:FILE* fopen(文件名字符串,打开模式)
r w a r+ w+ a+
(文本文件,二进制文件)
关闭文件: fclose(FILE* p);
读一个字符: int fgetc(FILE* P);
写一个字符: fputc(int c,FILE* P)
读一个字符串:fgets(char* buf,int size,FILE*)
写一个字符串:fputs(char* p,FILE* P);
char arr[4] = “abc” char* p = “abc”;(内存四区背完— 姜洋)
二进制读:fread(void* buf,每个块的大小,块个数,文件指针);
二进制写: fwrite(void*buf,每个块的大小,块个数,文件指针)
格式化读: int fscanf(FILE *stream, const char *format, …); 类似scanf
格式化写: int fprintf(FILE *stream, const char *format, …); 类似printf
文件偏移: fseek,rewind, ftell
(calloc,realloc) malloc -->memset() bzero( )

一、标准IO基本概念

1.1c语言函数 (c库函数) 调用实现某一个功能,(API)

       POSIX:可移植操作系统接口,软件需要在linux下运行,API关联的标准的总称

1.2系统调用:内核中的程序接口,应用程序和硬件设备之间的中间层

   1> 文件操作系统调用: 打开文件,关闭文件,读写
   2> 进程控制系统调用: 创建进程 关闭进程 ps -aux   
   3> 通信类控制系统调用: 发送消息,接受消息  socket套接字
   4> 设备管理系统调用: 打开设备 关闭设备
   5> 信息维护类系统嗲用:用户程序和操作系统之间的信息

1.3 文件基本概念

   文件:一组相关数据的集合,  文件名:集合的名称

1.4 文件分类 *

    普通文件:touch 文件
    目录文件: mkdir 目录名
    设备文件: 放在dev目录下,和普通文件一样
    字符文件: sudo mknod 字符设备名 c 主编号 次编号
    块  设 备:  sudo mknod 字符设备名 b 主编号 次编号 
    管道设备:  sudo mknod 管道设备名 p 主编号 次编号
    网络设备:  sudo mknod 网络设备名 s 主编号 次编号
    链接文件: 理解成快捷方式
        软连接:ln -s 被连接文件名 新文件名 ( 是保存了被连接文件的绝对路径,是另外一个新的文件,访问时用被连接文件的路径替换)
        硬链接:ln     被连接文件名 新文件名  (普通文件没区别,指向硬盘的同一块区域)快捷方式
       删除对应的被链接文件,软连接会受到影响,而硬链接不会。

1.5文件指针 FILE* (标准IO 和文件IO区别)

   typedef struct _iobuf{
         char* ptr ;   //下一个字符的位置
         int     cnt    //剩余字节数
         int*  base;  // 缓冲区的位置
         int  fd ;      // 文件描述符 (文件的标志)
         int flag ;     //文件的访问模式
   }  FILE

文件描述符:是一个非负整数,是一个索引指向内核为每个进程所维护打开文件的记录
三个默认的打开的流: stdin(0)输入流 stdout(1)输出流 stderr(2)标准错误流

1.6文件描述符和文件指针的转换*

    #include <stdio.h>
    函数原型:FILE *fdopen(int fd, const char *mode);
    函数作用:把fd的文件描述符转换成文件指针,当文件IO中打开文件,需要用标准IO中的函数时需要使用。
    函数参数:fd  文件描述符    mode打开模式   
    返 回  值:文件指针。
练习:找到标准输入的 文件指针。
 #include <stdio.h>
 #include <stdlib.h>
#include <string.h>
#include <errno.h>
int main()
{
   FILE * file = fopen("abc","r");
   FILE* p = fdopen(0,"r");
  if(p)
 {
    printf("p = %p\n",p);
 }
if(file == NULL)
 {
    //perror("fdopen");
    printf("%s\n",strerror(errno));
    exit(0);
   }
  return  0;
 }
  
    函数原型: int fileno(FILE *stream);
    函数作用:文件指针转换成文件描述符。
    函数参数:stream 是文件指针   
    返 回  值:文件描述符。
    使用:  标准io打开的文件fopen,用read去读取或者write写需要文件描述符,需要进行转换。

二、文件IO

2.1 打开文件*

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

  函数原型  int open(char *pathname, int flags, mode_t mode);
  函数参数:pathname 文件名或者文件路径
                   flags         打开模式
                   mode       只有创建时候使用 文件创建权限0666
  作      用: 打开文件
  返 回  值: 文件描述符,也是文件的打开标志,失败返回-1; 
  打开模式:
      第一组: 只读打开   O_RDONLY
                     只写方式:O_WRONLY
                     读写方式:O_RDWR
      第二组:  O_CREAT 创建文件,如果文件不存在则创建,需要和mode一起配合使用,mode的创建时候的文件权限
                     O_APPEND 追加的方式写入文件,如果文件已经有内容了,则在文件末尾添加内容,不会覆盖,文件末尾开始读不到内容。
        O_TRUNC 覆盖写,每一次写都会把原有内容 清空。

2.2 文件关闭 *

    函数原型: int close(int fd);
    头文件:  #include <unistd.h>
    作用: 关闭文件
    返回值: 是否成功,如果是0则关闭成功 -1关闭失败

   练习:创建danny.log文件  提示3秒后关闭
    #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
int fd = open("danny.log",O_RDWR| O_CREAT,0666);
if(fd == -1)
{
    printf("%s\n",strerror(errno));
    return 0;
}
sleep(1);
printf("倒计时1妙\n");
sleep(2);
close(fd);
} 

2.3 文件读写*

     #include <unistd.h>
   函数原型:  ssize_t read(int fd, void *buf, size_t count);
   函数参数: fd 文件描述符
                     buf  存放读取内容的缓存。(数组,malloc申请空间)
                     count:从文件中读取的字节数
   返回值:如果成功返回实际读取的字节数   如果失败返回-1,文件末尾返回0;
  
   函数原型: int write(int fd, const void *buf, long count);
   头  文  件: #include <unistd.h>
   作       用: 写入文件内
    参       数:  fd 文件描述符
                     buf  存放读取内容的缓存。(数组,malloc申请空间)
                     count:从文件中读取的字节数
   返回值:如果成功返回实际写入的字节数
               失败返回-1 错误信息错入errno

   练习:文件拷贝功能:./cp 文件1  文件2    (实现cp命令)

2.4 文件偏移*

     #include <sys/types.h>
    #include <unistd.h>

   函数原型:off_t lseek(int fd, off_t offset, int whence);
   作      用: 移动光标,移动文件偏移位置。
   参      数:whence    SEEK_CUR 从当前位置为餐卡偏移
                                   SEEK_SET  从文件头开始偏移
                                   SEEK_END  从文件尾开始偏移
                 offset:偏移多少,正数或者负数,正数往右,负数往
   注     意: socket文件--网络设备文件,管道文件没法移动,

2.5 文件权限 *

      函数原型:  int access(const char *pathname, int mode);
      头  文  件: #include <unistd.h>
      参       数: *pathname,文件名  ,mode 用来判断某一个模式
                        mode格式如下:
                        R_OK     判断文件是否有可读权限
                        W_OK    可写权限
                        F_OK      文件是否存在
                        X_OK       判断文件是否可执行
       返回值:如果是0则表示成功。失败返回-1  错误信息存入errno中
       练习:判断参数中的文件是否存在,如果存在判断有可执行权限

2.6 删除文件*

   函数原型:int remove(const char *pathname);
   头文件:  stdio.h
   作用: 删除文件
    返回值:如果成功返回0 失败返回-1  errno
    如果pathnam是目录,则需要调用rmdir()处理。
    如果pathname是文件,则会调用unlink()处理

2.7 fcntl 函数*

  函数原型: int fcntl(int fd, int cmd, ... /* arg */ );
  头 文 件:  #include <unistd.h>         #include <fcntl.h>
  参      数:  fd  文件描述符
                    cmd 命令
                   F_DUPFD      赋值文件描述符
                   F_GETFD      获取文件描述符标志
                   F_SETFD       修改文件描述符
                   F_GETFL       获取文件的状态标志
                   F_SETFL        设置文件的状态标志
                   F_GETLK       获取文件锁
                   F_SETLK        设置文件锁
                   F_GETOWN   获取信号处理
                   F_SETOWN    设置信号处理

                 第三个:操作命令的参数,比如说设置文件描述符,设置成多少需要用第三个
   返回值:如果是错误  返回-1
                返回文件描述符    返回文件锁   返回状态标志  


第一个重点:  文件状态获取和设置(可读可写)
      获取状态标志:通过第二个参数 F_GETFL 来获取到现有的状态标志。例如O_REDONLY  ,O_WRONLY, O_RDWR,然后用int变量接受,则得到的属性存入到了int中,
	把得到的返回值和某一个属性例如O_REONLY进行按位与操作     
     增加状态标志: 通过第二个参数F_SETFL来设置,第三个参数是设置的内容,我们可以通过 返回值 |= 你想为期增设的标志,最后调用fcntl函数保存标志。
     删除状态标志:      通过第二个参数F_SETFL来设置,第三个参数是设置的内容,我们可以通过 返回值 &=你想为期增设的标志的~来设置,最后调用fcntl函数保存标志。

     第二个重点 :设置IO设备非堵塞  F_SETFL
     设置: O_NONBLOCK 属性。
     第一种设置方式: 在open的时候添加非堵塞属性  
     第二种设置方式:使用fcntl函数添加非堵塞属性。
 
第三个重点:设置文件锁 F_SETLK    F_GETLK
     对文件枷锁后,只允许一个进程读写,其他进程等待读写,直到解锁。
      struct flock {
           ...
           short l_type;    /* 锁的类型: F_RDLCK, F_WRLCK, F_UNLCK */
           short l_whence;  /*偏移量的起始位置,枷锁的参考位置  SEEK_SET, SEEK_CUR, SEEK_END */
           off_t l_start;   /* 正数表示往后偏移后枷锁, */
           off_t l_len;     /* 枷锁多少个字节 */  如果字节数为0默认为偏移位置到文件结束。
           pid_t l_pid;     /* 锁的进程id号
             ...
       };
   代码:先定义机构体,给结构体成员赋值,fcntl(fd,F_SETLK,&结构体);
   struct flock  lock;
   lock.type =F_RDLCK;
   lock.whence = SEEK_SET;
   lock.start = 3;
   lock.len = 0;
   fcntl(fd,F_SETLK,&lock); 

文件IO第二天

一、

  1. 缓冲区: 标准io是有缓冲区的,输入输出数据先放入缓存,当缓存满了或者缓存遇到\n则会刷新,把数据刷新到屏幕等。
    #include <stdio.h>
    int fflush(FILE *stream);
    作用:刷新缓存区,需要用到FILE,因为文件IO是没有缓存区的,可以刷新输入缓存或者输出缓存,例如 fflush(stdout);

引入: open 使用创建属性 O_CREAT, 0666

1.创建文件 【重点】

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

  参数:pathname 文件名/文件路径  
            mode   创建模式  0666
  先从终端 输入一个文件名,如果文件存在则打印存在,如果不存在则创建文件。   ./a.out  danny.txt   
    练习:从终端输入文件名的参数,先判断文件是否存在,如果不存在则创建。

2. fcntl文件加锁

      对文件枷锁后,只允许一个进程读写,其他进程等待读写,直到解锁。
      struct flock {
           ...
           short l_type;    /* 锁的类型: F_RDLCK, F_WRLCK, F_UNLCK */
           short l_whence;  /*偏移量的起始位置,枷锁的参考位置  SEEK_SET, SEEK_CUR, SEEK_END */
           off_t l_start;   /* 正数表示往后偏移后枷锁, */
           off_t l_len;     /* 枷锁多少个字节 */  如果字节数为0默认为偏移位置到文件结束。
           pid_t l_pid;     /* 锁的进程id号
             ...
       };
   代码:先定义机构体,给结构体成员赋值,fcntl(fd,F_SETLK,&结构体);
   struct flock  lock;
   lock.type =F_RDLCK;
   lock.whence = SEEK_SET;
   lock.start = 3;
   lock.len = 0;
   fcntl(fd,F_SETLK,&lock); 

二. 时间函数

1. 函数原型: time_t time(time_t *); 【重点】

     参      数:   timt_t为	long int类型 的指针  或者NULL
     返 回 值:从公元1970年1月1号UTC 0点0分0秒到现在的秒数。

2. 函数原型:char *ctime(const time_t *);【重点】

       参      数: time_t 是从1970年1月1号0点到现在的时间  time();
       返回值:  时间字符串信息,本地时间的日期和事件
       作用:获取本地时间,会自动在时间字符串后面加 \r \n自动换行

3.函数原型: struct tm *gmtime(const time_t *);

   作用:转换成真实世界所使用的时间和日期表示方式,返回给tm结构体
   参数: time_t是从1970年到现在的秒数  time函数的返回值
   返回值: tm结构体,保存的是真实世界的时间。
    结构体信息如下:
    struct tm    {【重点】
   int tm_sec;			/* 秒数	[0-60] (1 leap second) */
	 int tm_min;			/* 分钟数	[0-59] */
  int tm_hour;			/* 小时	[0-23] */
	 int tm_mday;			/* 一个月中的日期		[1-31] */
  int tm_mon;			/* 月份[0-11] */
  int tm_year;			/* 年份	- 1900.  */
  int tm_wday;			/* 周中的天数(星期数).	[0-6] */
  int tm_yday;			/* 一年中的哪一天.[0-365]	*/
   }

4. 函数原型:char *asctime(const struct tm *);

     参数:  tm*   结构体指针,保存的是国际时间的结构体信息
     作用:  把国际时间转换成本地时间字符串
     返回值:时间字符串

5. 函数原型: struct tm *localtime(const time_t *); 【重点】

      参      数:  time_t 是time()函数的返回值
      作      用:  把1970年1月1号到现在的秒数转换成本地时间
      返 回  值: tm结构体指针

6. 函数原型: time_t mktime(struct tm *tm);

         参数: tm*的结构体
          返回值: time_t   在time函数中的参数中用过
         作用: 把tm结构体的时间转换成1970年1月1号到现在的秒数。

三、文件属性函数

函数原型: int stat(const char *pathname, struct stat *buf);【重点】

  头 文 件:       #include <sys/types.h>       #include <sys/stat.h>       #include <unistd.h>
  作      用: 获取文件的属性信息
  参      数: pathname  文件路径或者文件名     buf 是结构体指针,保存的是文件属性信息
  返 回 值:  如果成功返回0  如果是失败返回-1 
 结构体解析:
      struct stat {
             dev_t     st_dev;         /* 文件使用的设备号 */
             ino_t     st_ino;         /* inode 设备的次设备号*/
             mode_t    st_mode;        /* 文件的属性(目录 设备 链接)文件对应的模式 */
            nlink_t   st_nlink;       /* 硬链接数*/
            uid_t     st_uid;         /* 拥有者的用户ID */
            gid_t     st_gid;         /* 组用户id */
            dev_t     st_rdev;        /* 设备id */
            off_t     st_size;        /* 文件的总字节数*/
            blksize_t st_blksize;     /* 磁盘块大小4k */
            blkcnt_t  st_blocks;      /*占用的磁盘数 */ 
           struct timespec st_atim;  /* 最后一次访问的时间 */
           struct timespec st_mtim;  /* 最后一次修改的时间 */
           struct timespec st_ctim;  /* 最后一次改变状态的时间 */
     }

   详解stat结构体中的 st_mode;        
   总共16位 (short类型)
   文件类型属性:(st_mode;  的前4位)
       S_ISDIR(m)  目录文件
       S_ISCHR(m)  字符设备
       S_ISBLK(m)  块设备
       S_ISFIFO(m) 管道设备
       S_ISLNK(m)  s连接设备
       S_ISSOCK(m) 网络设备

   文件权限( (st_mode;  的后9位))
       S_IRWXU     00700   拥有者有可读可写可执行权限 rwx
       S_IRUSR     00400   拥有者有可读权限 r
       S_IWUSR     00200   拥有者有可写权限 w
       S_IXUSR     00100   拥有者有可执行权限 x
       S_IRWXG      组的权限          S_IRGRP            S_IWGRP               S_IXGRP  
      S_IRWXO        (other)其他人权限    S_IROTH            S_IWOTH          S_IXOTH

3.2 删除文件 remove()调用unlink 【重点】

      #include <unistd.h> 
      函数原型:int unlink(const char *pathname);
      返回值: 0表示成功   -1表示失败
      作用: 删除文件

3.3 截取文件内容 【重点】

   函数原型:       int truncate(const char *path, off_t length);
   头文件:#include <unistd.h>          #include <sys/types.h>
   参  数 :  path  文件路径 或者文件名
                 length  需要截取的长度
  返回值: 0表示成功   -1表示失败
 函数作用: 从path文件中文件首位置开始截取length个字节。

3.4 文件权限修改

   #include <sys/stat.h>
    函数原型:       int chmod(const char *pathname, mode_t mode);
    参     数 :   pathname 文件名
                      mode是文件新的权限   0666
    作用: 修改文件权限
   返回值: 如果成功返回0 失败返回-1

3.5 修改权限掩码

     #include <sys/types.h>
     #include <sys/stat.h>
     函数原型:    mode_t umask(mode_t mask);
     参数:  mask 新的权限掩码
    返回值: 返回老的权限掩码

四、目录操作函数

   #include <sys/stat.h>
   #include <sys/types.h>
   函数原型:int mkdir(const char *pathname, mode_t mode);  【重点】
   作用: 创建目录
   参数: pathname 文件路径 目录路径  目录名
              mode  创建权限  0777
    返回值: 成功0  失败-1

    #include <unistd.h>
   函数原型:int rmdir(const char *pathname); 【重点】
   作用:  删除目录  (空目录)
   参数: pathname  目录  
  返回值: 成功0  失败-1 
   
   #include <sys/types.h>
   #include <dirent.h>
    函数原型: DIR *opendir(const char *name); 【重点】
    作用:  打开目录  相当于cd 目录名
    参数:  name  目录名
    返回值: dir的结构体指针
   
#include <sys/types.h>
   #include <dirent.h>
   函数原型: int closedir(DIR *dirp); 【重点】
    参数:    dirp  是opendir的返回值
   作用: 关闭目录
  返回值: 成功0  失败-1 

   #include <dirent.h>
  函数原型: struct dirent *readdir(DIR *dirp); 【重点】
  参数: dirp 结构体指针
  返回值: 独到的文件或者目录信息  dirent结构体指针,失败返回NULL
 作用: 读取目录,按照文件一个一个的读取。
 返回值结构体:struct dirent {
                           ino_t          d_ino;       /* inode 数目 次设备号  inode节点 */
                           off_t          d_off;       /*现在偏移的位置*/
                           unsigned short d_reclen;    /* 目录的长度 */
                           unsigned char  d_type;      /* 文件类型  d  s  l  c  b */
                           char           d_name[256]; /* 文件名 */
       };
   struct dirent中d_type中的宏定义如下:
       DT_BLK  块设备     DT_CHR  字符设备     DT_DIR  目录         DT_REG  普通文件     DT_LNK  连接

文件IO第三天

一、目录函数

#include <sys/types.h>
#include <dirent.h>
函数原型:void rewinddir(DIR *dirp);
函数参数: dirp 这个是open的返回值
作 用:重新设置目录的起始位置,把目录偏移设置到起始位置。

函数原型 void seekdir(DIR *dirp, long loc);
函数参数:dirp 是open的返回值
loc 是目录起始位置到偏移位置的长度(偏移量)
作用:设置目录的偏移

函数原型: long telldir(DIR *dirp);
函数参数: open的返回值  DIR*的结构体指针。
返  回 值: 当前目录的偏移位置
作       用:   查看当前目录的偏移位置

  #include <unistd.h>
 函数原型:  int chdir(const char *path);
 函数作用:  跳转到某一个目录中
 参数 :      需要跳转的目标目录

二、库文件基础

2.1库文件特点:
       对源文件屏蔽 。
       尊重开发者的知识产权
       使用方便,方便开发。
2.2 库文件概念
      本质是上库文件是可执行的二进制文件,可以被操作系统载入内存执行
     库是别人写好,成熟的,可以重复使用的某一部分功能。
     windows和linux的库文件是不一样的,不兼容。
2.3 库文件分类
动态库:(在windows下是dll结尾,在linux下是.so文件)
          1>在程序运行的时候加载到目标程序中,编译不检测。
           2> 代码小 ,程序升级简单
           3>实现资源共享
静态库:(在windows下是lib文件,在linux下是.a文件)
         1>在程序编译阶段连接到目标文件
          2>体积大,不利于程序的更新
          3>程序运行运行过程中不需要静态库,移植方便。
   注意:动态库和静态库是寄生虫,没办法单独运行,单独使用。

三、静态库编写

  第一步:创建一个源文件和头文件 比如说xx.c  xx.h
  第二步: 在文件中按照多文件编译写需要的函数代码。
  第三步:将库的源文件编译成目标文件 xx.o
                gcc -c x.c -o xx.o
  第四步: 将目标文件制作成静态库(lib库名.a)
              命令:ar crs lib库名.a  -o xxx.o
  第五步: 编写测试代码  有main函数的代码(调用静态库函数)
  第六步: 编译xx.c文件同时链接库文件
              gcc -o abc xxx.c -L 目录  -l 库名
   第七步:执行可执行程序
   注意: gcc -o abc xxx.c -L 目录  -l 库名中 -L后不可以有空格, -l后也不可以有空格, -l后直接加静态库名称不需要加lib的前缀和.a后缀

四、动态库编写

  第一步:创建一个源文件和头文件 比如说xx.c  xx.h
  第二步: 在文件中按照多文件编译写需要的函数代码。
  第三步:将库的源文件编译成目标文件 xx.o
             命令:  gcc -fPIC  -c  xxx.c   
             fPIC 创建和地址无关的编译程序
   第四步:将.o文件编译成动态库(产生了xx.so)
             命令:gcc -shared  -fPIC -o xxxx.so  xxx.o
  第五步: 编写测试代码  有main函数的代码(调用动态库函数)
  第六步:将编译的xx.c文件编译连接动态库
           命令: gcc -o abc xxx.c -L目录  -l库名
  第七步: 运行可执行程序。
  注意: 在编译完so文件后,需要放入到/lib 或者/usr/lib下
              在gcc时 -L不可以省略 并且 -l后如果加空格则需要加动态库的后缀名 如果不加空格 则直接使用名称不用后缀。
              运行时动态库必须存在,否则没法找到函数地址,运行出错,但静态库不会,编译完成后删除不会影响代码运行。

猜你喜欢

转载自blog.csdn.net/qq_52531759/article/details/127181823