C基础之-文件基础

文件基础

概念:

  一组相关数据的有序集合

 

文件类型

不同操作系统支持的文件类型是不相同的,这里列出的是linux

  • 常规文件  r
  • 目录文件  d
  • 字符设备文件  c
  • 块设备文件  b
  • 管道文件  p
  • 套接字文件  s
  • 符号链接文件  l

 

标准I/O

  由ANSI C标准定义

  主流操作系统上都实现了C库

  特点: 标准I/O通过缓冲机制减少系统调用,实现更高的效率

 

FILE

  标准IO用一个结构体类型来存放打开文件的相关信息

  标准IO的所有操作都是围绕FILE来进行的

 

流(stream)

  FILE又被称为流(stream)又被分为文本流、二进制流两种

 

windows:

  二进制流:换行符 -- '\n'

  文本流:换行符 -- '\r''\n'

 

linux

  换行符:'\n'

 

流的缓冲类型

  全缓冲:

    当流的缓冲区无数据或空间时才执行实际I/O操作

  行缓冲:

    当在输入和输出中遇到换行符时,才执行I/O操作

    当流和一个终端关联时,典型的行缓冲

  无缓冲:

    数据直接写入文件,流不进行缓冲

 

 

标准I/O预定的3个流,程序运行时自动打开

标准输入流 0 STDIN_FILENO stdin
标准输出流 1 STDOUT_FILENO stdout
标准错误流 2 STDERR_FILENO stderr

 

 

流打开

FILE *fopen(const char * path,const char *mode)

成功时返回流指针;错误返回NULL

- 当用fopen新建文件后,该文件的权限为0666(rw-rw-rw-)

- 在linux系统中 umask设定会影响文件的权限访问 可以通过umask函数进行对应的修改

 

mode参数:

"r"或"rb" 以只读的方式打开文件,文件必须存在
"r+"或"r+b" 以读写的方式打开文件,文件必须存在
"w"或"wb" 以只写的方式打开文件,若文件存在则文件长度清零。若文件不存在则创建
"w+"或"w+b" 以只写方式打开文件,其他同"w"
"a"或"ab" 以只写的方式打开文件,若文件不存在则创建;向文件写入的数据将被追加到文件末尾
"a+"或"a+b" 以读写的方式打开文件。其他同"a"

*当给"b"参数时候,表示以二进制方式打开文件,但Linux下忽略该参数

 

错误流处理

void  perror(const char *)

输入提示关键字 捕获当前错误并加输入的关键字返回

 

打印错误信息 例如

int main() {
   FILE *fp;
   if ((fp = fopen("test","r+"))==NULL){
       perror("fopen");
       return -1;
   }
}

// out
// fopen: No such file or directory 

 

#include  <stdio.h>
#include <string.h> // 需要引入
#include <errno.h> // 需要引入 标准错误输入


int main() {
   FILE *fp;
   if ((fp = fopen("test","r+"))==NULL){
       printf("fopen:%s",strerror(errno));
       return -1;
   }
}

 

判断是否出错或结束

#include <stdio.h>
int ferror(FILE *stream);
int feof(FILE *stream);

// ferror()返回1表示流出错;否则返回0
// feof()返回1表示已经到末尾,否则返回0

 

 

读写流

流支持不同的读写方式

读写一个字符:

// fgetc()/fputc() 一次读/写一个字符

输入

#include <stdio.h>
int fgetc(FILE *stream);
int getc(FILE *stream);
int getchar(void);

// 成功时返回读取的字符
// 若到文件的末尾或出错返回EOF

 

Demo:

#include  <stdio.h>

int main() {
    int ch;
    ch = fgetc(stdin); // 从标准输入获取
    printf("%c", ch);
}

 

#include  <stdio.h>

int main() {
    FILE *fp;
    int ch = 0;
    int i = 0;
    int count = 0;
    if ((fp = fopen("./a.out", "r")) == NULL) {
        perror("fopen");
        return -1;
    }
    while ((ch = fgetc(fp)) != EOF) {
        count++;
    }
    printf("文本长度 :%d", count);
}

 

输出

#include <stdio.h>
int fputc(int c,FILE *stream);
int putc(int c,FILE *stream);
int putchar(int c);

// 成功时返回写入的字符
// 出错时返回EOF

 

Demo:

 

#include  <stdio.h>

int main() {
    putc('a',stdout);
    putchar('\n');
}

// out
// a
// 

 

#include  <stdio.h>

int main() {
    FILE *fp;
    int ch = 0;

    if ((fp = fopen("./a.out", "w")) == NULL) {
        perror("fopen");
        return -1;
    }
    for (ch = 'a'; ch <= 'z', ch++;) {
        fputc(ch,fp); // 输出到文本
    }
}

 

