C语言 为什么'head'命令正确地读取了以'\0'开头的字符串,而fgets却出错了?

fzwojiic  于 2023-10-16  发布在  其他
关注(0)|答案(3)|浏览(98)

这里是我想读出的文件,里面有一些意想不到的'\0'
下面是我的代码,有两种方法将其读取到shell,一种是使用fgets(),另一种是使用Linux 'head' cmd;
这是两种方法的结果
一个是fgets

Mon Aug 28 14:29:29 2023 admin[::ffff:192.168.7.116]:59927: brief-show slot 1 ont-info 
Mon Aug 28 14:29:33 2023 admin[::ffff:192.168.7.116]:59927: brief-show slot 1 ont-unbound 
Mon Aug 28 14:29:41 2023 admin[::ffff:192.168.7.116]:59927: brief-show slot 2 ont-info 
Mon Aug 28 14:29:43 2023 admin[::ffff:192.168.7.116]:59927: brief-show slot 2 ont-info 
Mon Aug 28 14:29:44 2023 admin[::ffff:192.168.7.116]:59927: brief-show slot 2 ont-info 
Mon Aug 28 14:29:47 2023 admin[::ffff:192.168.7.116]:59927: brief-show slot 2 ont-info 
Mon Aug 28 14:29:51 2023 admin[::ffff:192.168.7.116]:59927: brief-show slot 2 ont-info 
Mon Aug 28 14:29:53 2023 admin[::ffff:192.168.7.116]:59927: brief-show slot 2 ont-info 
Mon Aug 28 14:31:18 2023 admin[::ffff:192.168.7.116]:59927: brief-show slot 2 ont-info 
Mon Aug 28 14:33:50 2023 admin[::ffff:192.168.7.116]:59927: exi
Mon Aug 28 14:33:51 2023 admin[::ffff:192.168.7.116]:59927: exi
Mon Aug 28 14:33:54 2023 admin[::ffff:192.168.7.116]:59927: save config 
Mon Aug 28 14:37:01 2023 admin[::ffff:192.168.7.116]:60207: show slot 
Mon Aug 28 14:37:03 2023 admin[::ffff:192.168.7.116]:60207: show slot 
Mon Aug 28 14:37:07 2023 admin[::ffff:192.168.7.116]:60207: show slot 
Mon Aug 28 14:37:21 2023 admin[::ffff:192.168.7.116]:60207: show slot 
Mon Aug 28 14:37:22 2023 admin[::ffff:192.168.7.116]:60207: show slot 
Mon Aug 28 14:37:27 2023 admin[::ffff:192.168.7.116]:60207: show slot

一个头CMD

Mon Aug 28 14:29:29 2023 admin[::ffff:192.168.7.116]:59927: brief-show slot 1 ont-info 
Mon Aug 28 14:29:33 2023 admin[::ffff:192.168.7.116]:59927: brief-show slot 1 ont-unbound 
Mon Aug 28 14:29:41 2023 admin[::ffff:192.168.7.116]:59927: brief-show slot 2 ont-info 
Mon Aug 28 14:29:43 2023 admin[::ffff:192.168.7.116]:59927: brief-show slot 2 ont-info 
Mon Aug 28 14:29:44 2023 admin[::ffff:192.168.7.116]:59927: brief-show slot 2 ont-info 
Mon Aug 28 14:29:47 2023 admin[::ffff:192.168.7.116]:59927: brief-show slot 2 ont-info 
Mon Aug 28 14:29:51 2023 admin[::ffff:192.168.7.116]:59927: brief-show slot 2 ont-info 
Mon Aug 28 14:29:53 2023 admin[::ffff:192.168.7.116]:59927: brief-show slot 2 ont-info 
Mon Aug 28 14:31:18 2023 admin[::ffff:192.168.7.116]:59927: brief-show slot 2 ont-info 
Mon Aug 28 14:33:50 2023 admin[::ffff:192.168.7.116]:59927: exi
Mon Aug 28 14:33:51 2023 admin[::ffff:192.168.7.116]:59927: exi
Mon Aug 28 14:33:54 2023 admin[::ffff:192.168.7.116]:59927: save config 
Mon Aug 28 14:37:00 2023 admin[::ffff:192.168.7.116]:60207: show slot 
Mon Aug 28 14:37:01 2023 admin[::ffff:192.168.7.116]:60207: show slot 
Mon Aug 28 14:37:03 2023 admin[::ffff:192.168.7.116]:60207: show slot 
Mon Aug 28 14:37:07 2023 admin[::ffff:192.168.7.116]:60207: show slot 
Mon Aug 28 14:37:21 2023 admin[::ffff:192.168.7.116]:60207: show slot 
Mon Aug 28 14:37:22 2023 admin[::ffff:192.168.7.116]:60207: show slot 
Mon Aug 28 14:37:27 2023 admin[::ffff:192.168.7.116]:60207: show slot

你可以看到错误的行与时间“14:37:00”得到打印出来的头cmd,而与fgets没有;所以我很困惑头命令是如何打印出以'\0'开头的字符串的?
是head cmd擦除所有'\0',我试图将head cmd的输出推送到文件head -n 20 cli_log > head_log中,但我打开head_log,发现它与log_log相同。

txu3uszq

txu3uszq1#

fgets是一个string文件函数,\0在C字符串中有非常特殊的含义。这就是为什么它不像你认为的那样工作的原因。
如果文件格式不正确,请不要对函数进行字符串处理。
您需要将此文件视为二进制文件并相应地读取它

yqhsw0fo

yqhsw0fo2#

您的fgets-using程序使用printf("%s", line);行打印。由于\0是C字符串中的字符串终止字符,以\0开头的行在printf中看起来像是一个空字符串,因此它什么也不打印。但是,unix head命令可以处理文件中可能出现的所有字节,包括值为0的字节。

li9yvcax

li9yvcax3#

fgets()可以读取 * 空字符 *,将其保存到缓冲区中并继续阅读。这不是直接的问题。
问题是printf("%s", line);,因为打印在line中的 * 第一个 * 空字符处停止,无论是fgets()读入的空字符还是fgets()在完成时附加的空字符。
打印用fwrite(line, 1, n, stdout)读取的n字符,而不是printf("%s", line);
对于fgets(),查找n读取的字符数是有问题的。
备选案文1:
读取可能包含 * 空字符 * 的 * 行 *,并记录读取的字符数。
说明性代码(uncheck)使用"%4096[^\n]"读取一行的大部分内容(不是'\n'),并使用"%n"记录读取的字符数。

char line[4096+1];
int len;
int conversion_cnt;
while (i < n && (conversion_cnt = fscanf(fp, "%4096[^\n]%n", line, &len)) != EOF) {
  if (conversion_cnt == 1) {
    fwrite(line, 1, len, stdout);
  } else {
    int ch = fgetc(fp);
    if (ch == EOF) {
      break;
    } 
    assert(ch == '\n');  // Only '\n' expected here.
    i++;
    fputc(ch, stdout);
  } 
}

备选案文2:
使用fread()读取数据块,并在找到第20个'\n'时停止。使用memchr()或使用循环查找'\n'

char block[4096];
size_t len;
while (i < n && (len = fread(block, 1, sizeof block, fp)) > 0) {
  for (size_t j = 0; j < len; j++) {
    if (block[j] == '\n') {
      i++;
      if (i >= n) {
        len = j + 1;
        break;
      } 
    }
  }   
  fwrite(line, 1, len, stdout);
}

相关问题