1.标准IO

1.1什么是标准IO

1.1.1概念

标准IO:在C库中定义的一组专门用来输入输出的函数

1.1.2特点

1)通过缓冲机制减少系统调用,提高效率

        系统调用:内核向上提供的一组接口

2)围绕流进行操作,流FILE *描述,FILE代表的是结构体

用vi的ctags索引:

先建立ctags索引:密码是1

1.vi -t        查找的名称

    输入序号,回车

   

2.继续追踪

   将光标定位到要追踪的内容:ctrl ]

   回退:ctrl t

3.跳转到上次位置:ctrl o

    跳转到下次位置:ctrl i

    q:退出

vscode索引:

跳转到定义:ctrl 左键

前后跳转:

ctrl alt -

ctrl shift -

3)默认打开三个流:stdin(标准输入)、stdout(标准输出)、stderr(标准错误)

1.1.3操作

打开文件:fopen

关闭文件:fclose

读写操作:fgets、fputs、fread、fwrite

定位操作:rewind、fseek、ftell

1.2缓冲区

1.2.1全缓存:和文件相关

        刷新条件:

                缓存区满

                程序正常退出

                强制刷新:fflush(NULL);

1.2.2行缓存:和终端有关

        刷新条件:

                缓存区满

                程序正常退出

                强制刷新:fflush(NULL);

                \n

1.2.3不缓存:标准错误

1.3函数接口

1.3.1打开文件fopen

#include <stdio.h>
   FILE *fopen(const char *pathname, const char *mode);
功能:打开文件
参数:pathname:打开的文件路径
    mode:打开方式
        r:只读,流被定位到文件开头
        r+:可读可写,流被定位到文件开头
        w:只写,文件不存在创建,文件存在清空,流被定位到文件开头
        w+:可读可写,文件不存在创建,文件存在清空,流被定位到文件开头
        a:追加,文件不存在创建存在追加,流被定位到文件末尾
        a+:可读可写,文件不存在创建,存在追加,开始进行读时从头读,进行写时流被定位到文件末尾

返回值:成功:文件流
        失败:NULL, 并设置errno(错误码)
#include <stdio.h>

int main(int argc, char const *argv[])
{
    FILE * fp = fopen("./test", "r");
    if(NULL == fp)
    {
        // perror简单原理
        // vi -t EEXIST  // 查看相关错误码
        // 使用这个函数就会根据errno的值,找到对应的错误信息打印出来
        perror("fopen file err");
        return -1;
    }

    printf("fopen file success\n");
    return 0;
}

1.3.2关闭文件

#include <stdio.h>
   int fclose(FILE *stream);
功能:关闭文件
参数:stream:文件流

1.3.3读写文件操作

1.3.3.1每次读写一个字符串:fgets、fputs
#include <stdio.h>
       char *fgets(char *s, int size, FILE *stream);
功能:从文件中读取一个字符串
参数:s:存放读取的字符串首地址
    size:读取的大小
    stream:文件流
返回值:成功:读取的字符串首地址
        失败或读到文件末尾:NULL
特性:1. 一次调用最多读取一行数据,遇到\n或者达到文件末尾后不在继续下一行
    2. 实际读到个数为size-1个,末尾自动添加\0

#include <stdio.h>
       int fputs(const char *s, FILE *stream);
功能:向文件中写一个字符串
参数:s:要写入的内容
       stream:文件流
返回值:成功:非负整数
           失败:EOF

#include <stdio.h>

int main(int argc, char const *argv[])
{
    FILE *fp = fopen("./test", "r");
    if (NULL == fp)
    {
        // perror简单原理
        // vi -t EEXIST  // 查看相关错误码
        // 使用这个函数就会根据errno的值,找到对应的错误信息打印出来
        perror("fopen file err");
        return -1;
    }

    printf("fopen file success\n");

    char buf[32] = {};
    fgets(buf, 32, fp);
    fputs(buf, stdout); // buf: hello\n\0

    fgets(buf, 32, fp);
    fputs(buf, stdout); // buf: nihao\n\0

    fgets(buf, 3, fp);
    fputs(buf, stdout); // buf: 12\0ao\n\0

    fgets(buf, 3, fp);
    fputs(buf, stdout); // buf: 3\n\0ao\n\0

    fclose(fp);
    return 0;
}

练习:编程实现wc-l命令的功能

        wc-l文件名:显示文件的行数

思路:打开文件,循环读取文档,当读取文件末尾时循环结束,在循环中判断是否是一行,如果是则对整型变量进行自加,关闭文件。

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