文件copy

#include  <stdio.h>

int main(int argc, char *argv[]) {
    FILE *src, *desc;
    int i;
    if (argc < 3) {
        puts("Few parameters:project : <src> <desc>");
        return -1;
    }
    // 打开目标文件
    if ((src = fopen(argv[1], "r")) == NULL) {
        perror("src:");
        return -1;
    }
    if ((desc = fopen(argv[2], "w")) == NULL) {
        perror("desc:");
        fclose(src); // 失败释放打开的句柄
        return -1;
    }
    while ((i = fgetc(src)) != EOF) {
        fputc(i, desc);
    }
    fclose(src);
    fclose(desc);
}

 

读写一行:

// fgets()/fputs() 一次读/写一行

输入

#include <stdio.h>
char *gets(char *s);
char *fgets(char *s,int size,FILE *stream);

// 成功时返回s,到文件末尾或出错时返回NULL
// gets不推荐使用,容易造成缓冲区溢出;
// ***遇到'\n'或已输入size-1个字符返回时,总是包含'\0'

 

输出

#include <stdio.h>
int puts(const char *s);
int fputs(const char *s,FILE *stream);

// 成功时返回输出的字符个数
// 出错返回EOF
// puts将缓冲区s中的字符串输出到stdout,并追加'\n'

 

demo:

#include  <stdio.h>

int main(int argc, char *argv[]) {
    FILE *fp;
    char buf[] = "hello world";
    if ((fp = fopen(argv[1], "a")) == NULL) {
        perror("fopen");
        return -1;
    }
    fputs(buf, fp);
}

 

读写若干个对象:

// fread()/fwrite() 每次读/写若干个对象,而每个对象具有相同的长度

 

#include <stdio.h>
size_t fread(void *ptr,size_t size,size_t n,FILE *fp); // 容器指针,存储元素的长度,读取的长度,文件句柄
size_t fwrite(const void *prt,size_t size,size_t n,FILE *fp);

// 成功返回读写的对象个数
// 出错返回EOF

 

demo

#include  <stdio.h>

int main(int argc, char *argv[]) {
    FILE *fp;
    int buf[10] = {};
    if ((fp = fopen(argv[1], "a")) == NULL) {
        perror("fopen");
        return -1;
    }
    if (fread(buf, sizeof(int), 10, fp) < 0) {
        perror("fread");
        return -1;
    }
}

 

copy文件

#include  <stdio.h>

#define N 1024

int main(int argc, char *argv[]) {
    FILE *src, *desc;
    int i[N] = {};
    int n = 0;
    if (argc < 3) {
        puts("Few parameters:project : <src> <desc>");
        return -1;
    }
    // 打开目标文件
    if ((src = fopen(argv[1], "r")) == NULL) {
        perror("src:");
        return -1;
    }
    if ((desc = fopen(argv[2], "w")) == NULL) {
        perror("desc:");
        fclose(src); // 失败释放打开的句柄
        return -1;
    }
    if ((n = fread(i, sizeof(int), N, src)) > 0) {
        fwrite(i, n, sizeof(int), desc);
    }
    fclose(src);
    fclose(desc);
}

 

 

刷新流 

  fflush()

#include <stdio.h>
int fflush(FILE *fp);

// 成功时返回0
// 出错返回EOF

刷新会将缓冲区的内容落盘

 

 

定位流

  ftell/fseek/rewind

#include <stdio.h>
long ftell(FILE *stream); 
long fseek(FILE *stram,long offset,int whence);  // offset表示偏移量 可正可负  whence表示基准点
void rewind(FILE *stram);

// ftell()成功时返回流的当前读写位置,错误时返回EOF
// fseek()定位一个流,成功时返回0,出错时返回EOF
// whence参数:SEEK_SET-文件开头/SEEK_CUR-当前位置/SEEK_END-文件结尾
//  rewind将流定位到起始位置
// 读写流时,当前读写位置自动后移

 

Demo:

在文件末尾增加字符't'

#include  <stdio.h>

int main(int argc, char *argv[]) {
    FILE *fp;
    // 打开目标文件
    if ((fp = fopen(argv[1], "r")) == NULL) {
        perror("src:");
        return -1;
    }
    // 移动到文章末尾
    fseek(fp, 0, SEEK_END);
    // 增加字符t
    fputc('t', fp);
    fclose(fp);
}

 

获取文件长度

#include  <stdio.h>

int main(int argc, char *argv[]) {
    FILE *fp;
    int i;
    // 打开目标文件
    if ((fp = fopen(argv[1], "r")) == NULL) {
        perror("src:");
        return -1;
    }
    // 移动到文章末尾
    fseek(fp, 0, SEEK_END);
    // 获取文件长度
    i = ftell(fp);
    printf("长度为%d",i);
    fclose(fp);
}

 

