首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >读取可能嵌入了空值的文本文件

读取可能嵌入了空值的文本文件
EN

Code Review用户
提问于 2019-04-27 00:15:06
回答 3查看 498关注 0票数 4

我一直使用fgets来读取文件。但是,我想读取一个可能嵌入了\0的文件。我曾想过使用ftell来查询大小,但它似乎并不适用于所有的文件。

我有个测试文件,

代码语言:javascript
复制
31 32 33 00 34 35 36 0A E2 82 AC 0D 0A 61

这是我的fgets

代码语言:javascript
复制
#include  /* EXIT */
#include   /* printf perror fputc fread */
#include  /* strlen */
#include   /* errno */
#include  /* assert */

int main(void) {
    char file[1000], *f = file, *a;
    const int granularity = 80;
    int is_done = 0;

    for( ; ; ) {
        /* Fail when contents bigger than the size;
         would be a good spot to use realloc. */
        if(granularity > sizeof file - (f - file))
            { errno = ERANGE; break; }
        if(!fgets(f, granularity, stdin))
            { if(ferror(stdin)) break; is_done = 1; break; }
        f += strlen(f);
    }

    for(a = file; a < f; a++) printf("%02hhX ", *a);
    fputc('\n', stdout);
    return is_done ? EXIT_SUCCESS : (perror("stdin"), EXIT_FAILURE);
}

运行这个,(我在类似UNIX的机器上,)

代码语言:javascript
复制
$ bin/fgets < test 
31 32 33 E2 82 AC 0D 0A 61 

这是我的fread

代码语言:javascript
复制
#include  /* EXIT */
#include   /* printf perror fputc fread */
#include   /* errno */
#include  /* assert */

int main(void) {
    char file[1000], *f = file, *a;
    const int granularity = 80;
    size_t read;
    int is_done = 0;

    for( ; ; ) {
        if(granularity > sizeof file - (f - file))
            { errno = ERANGE; break; }
        read = fread(f, 1, granularity, stdin);
        if(ferror(stdin)) break;
        assert(read >= 0 && read <= granularity);
        f += read;
        if(read != granularity) { is_done = 1; break; }
    }

    for(a = file; a < f; a++) printf("%02hhX ", *a);
    fputc('\n', stdout);
    return is_done ? EXIT_SUCCESS : (perror("stdin"), EXIT_FAILURE);
}

运行这个,

代码语言:javascript
复制
$ bin/fread < test
31 32 33 00 34 35 36 0A E2 82 AC 0D 0A 61 

我想知道这是否是脚踏实地的正确和我如何改进。

EN

回答 3

Code Review用户

回答已采纳

发布于 2019-04-27 02:06:32

我发现您的代码很难阅读,原因如下:

  • 变量file实际上不是一个文件,而是一个缓冲区。
  • 变量f (通常表示文件)是指向该缓冲区的指针。
  • 变量a有一个根本没有任何含义的名称。
  • 如果第一个if语句将大括号缩进下一行,而在最后一个if语句中,主体位于同一行。
  • 在我期望它们的地方,例如在iffor之后,代码中缺少空格。
  • 永久循环通常编写为for (;;),而不是for( ; ; )
  • 代码的主要块在for循环中,该部分中没有一个空行。这意味着整个块正在做一件没有可能中断或逻辑中断的事情。
  • 逗号运算符一般都是皱眉的。
  • is_done并不是关于正在做的工作,它更多的是关于成功

由于上述所有原因,我会以不同的方式编写代码:

代码语言:javascript
复制
#include 
#include 
#include 
#include 

int main(void) {
    char buf[1000];
    size_t buflen = 0;
    const size_t granularity = 80;

    while (true) {
        if (granularity > sizeof buf - buflen) {
            errno = ERANGE;
            break;
        }

        size_t nread = fread(buf + buflen, 1, granularity, stdin);
        if (ferror(stdin))
            break;

        buflen += nread;
    }

    for (size_t i = 0; i < buflen; i++)
        printf("%02hhX ", buf[i]);
    fputc('\n', stdout);

    if (ferror(stdin)) {
        perror("stdin");
        return EXIT_FAILURE;
    }
}

我改变的是:

  • 我重命名了所有变量以匹配它们的用途。
  • 我用索引替换了进入缓冲区的各种指针。
  • 我删除了is_done变量,因为程序不应该仅仅因为文件是80个字节的倍数而打印错误。
  • 通过在使用变量afor循环中声明变量,将变量i移至较小的作用域;我还将其重命名为D43,因为它现在是索引而不是指针。
  • 我用if语句替换了逗号运算符,因为这是常用的形式。
  • 我将包含为布尔类型,以及常量truefalse
  • 我用for (;;)替换了while (true),这就不那么神奇了
  • 我按字母顺序对包含的标头进行排序,因为对于C标准库中的标头,顺序并不重要
  • 我将变量read重命名为nread,以避免与同名的POSIX函数发生冲突。
票数 6
EN

Code Review用户

发布于 2019-04-27 12:23:59

Roland提供了一个很好的评论,我想补充几点:

标准头文件定义宏/常量BUFSIZ。此宏主要用于输入和输出缓冲区。在最初的C中,它被定义为1024,但现在它因系统而异,可能是基于文件系统的阻塞大小。

最好使用BUFSIZ定义字符数组。

assert()宏作为调试工具通常很有用,并且不包括在生产代码中。这将是从代码中优化,如果宏NDEBUG被定义

当我第一次阅读代码时,我忽略了所有的break;语句;如果每条语句都放在单独的行上,可能会更好。

票数 4
EN

Code Review用户

发布于 2019-04-27 20:40:13

读取可能嵌入了空值的文本文件

fgets()根本不是这样做的最佳工具。

处理带有空字符的文本文件的代码使用*nix、getline()或类似的函数。

代码语言:javascript
复制
#include 
#include 

int main(void) {
  size_t sz = 0;
  char *buf = NULL;
  unsigned long long line = 0;
  ssize_t count;
  char ch = '\n';
  while ((count = getline(&buf, &sz, stdin)) > 0) {
    printf("%llu", ++line);
    for (ssize_t i = 0; i < count; i++) {
      ch = buf[i];
      printf(" %02hhX:", ch);
    }
  }
  if (ch != '\n') {
    printf("\n");
  }
  free(buf);
  if (ferror(stdin)) {
    perror("stdin");
    return EXIT_FAILURE;
  }
  return EXIT_SUCCESS;
}
票数 3
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/219220

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档