int main(int argc, char const *argv[])
{
    if(argc != 2)
    {
        printf("usage: %s <filename>\n", argv[0]);
        return -1;
    }

    FILE *fp = fopen(argv[1], "r");
    if(NULL == fp)
    {
        perror("fopen file err");
        return -1;
    }

    char buf[32] = {};
    int len = 0;
    while(fgets(buf, sizeof(buf), fp) != NULL)
    {
        if(buf[strlen(buf)-1] == '\n')
        {
            len++;
        }
    }

    printf("%s length: %d\n", argv[1], len);
    return 0;
}
1.3.3.2二进制读写fread、fwrite
#include <stdio.h>
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:从文件流读取多个元素(将二进制数据从文件读出)
参数:ptr:用来存放读取元素(可以用来存放任意类型的数据)
    size:元素大小 sizeof(数据类型)
    nmemb: 读取的对象个数
    stream:要读取的文件
返回值:成功:读取元素的个数
        失败或读取到文件尾:0

size_t fwrite(const void *ptr, size_t size, size_t nmemb,
FILE *stream);
功能:将二进制数据写入文件
参数:ptr :是一个指针,保存要输出数据的空间的地址。
     size :要写入的字节数 sizeof(数据类型)
     nmemb : 要进行写入元素的个数
      stream: 目标文件流指针
返回值:成功:写元素的个数
        失败:-1
#include <stdio.h>

int main(int argc, char const *argv[])
{
    int arr[3] = {1, 2, 3};
    int num[3] = {};

    FILE *fp = fopen("./test", "r+");
    if(NULL == fp){}

    fwrite(arr, sizeof(int), 3, fp);
    rewind(fp);  // 将文件的位置指针(流)定位到开头
    fread(num, sizeof(int), 3, fp);

    for(int i = 0 ;i < 3; i++)
        printf("%d\n", num[i]);

    return 0;
}

1.3.4文件定位操作 rewind、fseek、ftell

rewind

void rewind(FILE* stream);
功能:将文件的位置指针定位到起始位置

fseek

#include <stdio.h>
   int fseek(FILE *stream, long offset, int whence);
功能:文件的定位操作
参数:stream:文件流
     offset:偏移量:正数表示向后文件尾部偏移,负数表示向文件开头偏移
     whence:相对位置:
           SEEK_SET:相对于文件开头
           SEEK_CUR:相对于文件当前位置
           SEEK_END:相对于文件末尾
返回值:成功:0
        失败:-1

注:当打开方式为a或a+时,fseek不起作用
补充:其中SEEK_SET、SEEK_CUR、SEEK_END依次为0、1、2

例子:
把fp指针移动到离文件开头10个字节处:fseek(fp, 10, SEEK_SET);
把fp指针移动到离文件当前位置10个字节处:fseek(fp, 10, SEEK_CUR);
把fp指针移动到离文件结尾10个字节处:fseek(fp, -10, SEEK_END);

ftell
long ftell(FILE *stream)
功能:获取当前的文件位置
参数:要检测的文件流
返回值:成功:当前的文件位置
        失败:-1

2.文件IO

2.1什么是文件IO

2.1.1概念

又称系统IO,是系统调用,是操作系统提供的函数接口

在posix(可以移植操作系统接口)中定义的一组输入输出函数

2.1.2特点

1.没有缓冲机制,每次操作都会经过系统调用,效率比较低

2.围绕着文件描述符进行操作,文件描述符是非负整数:0、1、2

3.默认打开三个文件描述符:0(标准输入)、1(标准输出)、2(标准错误)

4.除目录外其他任意类型的文件都可以操作b、c、-、l、s、p

问题:打开三个文件,文件描述符:3、4、5

关闭3以后,重新打开文件,描述符是几?

问题:一个进程的文件描述符最大到几?最多能打开多少个文件描述符?最多能打开1024-3=1021个文件

2.1.3操作

打开文件:open

关闭文件:close

读写操作:read、write

定位操作:lseek

2.2函数操作

2.2.1打开文件open

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

int open(const char *pathname, int flags);
功能:打开文件
参数:pathname:文件路径名
    flags:打开方式
        O_RDONLY:只读  
        O_WRONLY:只写
        O_RDWR:可读可写
        O_CREAT:不存在创建
        O_TRUNC:清空
        O_APPEND:追加
返回值:成功:文件描述符
        失败:-1,并设置errno

当第二个参数中存在O_CREAT选项时,需要open函数传递第三个参数,创建文件的权限
int open(const char *pathname, int flags, mode_t mode);
0777 & (~umask) = 实际文件权限    umask: 0002
111 111 111
&
111 111 101
=
111 111 101  =  0775

补充:

1.当第二个参数中有O_CREAT选项时,需要给open函数传递第三个参数,指定创建文件的权限,open(file,xx|O_CREAT|xx,0666);

2.实际创建出来的文件权限为指定权限值&(~umask) //umask为文件权限掩码,通过umask命令查看