格式化输出

#include <stdio.h>
int printf(const char *fmt,...);
int fprintf(FILE *stream,const char *fmt,...)
int sprintf(char *s,const char *fmt,...)

// 成功时返回输出的字符个数
// 出错时返回EOF

 

#include  <stdio.h>

int main(int argc, char *argv[]) {
    FILE *fp;
    int year, month, day;
    char buf[64];
    // 打开目标文件
    if ((fp = fopen(argv[1], "r")) == NULL) {
        perror("src:");
        return -1;
    }
    year = 2020;
    month = 05;
    day = 05;
    // 输入到fp的流中
    fprintf(fp, "%d-%d-%d", year, month, day);
    // 输入到缓冲区中
    sprintf(buf, "%d-%d-%d", year, month, day);
}

 

 

关闭流

int fclose(FILE *stream);

调用成功返回0,失败返回EOF,并设置errno

流成功关闭后会自动刷新缓冲中的数据并释放缓冲区

当一个程序正常终止时,所有打开的流都会被关闭

流一旦关闭将不能对其执行任何操作

 

在linux中打开流的最大个数(修改ulimit可更改)

1021 + stdin + stdout + stderr = 1024

 

 

 

 

文件I/O

  POSIX(可移植操作系统接口)定义的一组函数

  不提供缓冲机制,每次读写操作都引起系统调用

  核心概念是文件描述符

  可以访问各种类型文件

  在Linux下,标准IO基于文件IO实现

 

文件描述符

  每一个打开的文件都对应一个文件描述符

  文件描述符是一个非负整数。Linux为程序中每打开的问你件分配一个文件描述符

  文件描述符从0开始分配,一次递增

  文件IO操作通过文件描述符来完成

  

文件描述符的0,1,2的含义

  分别是标准输入,标准输出,标准错误 一一对应

 

 

打开文件、创建文件

#include <fcntl.h>
int open(const char *path,int oflag,mode_t mode);

// path文件路径
// oflag打开方式,可以是多个参数
// 当open成功时返回文件描述符
// 出错返回EOF
// 创建文件时第三个参数指定文件的权限
// 设备文件只能打开不能创建

 

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 所指的文件并非为一目录, 则会令打开文件失败。注:此为Linux2. 2 以后特有的旗标, 以避免一些系统安全问题. 

参数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 权限, 代表其他用户具有可执行的权限.

 

Demo:

#include  <stdio.h>
#include <fcntl.h>

