标准IO
C标准库IO接口, <stdio.h>
打开关闭
- 如果一个系统函数有打开和其对应的关闭操作,那么函数的返回值是放在 堆 上的(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参数补充:- 只识别串开头的内容,如r+write只识别r+
- r/r+:要求文件必须存在
- 和系统调用
open的flag参数对应的关系:
mode flags r O_RDONLY w O_WRONLY | O_CREAT | O_TRUNC a O_WRONLY | O_CREAT | O_APPEND r+ O_RDWR w+ O_RDWR | O_CREAT | O_TRUNC a+ O_RDWR | O_CREAT | O_APPEND -
最大打开个数
- 在编程世界里,所有的资源都应该是有上限的。
- 可以用
ulimit -n查看/修改
# 查看 $ ulimit -n # -n: file descriptors 1024 # 修改: $ ulimit -n 65536- 测试最大打开文件的例子:
#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模式创建的新文件的权限- mod = 0666 & ~umask
- 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; }