3.打开文件方式应对表:

标准IO

文件IO

r

O_RDONLY

r+

O_RDWR

w

O_WRONLY|O_CREAT|O_TRUNC

w+

O_RDWR|O_CREAT|O_TRUNC

a

O_WRONLY|O_CREAT|O_APPEND

a+

O_RDWR|O_CREAT|O_APPEND

2.2.2关闭文件

#include <unistd.h>
int close(int  fd);
参数:fd:文件描述符
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char const *argv[])
{
    int fd = open("./xxx", O_WRONLY | O_CREAT | O_TRUNC, 0666);
    if(fd < 0)
    {
        perror("open err");
        return -1;
    }
    printf("fd: %d\n", fd);

    close(fd);
    return 0;
}

2.2.3读写文件

read

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
功能:从一个打开的可读文件中读取数据
参数:fd:文件描述符
    buf:存放位置
    count:期望的个数
返回值:成功:实际读到的个数
        返回-1:表示出错,并设置errno号
        返回0:表示读到文件结尾
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char const *argv[])
{
    char buf[32] = {};
    int fd = open("./xxx", O_RDONLY);
    if(fd < 0)
    {
        perror("open err");
        return -1;
    }
    printf("fd: %d\n", fd);

    // 不同于标准IO中的fgets,read是指定多少就获取多少
    // read(fd, buf, 5);
    // printf("%s\n", buf);

    int num = read(fd, buf, 32);
    printf("buf: %s\n", buf);
    printf("num: %d\n", num);

    close(fd);
    return 0;
}

write

#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
功能:向指定文件描述符中,写入 count个字节的数据。
参数:fd   文件描述符
      buf   要写的内容
      count  期望值
返回值:成功:实际写入数据的个数
        失败:-1
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char const *argv[])
{
    char buf[32] = {};
    int fd = open("./xxx", O_RDWR);
    if(fd < 0)
    {
        perror("open err");
        return -1;
    }
    printf("fd: %d\n", fd);

    // 不同于标准IO中的fgets,read是指定多少就获取多少
    // read(fd, buf, 5);
    // printf("%s\n", buf);

    int num = read(fd, buf, 32);
    printf("buf: %s\n", buf);
    printf("num: %d\n", num);

    // 如果这样,文件中会出现一些乱码,因为buf空间中有很多\0
    // write(fd, buf, sizeof(buf));

    write(fd, buf, num);

    close(fd);
    return 0;
}

补充:在读和写一起使用时,一般write的字符个数指定为read的返回值,“读多少写多少”

练习:编程实现cp命令功能,用文件IO实现。

cp 源文件 新文件

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

int main(int argc, char const *argv[])
{
    char buf[32] = {};
    int r_fd = open("./read.c", O_RDONLY);  // r
    if(r_fd < 0)
    {
        perror("open err");
        return -1;
    }

    int w_fd = open("./xxx", O_WRONLY | O_CREAT | O_TRUNC, 0666);  // w
    if(w_fd < 0)
    {
        perror("open err");
        return -1;
    }

    ssize_t n = 0;
    while((n = read(r_fd, buf, sizeof(buf))) > 0)
    {
        write(w_fd, buf, n);
    }

    close(r_fd);
    close(w_fd);
    return 0;
}

补充:(命令:完全相同的话是没有任何输出的,有不同的地方它会把不同地方列出来)

        diff 文件1 文件2:比较两个文件是否相同

2.2.4定位操作lseek

#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
功能:设置文件的偏移位置
参数:fd  文件描述符
      offset  偏移量
          正数:向文件结尾位置移动
          负数:向文件开始位置移动
      whence  相对位置
          SEEK_SET  开始位置
          SEEK_CUR  当前位置
          SEEK_END  结尾位置
补充:和fseek一样其中SEEK_SET,SEEK_CUR和SEEK_END和依次为0,1和2

返回值:成功:文件的当前位置
        失败:-1

标准IO和文件IO区别

标准IO

文件IO

概念

在C库中定义的一组输入输出的函数

在posix中定义的一组输入输出的函数

特点

  1. 有缓冲区,减少系统调用,提效率
  2. 围绕流操作,FILE *
  3. 默认打开三个流:stdin\stdout\stderr
  4. 只操作普通文件
  5. 可移植性相对较好
  1. 无缓冲区,每次操作都引起系统调用
  2. 围绕文件描述符操作
  3. 默认打开三个文件描述符:0\1\2
  4. 除目录外其他文件
  5. 可移植性相对较弱

函数

打开文件:fopen\freopen

关闭文件:fclose

读写文件:fgetc/fputc fgets/fputs fread/fwrite

文件定位:fseek/rewind/ftell

打开文件:open

关闭文件:close

读写文件:read/write

文件定位:lseek

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