// 以只写的方式打开文件,如果不存在则创建,如果存在则清空
int main(int argc, char *argv[]) {
    int fd;
    if ((fd = open("1.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) {
        perror("open");
    }
}

 

#include  <stdio.h>
#include <fcntl.h>
#include <string.h> // 需要引入
#include <errno.h> // 需要引入 标准错误输入

// 以读写的方式打开文件,如果不存在则创建,如果存在则报错
int main(int argc, char *argv[]) {
    int fd;
    if ((fd = open("1.txt", O_RDWR | O_CREAT, 0666)) < 0) {
        if (errno == EEXIST) {
            perror("exist error");
        } else {
            perror("other error");
        }
    }
}

 

读取文件

#include <unistd.h>
ssize_t read(int fd,void *buf,size_t count); // buf容器 count 一般定为缓冲区的大小 保证缓冲区不会溢出

// 成功返回实际读取的字节数
// 失败返回EOF
// 读到文件尾时返回0

 

demo:统计文件大小

#include  <stdio.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char *argv[]) {
    int fd, n, total;
    char buf[64];
    if ((fd = open(argv[1], O_RDONLY)) < 0) {
        perror("open");
        return -1;
    }
    // 读取文件的内容
    // 获取文件大小
    while ((n = read(fd, buf, 64)) > 0) {
        total += n;
    }
}

 

写入文件

#include <unistd.h>
ssize_t write(int fd,void *buf,size_t count);

// 成功时返回实际写入的字节数
// 出错返回EOF
// count不应超过buf大小

 

demo:键盘输入

#include  <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char *argv[]) {
    int fd;
    char buf[64];
    if ((fd = open(argv[1], O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) {
        perror("open");
        return -1;
    }
    // 读取文件的内容
    // 获取文件大小
    while (fgets(buf, 20, stdin) > 0) {
        if (strcmp(buf, "quit\n") == 0) {
            break;
        }
        write(fd, buf, strlen(buf));
    }
}

 

 

demo:文件的复制

#include  <stdio.h>
#include <fcntl.h>
#include <unistd.h>

#define N 64

int main(int argc, char *argv[]) {
    int src, desc;
    int n;
    char buf[N];
    if (argc < 3) {
        perror("Parameter less than 3");
        return -1;
    }
    // 打开源文件 只读
    if ((src = open(argv[1], O_RDONLY)) < 0) {
        perror("open src");
        return -1;
    }
    // 打开目标文件
    if ((desc = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) {
        perror("open desc");
        close(src);
        return -1;
    }

    // 获取文件大小
    while ((n = read(src, buf, N)) > 0) {
        write(desc, buf, n);
    }
    close(src);
    close(desc);
}

 

 

 

 

 

定位

#include <unistd.h>
off_t lseek(int fd,off_t offset,int whence); // 与fseek类似 whence基准点一样

// 成功返回当前文件的读写位置
// 出错返回EOF

 

 

文件关闭

#include <unistd.h>
int close(int fd);

// 成功时返回0
// 出错时返回EOF
// 程序结束时自动关闭所有打开的文件
// 文件关闭后,就不能对文件描述符进行操作

 

 

访问(打开)目录

#include <dirent.h>
DIR *opendir(const char*name); // name目录路径

// DIR是用来描述一个打开的目录文件的结构体类型
// 成功时返回目录流指针
// 错误时返回NULL

 

 

查看目录内容

#include <dirent.h>
struct dirent *readdir(DIR *dirp); 
// struct dirent是用来描述目录流中一个目录项的结构体类型
// 包含成员 char d_name[256]  —— 文件名  其他参考帮助文档
// 成功时返回目录流dirp中下一个目录项
// 出错或到末尾是返回NULL

 

demo:查看指定目录下的文件名

#include  <stdio.h>
#include <dirent.h>

int main(int argc, char *argv[]) {
    DIR *dirp;
    struct dirent *dp;
    if (argc < 2) {
        perror("Parameter is less than 2");
        return -1;
    }
    // 打开目录
    if ((dirp = opendir(argv[1])) == NULL) {
        perror("opendir");
        return -1;
    }
    while ((dp = readdir(dirp)) != NULL) {
        printf("%s\n", dp->d_name);
    }
    closedir(dirp);
}

 

 

关闭目录流

#include <dirent.h>
int closedir(DIR *dirp);

// 成功返回0
// 失败返回-1

 

 

修改文件的访问权限 chmod/fchmod

#include <sys/stat.h>
int chomd(const char *path,mode_t mode);
int fchmod(int fd,mode_t mode);

// mode 例如 0666 权限码
// 成功时返回0
// 出错返回EOF
// root用户和所有者才可以修改文件的访问权限
// demo:
chomd("text.txt",0666);

 

获取文件的属性

#include <sys/stat.h>
int stat(const char *path,struct stat *buf); //path为路径,buf为获取存放的结构体
int lstat(const char *path,struct stat *buf);
int fstat(int fd,struct stat*buf);

// stat与lstat区别: 如果path是符号链接stat获取到的是目标文件的属性,而lstat获取的是链接文件的属性
// 成功时返回0
// 出错返回EOF
// struct stat结构体属性
// mode_t st_mode  文件类型和访问权限
// uid_t st_uid 所有者id
// uid_t st_gid 用户组id
// off_t st_size 文件大小
// time_t st_mtime 最后修改时间
// ....

mode_t st_mode  判断文件类型

// 通过系统提供的宏来判断文件类型
st_mode & 0170000
S_ISREG(st_mode) 0100000
S_ISDIR(st_mode) 0040000
S_ISCHR(st_mode) 0020000
S_ISBLK(st_mode) 0060000
S_ISFIFO(st_mode) 0010000
S_ISLNG(st_mode) 0120000
S_ISSOCK(st_mode) 0140000

mode_t st_mode 获取权限

// 通过系统提供的宏来获取we年访问权限
// 所有者
S_IRUSR——00400——8 可读
S_IWUSR——00200——7 可写
S_IXUSR——00100——6
// 同组用户
S_IRGRP——00040——5
S_IWGRP——00020——4
S_IXGRP——00010——3
// 其他组用户
S_IROTH——00004——2
S_IWOTH——00002——1
S_IXOTH——00001——0
// 通过系统提供的宏来获取文件的访问权限
// 所有者
S_IRUSR——00400——8 可读
S_IWUSR——00200——7 可写
S_IXUSR——00100——6
// 同组用户
S_IRGRP——00040——5
S_IWGRP——00020——4
S_IXGRP——00010——3
// 其他组用户
S_IROTH——00004——2
S_IWOTH——00002——1
S_IXOTH——00001——0

 

posted @ 2020-05-07 08:25  Binb  阅读(198)  评论(0编辑  收藏  举报