【问题标题】:Converting read-file into C将读取文件转换为 C
【发布时间】:2021-04-08 21:23:11
【问题描述】:

这是Conversion of string.endswith method into C 的后续,我正在(尝试)将python 程序转换为C。

下一部分是正确地将文件中的数据读取到缓冲区中,并检查可能出错的各个地方。到目前为止,这是我所拥有的:

#define BUFFER_SIZE 1024*10
char buffer[BUFFER_SIZE];

void exit_with_error(const char* msg)
{
    fprintf(stderr, "%s", msg);
    exit(EXIT_FAILURE);
}

int main(int argc, char* argv[])
{
     // with open(argv[-1) as f:
    //    contents = f.read()

    // 1. Open file
    FILE *fp = fopen(argv[argc-1], "r");
    if (fp == NULL) exit_with_error("Error reading file");

    // 2. Read and confirm BUFFER is OK
    fseek(fp, 0L, SEEK_END);
    long fsize = ftell(fp);
    if (fsize > BUFFER_SIZE) exit_with_error("File is larger than buffer.");

    // 3. Write to buffer and close.
    rewind(fp);
    size_t read_size = fread(buffer, 1, fsize, fp);
    if (read_size != fsize) exit_with_error("There was an error reading the file.");
    fclose(fp);

}

我对上述问题有几个问题:

  • 对于将文件读入缓冲区,更常见的是具有标准缓冲区大小并将其写入缓冲区,还是获取大小然后执行malloc。一种方式比另一种方式有优势吗?
  • 是否有必要在 main 方法中进行所有这些错误检查,或者我可以假设用户提供了正确的文件(并且它是可读的,等等)
  • 最后,为什么有些文件方法返回size_t,而另一些返回long(比如做ftell?)它们都使用size_t安全吗?不是那种类型?

【问题讨论】:

  • 1.实在不好回答。这取决于上下文和要求。有时分配一个固定的缓冲区并分块读取是正确的做法。有时获取文件大小并分配完整的缓冲区是正确的。 2. 是的,这是必要的。依赖未经验证的用户输入或不检查函数返回值是灾难的根源。
  • 至于问题 3:size_t 取决于您的编译器版本,对于 32 位编译器定义为 unsigned int,对于 64 位编译器定义为 unsigned long long。在选择要使用的类型时,最好遵循函数的声明。至于ftell,错误返回值之一是-1,它与unsigned 类型不匹配。
  • @IradOhayon 也可以是unsigned long
  • 如果fopen 失败,给出错误消息“读取文件时出错”是一种误导。读取文件没有错误。让系统给你一个很好的错误信息:FILE *fp = fopen(argv[argc-1], "r"); if (fp == NULL) { perror(argv[argc - 1]); exit(EXIT_FAILURE); }(或使用包装器并让包装器打印strerror(errno)
  • fseek/ftell 组合不适用于获取文件的读取大小。并非每个文件都是可密封的,文件大小可能会在fseek 和读取之间发生变化,并且一些奇怪的操作系统(又名 Windows)可能会在读取时进行文件翻译。将整个文件放入缓冲区的正确方法是循环读取文件,边写边写入缓冲区,直到 EOF,并根据需要调整缓冲区大小。更好的是没有整个文件缓冲区,而是在读取时处理数据。

标签: c


【解决方案1】:

对于将文件读入缓冲区,更常见的是具有标准缓冲区大小并将其写入缓冲区,还是获取大小然后执行 malloc。一种方式比另一种方式有优势吗?

“抓住大小”很棘手。 long fsize = ftell(fp); 工作 经常,但未指定以实现长度目标。高度可移植的代码不使用ftell()

要读取整个文件,可以多次调用fread()

是否有必要在 main 方法中进行所有这些错误检查,或者我可以假设用户提供了正确的文件(并且它是可读的,等等)

健壮的代码避免做出假设。最好假设用户输入是邪恶(观看Potassium 视频)并执行大量错误检查。

最后,为什么有些文件方法返回size_t,而另一些返回long(比如做ftell?)它们都使用size_t安全吗?不是那种?

size_t 的范围通常由 内存 大小及其架构决定。文件大小的范围是文件系统的限制。文件大小可能超过内存大小的情况并不少见。当 C 第一次出现时,long 是最大的签名类型,并且足以满足所有情况,例如,大约 1995 年。现在文件可以超过 2G,(long最小值最大值)。即使使用 32 位 long,也可以通过 fgetpos() 跟踪巨大的文件偏移量。许多较新的系统采用 64 位 long,通过 ftell() 允许更大的范围。


如果代码尝试使用ftell() 查找文件大小 ....

检查ftell()和其他I/O函数结果。

if (fseek(fp, 0, SEEK_END);) {
  exit_with_error("fseek failure.");
}
long fsize = ftell(fp);
if (fsize == -1) {
  exit_with_error("ftell failure.");
}

// In many cases, files exceeding `SIZE_MAX` are also problematic.
// Yet rarely is LONG_MAX > SIZE_MAX
if (fsize > SIZE_MAX) {
  exit_with_error("fsize very large.");
}

通常更希望以二进制模式打开文件。


最后,IMO,为了处理文件,避免将文件全部读入内存并以块的形式处理数据。

【讨论】:

  • 即使对于较小的文件,甚至在 Python 中,如果可以避免的话,最好避免一次将整个文件读入内存。
猜你喜欢
  • 2019-09-07
  • 1970-01-01
  • 1970-01-01
  • 2014-09-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-14
相关资源
最近更新 更多