文件的基础
-  概念:一组相关数据的有序集合 
-  文件的类型: 
 常规文件-r
 目录文件-d
 字符设备文件-c:键盘
 块设备文件-b:U盘 磁盘
 管道文件-p
 套接字文件-s
 符号链接文件-I:快捷方式
标准I/O 流
-  file 
 标准IO用一个结构体类型来保存打开的文件的相关信息
 标准的IO操作都是围绕file来进行的
-  流(stream) 
 file有被称为流(stream)
 文本流/二进制流
  
文本流/二进制流
-  window : 
 二进制流:换行符 ‘/n’
 文本流:换行符:‘/r’ ‘/n’
-  linux: 
 换行符:‘/n’
流的缓冲的类型
- 全缓冲:缓冲区写满再将数据写入磁盘
- 行缓冲:当行结束时,将数据写入磁盘
- 无缓冲:直接读写磁盘
- 标准的输入流
- 标准的输出流
- 标准的错误流
文件的打开和关闭
-  打开:占用资源 
-  关闭:释放资源 
-  打开函数:FILE *fopen (const char *path, const char *mode); 
-  
  - Path:普通文件当前路径不需要加目录,其他要使用完整的路径
 
-  
  - Mode:
  
 
- Mode:
-  
  - 返回值:出现错误返回NULL,使用必须判断是否为空
 
文件的关闭
函数:int fclose(FILE *stream)
- fclose()调用成功返回0,失败返回EOF(-1),并设置errno
- fclose()函数的入参stream必须保证为非空,否则出现断错误。
打开关闭练习
#include <errno.h>
#include <stdio.h>
#include <string.h>
int main() {
  FILE *fp;
  int ret;
  fp = fopen("test.txt", "r");
  if (fp = NULL) {
    perror("fopen");
    printf("fopen:%s\n", strerror(errno));
  } else {
    printf("Open file success\n");
  }
  ret = fclose(fp);
  if (ret == 0) {
    printf("file close sucess\n");
  } else {
    perror("fclose");
    printf("fclose:%s\n", strerror(errno));
  }
  return 0;
}
字符的输入
读单个字符
- int fgetc(FILE *stream);
- int getc(FILE *stream); //宏
- int getchar(void);
成功时返回读取的字符;若到文件末尾或出错时返回EOF(-1),
 getchar()等同于fgetc(stdin)
 getc和fgetc区别是一个是宏一个是函数
读取整个行
- char *gets(char *s); 读取标准输入到缓冲区s
- char *fgets(char *s, int size, FILE *stream);
 成功时返回s,到文件末尾或出错时返回NULL
 遇到’\n’或已输入size-1个字符时返回,总是包含’\0’
字符的输出
写单个字符
- int fputc(int c, FILE *stream);
- int putc(int c, FILE *stream);
- int putchar(int c);
 成功时返回写入的字符;出错时返回EOF
 putchar©等同于fputc(c, stdout)
读取整个行
- int puts(const char *s);
- int fputs(const char *s, FILE *stream);
成功时返回非负整数;出错时返回EOF
 puts将缓冲区s中的字符串输出到stdout,并追加’\n’
 fputs将缓冲区s中的字符串输出到stream,不追加 ‘\n’
二进制读写
文本文件和二进制的区别:
 存储的格式不同:文本文件只能存储文本。
二进制读写函数格式:
-  size_t fread(void *ptr, size_t size, size_t n, FILE *fp); 
 void *ptr 读取内容放的位置指针
 size_t size 读取的块大小
 size_t n 读取的个数
 FILE *fp 读取的文件指针
-  size_t fwrite(const void *ptr, size_t size, size_t n, FILE *fp); 
 void *ptr 写文件的内容的位置指针
 size_t size 写的块大小
 size_t n 写的个数
 FILE *fp 要写的文件指针
流的刷新
int fflush(FILE *fp);
成功时返回0;出错时返回EOF
 将流缓冲区中的数据写入实际的文件
 Linux下只能刷新输出缓冲区,输入缓冲区丢弃
如果输出到屏幕使用fflush(stdout)
流的定位:
long ftell(FILE *stream);
 long fseek(FILE *stream, long offset, int whence);
 void rewind(FILE *stream);
 fseek 参数whence参数:SEEK_SET/SEEK_CUR/SEEK_END
 SEEK_SET 从距文件开头 offset 位移量为新的读写位置
 SEEK_CUR:以目前的读写位置往后增加 offset 个位移量
 SEEK_END:将读写位置指向文件尾后再增加 offset 个位移量
 offset参数:偏移量,可正可负
 注意事项:
 1.文件的打开使用a模式 fseek无效
 2.rewind(fp) 相当于 fseek(fp,0,SEEK_SET);
 3.这三个函数只适用2G以下的文件
格式化输出(重要)
int fprintf(FILE *stream, const char *fmt, …);
 int sprintf(char *s, const char *fmt, …);
成功时返回输出的字符个数;出错时返回EOF
格式化输入
int fscanf(FILE *stream, const char *format, …);
 int sscanf(const char *str, const char *format, …);
文件IO的API
文件描述符概念:
 英文:缩写fd(file descriptor)
 是0-1023的数字,表示文件。
 0, 1, 2 的含义 标准输入,标准输出,错误
- 文件IO 打开 open
 int open(const char *pathname, int flags); 不创建文件
 int open(const char *pathname, int flags, mode_t mode); 创建文件,不能创建设备文件
 成功时返回文件描述符;出错时返回EOF
