0

嗨stackoverflow(ers)!

我正在使用 XV6 OS 学习 Unix(在此处找到的文档),并且一直在尝试用 C 编写尾部函数。预期输出:

  1. tail 是给出文件的最后 10 行
  2. tail - 给出文件的最后一行
  3. tail ... 就是给出文件的最后 10 行 ...
  4. tail - ... 是给出...的最后一行
  5. grep | tail 是给出最后 10 个句子,其中包含

我已经编写了两个版本的tail,一个使用char* [] 实现,另一个通过写入文件然后从中读取(都在下面发布)我使用char* [] 实现tail 的版本似乎更准确实际的命令。但是,在我写入临时文件然后从中读取的版本中,我得到更多行作为输出,我不确定为什么会发生这种情况。我的猜测是,在从一个文件读取并写入另一个文件时,'\n' 的位置会变得混乱。我非常感谢帮助解决这个问题!

如果我在做傻事,请不要生我的气。我是 Unix 中的 C 新手,只是想学习。

tail.c 使用 char* []

#include "types.h"
#include "stat.h"
#include "user.h"
#include "fcntl.h"

char buf [512];

void tail (int fd, int toSub) {
  int n;
  int numLines = 0;
  int linesToPrint = 0;
  char *buffer;
  buffer = (char*) malloc (500000);
  int buffSize = 0;
  while ((n = read(fd, buf, sizeof(buf))) > 0) {
    for (int i = 0; i<n; i++) {
      buffer[buffSize] = (char)buf[i];
      buffSize++;
      if(buf[i] == '\n')
        numLines++;
    }
  }
  if (n < 0) {
    printf (1, "tail: read error \n");
    exit ();
  }
  if (numLines < toSub)
    linesToPrint = 0;
  linesToPrint = numLines - toSub;

  int counter = 0;
  for (int i = 0; i < buffSize; i++) {
    if (counter >= linesToPrint)
      printf(1,"%c",buffer[i]);
    if (buffer[i] == '\n')
      counter++;
  }
  free (buffer);
}

int main (int argc, char *argv[]) {
  int toSub = 10;
  int fd = -1;

  if (argc <= 1) {
    tail (0, toSub);
    exit();
  }
  else if (argc > 1 && argv[1][0] == '-') {
    char getToSub [10];
    for (int k=1; k<strlen(argv[1]); k++) {
      getToSub[k-1] = argv[1][k];
    }
    toSub = (atoi)(getToSub);
  }
  else {
    if((fd = open (argv[1], toSub)) < 0) {
      printf (1, "tail: cannot open %s\n", argv[1]);
      exit ();
    }
    tail (fd, toSub);
    close (fd);
  }
  if (argc > 2) {
    for (int i=2; i<argc; i++) {
      if((fd = open (argv[i], 0)) < 0) {
        printf (1, "tail: cannot open %s\n", argv[i]);
        exit ();
      }
      else {
        tail (fd, toSub);
        close (fd);
      }
    }
  }
  exit();
}

tail.c 使用写入

#include "types.h"
#include "stat.h"
#include "user.h"
#include "fcntl.h"

char buf [512];

void tail (int fd, int toSub) {
  int n;
  int numLines;
  int linesToPrint;
  int ptrDump;
  ptrDump = open ("tailDump", O_CREATE | O_RDWR);
  while ((n = read(fd, buf, sizeof(buf))) > 0) {
    write (ptrDump, buf, sizeof(buf));
    for (int i = 0; i<n; i++) {
      if(buf[i] == '\n')
        numLines++;
    }
  }
  if (n < 0) {
    printf (1, "tail: read error \n");
    exit ();
  }
  if (numLines < toSub)
    linesToPrint = 0;
  linesToPrint = numLines - toSub;

  close (ptrDump);
  ptrDump = open ("tailDump", 0);

  int counter = 0;
  while ((n = read(ptrDump, buf, sizeof(buf))) > 0) {
    for (int i = 0; i<n; i++) {
      if (counter > linesToPrint)
        printf(1,"%c",buf[i]);
      if (buf[i] == '\n')
        counter++;
      }
    }
    close (ptrDump);
    unlink("tailDump");
}

int main (int argc, char *argv[]) {
  int toSub = 10;
  int fd = -1;

  if (argc <= 1) {
    tail (0, toSub);
    exit();
  }
  else if (argc > 1 && argv[1][0] == '-') {
    char getToSub [10];
    for (int k=1; k<strlen(argv[1]); k++) {
      getToSub[k-1] = argv[1][k];
    }
    toSub = (atoi)(getToSub);
  }
  else {
    if((fd = open (argv[1], toSub)) < 0) {
      printf (1, "tail: cannot open %s\n", argv[1]);
      exit ();
    }
    tail (fd, toSub);
    close (fd);
  }
  if (argc > 2) {
    for (int i=2; i<argc; i++) {
      if((fd = open (argv[i], 0)) < 0) {
        printf (1, "tail: cannot open %s\n", argv[i]);
        exit ();
      }
      else {
        tail (fd, toSub);
        close (fd);
      }
    }
  }
  exit();
}

