我一直使用fgets来读取文件。但是,我想读取一个可能嵌入了\0的文件。我曾想过使用ftell来查询大小,但它似乎并不适用于所有的文件。
我有个测试文件,
31 32 33 00 34 35 36 0A E2 82 AC 0D 0A 61这是我的fgets。
#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的机器上,)
$ bin/fgets < test
31 32 33 E2 82 AC 0D 0A 61 这是我的fread。
#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);
}运行这个,
$ bin/fread < test
31 32 33 00 34 35 36 0A E2 82 AC 0D 0A 61 我想知道这是否是脚踏实地的正确和我如何改进。
发布于 2019-04-27 02:06:32
我发现您的代码很难阅读,原因如下:
file实际上不是一个文件,而是一个缓冲区。f (通常表示文件)是指向该缓冲区的指针。a有一个根本没有任何含义的名称。if语句将大括号缩进下一行,而在最后一个if语句中,主体位于同一行。if或for之后,代码中缺少空格。for (;;),而不是for( ; ; )。for循环中,该部分中没有一个空行。这意味着整个块正在做一件没有可能中断或逻辑中断的事情。is_done并不是关于正在做的工作,它更多的是关于成功由于上述所有原因,我会以不同的方式编写代码:
#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个字节的倍数而打印错误。a的for循环中声明变量,将变量i移至较小的作用域;我还将其重命名为D43,因为它现在是索引而不是指针。if语句替换了逗号运算符,因为这是常用的形式。true和falsefor (;;)替换了while (true),这就不那么神奇了read重命名为nread,以避免与同名的POSIX函数发生冲突。发布于 2019-04-27 12:23:59
Roland提供了一个很好的评论,我想补充几点:
标准头文件定义宏/常量BUFSIZ。此宏主要用于输入和输出缓冲区。在最初的C中,它被定义为1024,但现在它因系统而异,可能是基于文件系统的阻塞大小。
最好使用BUFSIZ定义字符数组。
assert()宏作为调试工具通常很有用,并且不包括在生产代码中。这将是从代码中优化,如果宏NDEBUG被定义。
当我第一次阅读代码时,我忽略了所有的break;语句;如果每条语句都放在单独的行上,可能会更好。
发布于 2019-04-27 20:40:13
读取可能嵌入了空值的文本文件
fgets()根本不是这样做的最佳工具。
处理带有空字符的文本文件的代码使用*nix、getline()或类似的函数。
#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;
}https://codereview.stackexchange.com/questions/219220
复制相似问题