文件IO和标准的模式对应关系:
 r—— O_RDONLY
 r+—— O_RDWR
 w —— O_WRONLY | O_CREAT | O_TRUNC, 0664
 w+ —— O_RDWR | O_CREAT | O_TRUNC, 0664
 a —— O_WRONLY | O_CREAT | O_APPEND, 0664
 a+ —— O_RDWR | O_CREAT | O_APPEND, 0664
- umask概念:
 umask 用来设定文件或目录的初始权限
- 文件的关闭
 int close(int fd)
 关闭后文件描述符不能代表文件
对目录的操作
打开目录
#include <dirent.h>
 DIR *opendir(const char *name);
 DIR *fdopendir(int fd); 使用文件描述符,要配合open函数使用
 DIR是用来描述一个打开的目录文件的结构体类型
 成功时返回目录流指针;出错时返回NULL
读取目录
#include <dirent.h>
 struct dirent *readdir(DIR *dirp);
struct dirent是用来描述目录流中一个目录项的结构体类型
 包含成员char d_name[256] 参考帮助文档
 成功时返回目录流dirp中下一个目录项;
 出错或到末尾时时返回NULL
关闭目录
closedir函数用来关闭一个目录文件:
 #include <dirent.h>
 int closedir(DIR *dirp);
成功时返回0;出错时返回EOF
修改文件权限
chmod/fchmod函数用来修改文件的访问权限:
 #include <sys/stat.h>
 int chmod(const char *path, mode_t mode);
 int fchmod(int fd, mode_t mode);
成功时返回0;出错时返回EOF
获取文件属性
#include <sys/stat.h>
 int stat(const char *path, struct stat *buf);
 int lstat(const char *path, struct stat *buf);
 int fstat(int fd, struct stat *buf);
成功时返回0;出错时返回EOF
 如果path是符号链接stat获取的是目标文件的属性;而lstat获取的是链接文件的属性
练习:使用文件IO实现“每隔1秒向文件1.txt写入当前系统时间,行号递增”
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
int main() {
  int fd;
  int ret;
  time_t ctime;
  struct tm *ctimestr;
  int linecount = 0;
  char rbuf[32];
  char wbuf[32];
  fd = open("1.txt", O_RDWR | O_CREAT | O_APPEND, 0666);
  if (fd < 0) {
    perror("open");
    return 0;
  }
  while (1) {
    memset(rbuf, 0, sizeof(rbuf));
    ret = read(fd, rbuf, sizeof(rbuf)); //写入字符串长度 "\0结束"
    if (ret < 0) {
      perror("write");
      ret = close(fd);
      return 0;
    }
    if (ret == 0) {
      break;
    }
    if (strcmp(rbuf, "\n")) {
      linecount++;
    }
  }
  while (1) {
    ctime = time(NULL);           //获取当前时间
    ctimestr = localtime(&ctime); //转换为结构体格式
    sprintf(wbuf, "%d, %04d-%02d-%02d %02d:%02d:%02d\n", linecount,
            ctimestr->tm_year + 1900, ctimestr->tm_mon + 1, ctimestr->tm_mday,
            ctimestr->tm_hour, ctimestr->tm_min, ctimestr->tm_sec);
    printf("wbuf:%s", wbuf);
    ret = write(fd, wbuf, sizeof(wbuf)); 
    if (ret < 0) {
      perror("write");
      ret = close(fd);
      return 0;
    }
    linecount++;
    sleep(1);
  }
  ret = close(fd);
  if (ret < 0) {
    perror("close");
    return 0;
  }
  return 0;
}
练习:遍历一个文件夹下所有文件,并打印文件权限、大小和日期
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#define FILENAME "./day5_1"
int main() {
  DIR *dp;
  struct dirent *dt;
  struct stat buf;
  int ret;
  struct tm *t;
  dp = opendir(FILENAME);
  if (dp < 0) {
    perror("opendir");
    return 0;
  }
  while ((dt = readdir(dp)) != NULL) {
    if (strcmp(dt->d_name, ".") == 0 || strcmp(dt->d_name, "..") == 0) {
      continue;
    }
    // printf("%s\n", dt->d_name);
    char name[64] = {0};
    memset(name, 0, sizeof(name));
    memcpy(name, FILENAME, sizeof(FILENAME));
    strcat(name, "/");
    strcat(name, dt->d_name);
    // printf("%s\n", name);
    ret = stat(name, &buf);
    if (ret < 0) {
      perror("stat");
      return 0;
    }
    if (S_ISREG(buf.st_mode)) {
      printf("-");
    }
    if (S_ISDIR(buf.st_mode)) {
      printf("d");
    }
    if (S_ISCHR(buf.st_mode)) {
      printf("c");
    }
    if (S_ISBLK(buf.st_mode)) {
      printf("b");
    }
    if (S_ISFIFO(buf.st_mode)) {
      printf("p");
    }
    if (S_ISSOCK(buf.st_mode)) {
      printf("s");
    }
    for (size_t i = 0; i < 8; i++) {
      if (buf.st_mode & (1 << i)) {
        switch (i % 3) {
        case 2:
          printf("r");
          break;
        case 1:
          printf("w");
          break;
        case 0:
          printf("x");
          break;
        }
      } else {
        printf("-");
      }
    }
    printf(" %d", (int)buf.st_size);
    t = localtime(&buf.st_ctime);
    printf(" %d-%d-%d %d:%d:%d", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
           t->tm_hour+8, t->tm_min, t->tm_sec);//小时+8北京时间
    printf(" %s\n", dt->d_name);
  }
  closedir(dp);
  return 0;
}



















