学习笔记2

第9章 I/O库函数

  1. 摘要
    本章讨论了I/O库函数的作用及优势;用程序说明了I/O库函数与系统调用的关系,解释了二者之间相似性和基本区别;详细介绍了I/O库函数算法:fread、fwrite、fclose,重点在于与read、write、close系统调用的交互;介绍了I/O库函数的不同模式;阐述了文件流缓冲方案并用示例表现不同效果;阐释了有不同参数的函数以及如何使用stadarg宏访问参数。

  2. I/O库函数
    I/O库函数是一系列文件操作函数,弥补了系统不支持调用众多逻辑单元的缺陷,既方便用户使用,又提高了整体效率。

  3. I/O库函数与系统调用

  • 系统调用函数:open()、read()、write()、lseek()、close();
  • I/O库函数:fopen()、fread()、fwrite()、fseek()、fclose()。
    每个I/O库函数的根都在对应的系统调用函数中。
    I/O库函数与系统调用二者根本区别:
  1. I/O库函数的算法
  • fread算法:
    第一次调用时,FILE结构体缓冲区为空,文件扫描符fd发出
点击查看代码
n = read(fd, fbuffer, BLKSIZE)

系统调用,用数据块填充fbuf[],初始化fbuf[]指针、计数器和状态变量。接着,通过复制数据到程序的缓冲区,尝试内部缓冲区的fread()调用。若内部缓冲区数据不够,则会再发一个read()系统来填充,将数据从内部缓冲区传输到程序缓冲区,直至满足所需。完成后更新指针、计数器等,准备下一个fread()请求。最后返回实际读取的数据对象数量。

  • fwrite算法
    与fread()算法相似,只是数据传输方向不同。

  • fclose算法
    若文件以写的方式被打开,fclose()会先关闭文件流的局部缓冲区。然后发出一个close(fd)系统调用来关闭FILE结构体中的文件描述符。最后释放FILE结构体,并将FILE指针重置为NULL。

  1. 使用I/O库函数或系统调用
  • 什么时候使用系统调用或库函数进行文件I/O?
  1. I/O库模式
  • fopen中模式参数:
    r:读
    w:写
    a:追加
    r+:读/写,不会截断文件
    w+:读/写,但是会先截断文件;如果文件不存在会创建文件
    a+:通过追加进行读/写;如果文件不存在会创建文件

  • 字符模式I/O

    fgetc()返回的是整数
    编写一个c程序,将文本文件中的字母由小写转换为大写:

点击查看代码
#include <stdio.h>
#include <ctype.h>

int main() {
    FILE *inputFile, *outputFile;
    char inputFileName[] = "input.txt";    // 替换成你的输入文件名
    char outputFileName[] = "output.txt";  // 替换成你的输出文件名
    char ch;

    // 打开输入文件
    inputFile = fopen(inputFileName, "r");
    if (inputFile == NULL) {
        perror("无法打开输入文件");
        return 1;
    }

    // 打开输出文件
    outputFile = fopen(outputFileName, "w");
    if (outputFile == NULL) {
        perror("无法打开输出文件");
        fclose(inputFile);
        return 1;
    }

    // 读取输入文件,转换并写入输出文件
    while ((ch = fgetc(inputFile)) != EOF) {
        if (islower(ch)) {
            ch = toupper(ch);
        }
        fputc(ch, outputFile);
    }

    // 关闭文件
    fclose(inputFile);
    fclose(outputFile);

    printf("文件转换完成。\n");

    return 0;
}

  • 行模式I/O
    从fp中读取最多为一行(以\n结尾)的字符:
点击查看代码
char *fgets(char *buf, int size, FILE *fp)
将buf中的一行写入fp中
点击查看代码
int fputs(char *buf, FILE *fp)
  • 格式化I/O
    FMT:格式字符串
    格式化输入
点击查看代码
scanf(char *FMT, &items);  // from stdin
fscanf(fp, char *FMT, &items);  //from file stream
格式化输出
点击查看代码
printf(char *FMT, items);  // to stdout
fprintf(fp, char *FMT, items);  // to file stream
  • 内存中的转换函数
点击查看代码
sscanf(buf, FMT, &items);  // input from buf[ ] in memory
sprintf(buf, FMT, items);  // print to buf[  ] in memory

sscanf()和sprintf()并非I/O函数,而是内存中的数据转换函数。

  • 其他I/O库函数
    fseek()、ftell()、rewind():更改文件流中的读/写字节位置
    feof()、ferr()、fileno():测试文件流状态
    fdopen():用文件描述符打开文件流
    freopen():以新名称重新打开现有的流
    setbuf()、setvbuf():设置缓冲方案
    popen():创建管道,复刻子进程来调用sh

  • 限制混合fread-fwrite
    当某文件流同时用于读/写时,就会限制使用混合fread()和fwrite()调用。规范要求每对fread()和fwrite()之间至少有一个fseek()或ftell()。

  1. 文件流缓冲
    每个文件流都有一个FILE结构体,其中包含一个内部缓冲区。对文件流读写需要遍历FILE结构体的内部缓冲区。
  • 文件流:
    无缓冲:从非缓冲流中写入或读取的字符将尽快单独传输到文件或从文件中传输。
    行缓冲:遇到换行符时,写入行缓冲流的字符以块的形式传输。
    全缓冲:写入全缓冲流或从中读取字符以块大小传输到文件或从文件中传输。(文件流的正常缓冲方案)
  • 通过fopen()创建文件流之后,在对其执行任何操作前,用户均可发出一个
点击查看代码
setvbuf(FILE *stream, char *buf, int node, int size);
调用来设置缓冲区(buf)、缓冲区大小(size)和缓冲方案(mode),它们必须是一下一个宏: _IONBUF:无缓冲 _IOLBUF:行缓冲 _IOFBUF:全缓冲
  • 对于行缓冲流或全缓冲流,可用fflush(stream)立即清除流的缓冲区。
  1. 变参函数
  • 在I/O库函数中,多种不同类型的可变数量参数都可以调用printf()。参数数量可变的函数必须至少使用一个参数进行声明,后跟3个点。
点击查看代码
int func(int m, int n . . .)  // n = last specified parameter
* 在函数内部,可以通过C语言库宏访问参数:
点击查看代码
void va_start(va_list ap, last);  // start param list from last parameter
type va_arg(va_list ap, type);    // type = next parameter type
va_end(va_list ap);               // clear parameter list
  1. 类printf函数
  • 在Linux中,putchar(char c)可打印一个字符。只使用putchar()来实现函数 int mysprintf(char *fmt, . .)用于格式化打印其他参数。其中fmt是字符串,包括
    %c : print char
    %s : print string
    &u : print unsigned integer
    %d : print signed integer
    %x : print unsigned integer in HEX
    (忽略保留的宽度和精度,只按格式字符串中指定的内容打印存在的参数即可)

  • myprintf()的算法
    (1)扫描格式字符串fmt。打印任何不是%的字符。对于每个‘\n’字符,打印一个额外的‘\r’字符。
    (2)当遇到‘%’时,得到的下一个字符必须是‘c’、‘s’、‘u’、‘d’或‘x’中的一个。使用va—_arg(ap,type)来提取相应的参数。然后通过参数类型调用来打印函数。
    (3)当fmt字符串扫描结束时,算法结束。

苏格拉底挑战

posted @ 2023-09-15 08:35  窦豆  阅读(19)  评论(0)    收藏  举报