LinuxC-API: IO

55 阅读8分钟

标准IO

C标准库IO接口, <stdio.h>

打开关闭

  1. 如果一个系统函数有打开和其对应的关闭操作,那么函数的返回值是放在 堆 上的(malloc 与free),否则有可能在堆上也有可能在静态区

fopen

  • 接口:

    #include <stdio.h>
    
    /// @brief 以mode指定的模式打开path指定的文件
    /// @param filename [in] 文件路径
    /// @param mode [in] 打开文件的模式 ("r", "w", "a")               
    /// @return 成功打开返回对应的文件指针,失败返回空指针 NULL,并设置 errno
    FILE *fopen(const char *restrict path, const char *restrict mode);
    
  • mode 参数补充:

    1. 只识别串开头的内容,如r+write只识别r+
    2. r/r+:要求文件必须存在
    3. 和系统调用 openflag 参数对应的关系:
    modeflags
    rO_RDONLY
    wO_WRONLY | O_CREAT | O_TRUNC
    aO_WRONLY | O_CREAT | O_APPEND
    r+O_RDWR
    w+O_RDWR | O_CREAT | O_TRUNC
    a+O_RDWR | O_CREAT | O_APPEND
  • 最大打开个数

    1. 在编程世界里,所有的资源都应该是有上限的
    2. 可以用 ulimit -n 查看/修改
    # 查看
    $ ulimit -n # -n: file descriptors
    1024
    # 修改:
    $ ulimit -n 65536
    
    1. 测试最大打开文件的例子:
    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    
    int main() {
        int count = 0;
    
        while(1) {
            FILE *fp = fopen("tmp","r");
            if (NULL == fp) {
                perror("fopen()");
                break;
            }
            count++;
        }
        printf("%d\n",count);
        return 0;
    }
    
  • a/w 模式创建的新文件的权限

    1. mod = 0666 & ~umask
    2. umask:
    # 查看
    $ umask
    022
    # 修改
    $ umask 022
    

fclose

  • 原型
    #include <stdio.h>
    /// @brief 关闭一个已打开的文件流指针
    /// @param stream [in] 已打开的文件流指针
    /// @return 成功关闭返回0,失败返回EOF,并设置 errno
    int fclose(FILE *stream);
    

例子

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

int main() {
    FILE *fp = fopen("/tmp/out.txt", "w");
    if (NULL == fp) {
        fprintf(stderr, "fopen err, errno = [%d][%s]\n", errno, strerror(errno));
        return -1;
    }

    // 一般fclose不用判断
    if (fclose(fp) != 0) {
        fprintf(stderr, "fopen err, errno = [%d][%s]\n", errno, strerror(errno));
        return -1;
    }
    return 0;
}

读写

fgetc

  • 原型
    #include <stdio.h>
    /// @brief 从指定的流 stream 获取下一个字符(一个无符号字符),并把位置标识符往前移动。
    /// @param stream [in] 已打开的文件流指针
    /// @return 成功以 unsigned char 强制转换为 int 的形式返回读取的字符,如果到达文件末尾或发生读错误,则返回 EOF。
    /// @note man手册没有明确指出出错会设置errno,需使用ferror获取文件流内部错误码
    int fgetc(FILE *stream);
    

fputc

  • 原型
    #include <stdio.h>
     
    /// @brief 将一个字符写入指定的输出流。该函数将字符 `c` 写入到由 `stream` 指向的输出流中。在写入之前,`c` 会被转换为 unsigned char 类型。
    /// @param c [in] 要写入的字符
    /// @param stream [out] 输出流指针
    /// @return 成功时返回写入的字符(转换为 unsigned char 后的值);失败时返回 EOF。
    /// @note man手册没有明确指出出错会设置errno,需使用ferror获取文件流内部错误码
    int fputc(int c, FILE *stream);
    

fgets

  • 原型
    #include <stdio.h>
    
    /// @brief 从指定的输入流中读取一行字符串。
    /// @param s [out] 指向字符数组的指针,用于存储读取到的字符串。
    /// @param n [in] 指定最多读取的字符数(包括末尾的空字符 '\0')。
    /// @param stream [in] 指向 FILE 结构的指针,代表目标输入流(如 stdin、文件流等)。
    /// @return 成功时返回指向字符串 s 的指针;到达文件结尾或读取失败时返回 NULL。
    /// @note 
    ///       - 该函数最多读取 n-1 个字符,以便在末尾保留一个空间存储字符串结束符 '\0'。
    ///       - 读取在遇到换行符 '\n' 时停止,并且换行符会包含在存储的字符串中,之后再写入 '\0'。
    ///       - 如果输入行长度超过 n-1 个字符,则只读取前 n-1 个,剩余部分保留在流中供后续读取。
    ///       - man手册没有明确指出出错会设置errno,需使用ferror(stream)获取文件流内部错误状态。
    char *fgets(char *s, int n, FILE *stream);
    