我将代码放在我的 Github 上(在此处找到)以及 tail_using_str.c 和 tail_using_file.c

4

2 回答 2

2

我认为你的问题在这里:

  while ((n = read(fd, buf, sizeof(buf))) > 0) {
    write (ptrDump, buf, sizeof(buf));

您以字节为单位读取,n但在写入时,您写入sizeof(buf)字节。换句话说,您可能写入了太多字节。

也许你想要这个:

  while ((n = read(fd, buf, sizeof(buf))) > 0) {
    write (ptrDump, buf, n);
                         ^
                        note
于 2017-02-06T07:46:44.260 回答
0

如果我在做傻事,请不要生我的气。我是 Unix 中的 C 新手,只是想学习。

因此,这个答案并不是绝对必要的,因为您提出的核心问题已经得到解答。您发布的问题实际上引发了更多未明确提出的问题,我打算在这里回答。

的预期输出: ...tail -是给出文件的最后一行

据WHO称?不是根据 POSIX,也不是根据tail(1)首次出现的 UNIX V7 。

(嗯,实际上tail(1)第一次出现在PWB/UNIX中,但没有被广泛使用。)

grep | tail是给出最后 10 个句子,其中包含

你的意思是最后 10,而不是句子。grep不产生句子。

(除了在苏联 Unix 中,grep在哪里句子!)

char *buffer;

buffer = (char*) malloc (500000);

这个和下面的exit调用会造成内存泄漏。您可能会说它是无害的,因为操作系统会在程序退出时将内存归还,但它很草率,并且Valgrind之类的工具会调用它。

在函数的所有可能退出点之前的缓冲区,或者free()在堆栈上声明此缓冲区:

char buffer[500000]

根据 xv6 的限制,您可能无法在堆栈上声明那么大的缓冲区。堆栈大小的一个常见现代限制是 2 MiB,这适用于整个堆栈,由最深调用链中的所有函数使用。这在现代系统中是可配置的,但在 xv6 中可能无法配置。

如果您被迫选择该malloc()选项,则可以在一行中执行此操作:

char *buffer = (char*) malloc (500000);

此外:

  • 拥有bufand是不好的风格buffer。懒惰的。给每个缓冲区一个目的驱动的名称,比如lineBufaccumBuf

  • buffSize名称混乱。目前尚不清楚它指的是哪个缓冲区,也不是缓冲区的大小。称之为accumBytes解决这两个问题的东西。

  • 您在现代 POSIX 系统上缺少一堆#includes必需的,并且您有一些在这些系统上不起作用。我会看看 xv6 是否具有stdio.h.h,和stdlib.h,以及它们是否具有 POSIX 可移植性。我还想看看你是否可以通过,因为至少在 macOS 和其他 Unix 上这是必需的。现代系统不需要它,所以如果您在 xv6 上实际上不需要它,请将其删除。string.hunistd.h#include#include types.hsys/types.huser.h

  • 您的内存变体将整个文件读入 RAM,然后跳过它不想打印的 RAM 中的字节。稍加思考将显示如何既可以减小缓冲区大小,又可以不对输入数据进行两次传递。(提示: 。如果您希望允许大于字节accumBuf[toSub][sizeof(lineBuf)]的行数,请随意将第二项乘以某个数量。)sizeof(lineBuf)

if(buf[i] == '\n') numLines++;

您可能应该检查累积缓冲区末尾的非'\n'字节并为其添加另一行。没有 LF 终止符的行不是很干净,但用户的期望通常是您将尾随片段视为一行。

printf (1, "tail: read error \n");

这是什么1, 噪音?您是否要指定stdout?这仅对 是正确的write,而不是printfprintf()已经发送到stdout. (确实,您必须使用fprintf()才能发送到其他任何地方。)

由于这些仅适用于您的错误情况,这意味着您不能测试错误。

这是为 POSIX 可移植性编写代码的另一个原因,即使您最终的目标是xv6:现代 Unix 系统 C 编译器对他们愿意接受的代码更加严格。现代 C 编译器完成了我们过去不得不依赖的工具的大部分工作lint

exit()

exit(2)接受一个参数,即退出状态码,通常为 0 表示干净退出,非零表示错误。你的编译器让你侥幸逃脱的唯一原因是早期的 C 编译器没有严格检查给出的参数列表与函数声明的参数。事实上,xv6 可能正在发布一个 K&R 编译器,它甚至没有函数原型来声明参数列表。程序员应该在没有被警告的情况下做正确的事情。

linesToPrint = numLines - toSub;

那不是“要打印的行”,而是“要跳过打印的行”。我花了 5 分钟的时间盯着代码来克服语义不匹配的问题。编译器不在乎,但变量名不适用于编译器。如果它们仅用于编译器,我们只需将它们称为 all a,b等。

printf("%c",buffer[i]);

在这里使用putchar()

int counter = 0;

再次,懒惰。算什么

我只完成了第一个程序的一半,但评论已经足够了。我希望你从中学到了一些东西。

于 2017-02-06T19:35:08.927 回答