fputs

  • 原型
    /// @brief 将一个字符串写入指定的输出流。
    /// @param s [in] 指向以空字符 '\0' 结尾的字符串,该字符串将被写入到输出流中(不包含结尾的 '\0')。
    /// @param stream [out] 指向 FILE 结构的指针,代表目标输出流(如 stdout、文件流等)。
    /// @return 成功时返回一个非负数;失败时返回 EOF。
    /// @note 
    ///       - 该函数将字符串 s 中的所有字符(不包括结尾的空字符 '\0')写入到 stream 指向的输出流。
    ///       - 不会在输出的字符串末尾自动添加换行符 '\n'。
    ///       - man手册没有明确指出出错会设置errno,需使用ferror(stream)获取文件流内部错误状态。
    ///       - 函数成功返回值仅表示操作完成,不表示写入的字符数量。
    int fputs(const char *s, FILE *stream);
    

fread

  • 原型
    #include <stdio.h>
    
    /// @brief 从指定的输入流中读取数据块。
    /// @param ptr [out] 指向用于存储读取数据的内存块的指针。
    /// @param size [in] 指定每个数据项的大小(以字节为单位)
    /// @param nmemb [in] 指定要读取的数据项数量。
    /// @param stream [in] 指向 FILE 结构的指针,代表输入流。
    /// @return 成功时返回实际读取的数据项数量(不是字节数);若发生错误或到达文件结尾则返回一个少于 nmemb 的值。
    ///         返回值为 size_t 类型。如果在读取任何数据项之前发生错误或到达文件结尾,返回 0。
    /// @note 
    ///       - 实际读取的字节数为 return_value * size。
    ///       - 遇到文件结尾或读取错误都会导致返回值小于 nmemb。需使用 feof() 和 ferror() 来区分这两种情况。
    ///       - man手册没有明确指出出错会设置errno,需使用ferror(stream)获取文件流内部错误状态。
    ///       - 读取是二进制安全的,不会对数据做任何解释或转换(除了文本流中的换行符转换)。
    size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
    

fwrite

  • 原型
    #include <stdio.h>
    
    /// @brief 将数据块写入指定的输出流。
    /// @param ptr [in] 指向包含待写入数据的内存块的指针。
    /// @param size [in] 指定每个数据项的大小(以字节为单位)。
    /// @param nmemb [in] 指定要写入的数据项数量。
    /// @param stream [out] 指向 FILE 结构的指针,代表输出流。
    /// @return 成功时返回实际写入的数据项数量(不是字节数);若发生错误则返回一个少于 nmemb 的值。
    ///         返回值为 size_t 类型。如果在写入任何数据项之前发生错误,返回 0。
    /// @note 
    ///       - 实际写入的字节数为 return_value * size。
    ///       - 只有写入错误会导致返回值小于 nmemb;不会因空间不足等原因部分写入(与低级 write 系统调用不同)。
    ///       - man手册没有明确指出出错会设置errno,需使用ferror(stream)获取文件流内部错误状态。
    ///       - 写入是二进制安全的,数据原样写入(文本流中换行符可能被转换)。
    size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
    

例子

  • fgetc,fputc版本 mycopy:
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    
    // gcc fgetc_fputc_mycp.c -o mycopy
    
    int main(int argc, char *argv[]) {
        if (argc < 3) {
            fprintf(stderr, "Usage: %s src_file dst_file\n", argv[0]);
            return -1;
        }
        int ec = 0;
        int ch = 0;
        FILE *rfp = NULL;
        FILE *wfp = NULL;
        rfp = fopen(argv[1], "r");
        if (NULL == rfp) {
            fprintf(stderr, "fopen [%s] err, errno = [%d][%s]\n", 
                argv[1], errno, strerror(errno));
            goto cleanup;
        }
    
        wfp = fopen(argv[2], "w");
        if (NULL == wfp) {
            fprintf(stderr, "fopen [%s] err, errno = [%d][%s]\n", 
                argv[2], errno, strerror(errno));
            goto cleanup;
        }
    
        while (1) {
            ch = fgetc(rfp);
            if (EOF == ch) {
                if (feof(rfp) != 0) {
                    // 读到文件末尾
                    goto cleanup;
                } else {
                    ec = ferror(rfp);
                    if (ec != 0) {
                        fprintf(stderr, "fgetc err, ec = [%d][%s]\n", 
                            ec, strerror(ec));
                    }
                    goto cleanup;
                }
            }
            if (EOF == fputc(ch, wfp)) {
                ec = ferror(rfp);
                fprintf(stderr, "fputc err, ec = [%d][%s]\n", 
                    ec, strerror(ec));
                goto cleanup;
            }
        }
    cleanup:
        if (rfp) {
            fclose(rfp);
        }
        if (wfp) {
            fclose(wfp);
        }
        return 0;
    }
    
  • fgets,fputs版本 mycopy:
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    
    // gcc fgetc_fputc_mycp.c -o mycopy
    
    int main(int argc, char *argv[]) {
        if (argc < 3) {
            fprintf(stderr, "Usage: %s src_file dst_file\n", argv[0]);
            return -1;
        }
        int ec = 0;
        int ch = 0;
        FILE *rfp = NULL;
        FILE *wfp = NULL;
        rfp = fopen(argv[1], "r");
        if (NULL == rfp) {
            fprintf(stderr, "fopen [%s] err, errno = [%d][%s]\n", 
                argv[1], errno, strerror(errno));
            goto cleanup;
        }
    
        wfp = fopen(argv[2], "w");
        if (NULL == wfp) {
            fprintf(stderr, "fopen [%s] err, errno = [%d][%s]\n", 
                argv[2], ec, strerror(ec));
            goto cleanup;
        }
    
        while (1) {
            ch = fgetc(rfp);
            if (EOF == ch) {
                if (feof(rfp) != 0) {
                    // 读到文件末尾
                    goto cleanup;
                } else {
                    ec = ferror(rfp);
                    if (ec != 0) {
                        fprintf(stderr, "fgetc err, ec = [%d][%s]\n", 
                            ec, strerror(ec));
                    }
                    goto cleanup;
                }
            }
            if (EOF == fputc(ch, wfp)) {
                ec = ferror(rfp);
                fprintf(stderr, "fputc err, ec = [%d][%s]\n", 
                    ec, strerror(ec));
                goto cleanup;
            }
        }
    cleanup:
        if (rfp) {
            fclose(rfp);
        }
        if (wfp) {
            fclose(wfp);
        }
        return 0;
    }
    
  • fread,fwrite版本 mycopy:
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    
    // gcc fread_fwrite_mycp.c -o mycopy
    
    int main(int argc, char *argv[]) {
        if (argc < 3) {
            fprintf(stderr, "Usage: %s src_file dst_file\n", argv[0]);
            return -1;
        }
        int ec = 0;
        char buf[256];
        int rlen = 0;
        int wlen = 0;
        int offset = 0;
        int remain = 0;
        FILE *rfp = NULL;
        FILE *wfp = NULL;
        rfp = fopen(argv[1], "r");
        if (NULL == rfp) {
            fprintf(stderr, "fopen [%s] err, errno = [%d][%s]\n", 
                argv[1], errno, strerror(errno));
            goto cleanup;
        }
    
        wfp = fopen(argv[2], "w");
        if (NULL == wfp) {
            fprintf(stderr, "fopen [%s] err, errno = [%d][%s]\n", 
                argv[2], errno, strerror(errno));
            goto cleanup;
        }
    
        while (1) {
            rlen = fread(buf, 1, sizeof(buf), rfp);
            if (0 == rlen) {
                if (feof(rfp) != 0) {
                    // 读到文件末尾
                    goto cleanup;
                } else {
                    ec = ferror(rfp);
                    if (ec != 0) {
                        fprintf(stderr, "fgetc err, ec = [%d][%s]\n", 
                            ec, strerror(ec));
                    }
                    goto cleanup;
                }
            }
            offset = 0;
            remain = rlen;
            while (remain != 0) {
                wlen = fwrite(buf + offset, 1, remain, wfp);
                if (0 == wlen) {
                    ec = ferror(wfp);
                    fprintf(stderr, "fputc err, ec = [%d][%s]\n", 
                        ec, strerror(ec));
                    goto cleanup;
                }
                offset += wlen;
                remain -= wlen;
            }
        }
    cleanup:
        if (rfp) {
            fclose(rfp);
        }
        if (wfp) {
            fclose(wfp);
        }
        return 0;
    }