【问题标题】:How to make a C program execute continuously while accepting input from stdin?如何在接受标准输入的同时使 C 程序连续执行?
【发布时间】:2019-10-23 14:10:47
【问题描述】:

以下是myprogram.c的摘录:

int main(int argc, char *argv[])
{
   UserOptions opt;
   DmtxEncode *enc;
   unsigned char codeBuffer[BUFFER_SIZE];
   int codeBufferSize;
   opt = GetDefaultOptions();


   /* Read input data into buffer */
   codeBufferSize = ReadInputData(codeBuffer, &opt);

   //do something with the input data

   exit(0);
}

static size_t ReadInputData(unsigned char *codeBuffer, UserOptions *opt)
{
   int fd;
   ssize_t bytesRead;
   size_t bytesReadTotal;

   /* Open file or stdin for reading */
   fd = (opt->inputPath == NULL) ? 0 : open(opt->inputPath, O_RDONLY);

   /* Read input contents into buffer */
   for(bytesReadTotal = 0;; bytesReadTotal += bytesRead) {
      bytesRead = read(fd, codeBuffer + bytesReadTotal, BUFFER_SIZE);
      if(bytesRead == 0)
         break;
   }

   return bytesReadTotal;
}

这以echo 1234 | myprogram 的形式接受输入,程序生成输出并终止。

我想让这个程序连续执行并接受用户输入,其中两个单独的输入由换行符 (\n) 区分。例如,我想将1234 \n 5678 \n 6363 \n 作为输入。程序应该首先获取输入1234,为其生成输出,然后看到有换行符并将5678 视为第二个输入,生成输出等等。

我试过修改ReadInputData函数如下:

static size_t ReadInputData(unsigned char *codeBuffer, UserOptions *opt) {
    int fd;
    ssize_t bytesRead;
    size_t bytesReadTotal;
    size_t startOfBlock;
    unsigned char tmpBuffer[BUFFER_SIZE];

    /* Open file or stdin for reading */
    fd = (opt->inputPath == NULL) ? 0 : open(opt->inputPath, O_RDONLY);

    for (bytesReadTotal = 0;; bytesReadTotal += bytesRead) {

        startOfBlock = tmpBuffer + bytesReadTotal;
        bytesRead = read(fd, startOfBlock, BUFFER_SIZE);

        for (int i = startOfBlock; i < startOfBlock + bytesRead; i++) {
            if (tmpBuffer[i] != '\n')
                codeBuffer[i] = tmpBuffer[i];
            else
                break;
    }
    }


    return bytesReadTotal;
}

并将main 的工作原理封装在while(1) 循环中,但这只会产生单个输入的输出并终止程序。

有人可以帮我弄清楚我在这里做错了什么吗?

编辑:根据建议,我修改ReadInputData函数如下:

static size_t
ReadInputData(unsigned char *codeBuffer, UserOptions *opt) {
    int fd;
    ssize_t bytesRead;
    size_t bytesReadTotal;
    unsigned char* startOfBlock;
    unsigned char tmpBuffer[BUFFER_SIZE];

    /* Open file or stdin for reading */
    fd = (opt->inputPath == NULL) ? 0 : open(opt->inputPath, O_RDONLY);

    for (bytesReadTotal = 0;; bytesReadTotal += bytesRead) {

        startOfBlock = tmpBuffer + bytesReadTotal;
        bytesRead = read(fd, startOfBlock, DMTXWRITE_BUFFER_SIZE);

        if (bytesRead == 0)
            break;
        if (bytesRead == -1)
            FatalError(EX_IOERR, _("Message read error"));
        else if (bytesReadTotal > DMTXWRITE_BUFFER_SIZE)
            FatalError(EX_DATAERR, _("Message to be encoded is too large"));

        for (int i = 0; i < bytesRead; i++) {
            if (startOfBlock[i] != '\n')
                codeBuffer[i] = startOfBlock[i];
            else
                break;
        }

    }

这解决了换行符的问题,但程序在接受一个输入后退出,而不是接受连续输入。 main 的内容在 while(1) 循环内。

编辑: main 带有 while 循环。 main 中没有 exit(0)

int main(int argc, char *argv[])
{
   UserOptions opt;
   DmtxEncode *enc;
   unsigned char codeBuffer[BUFFER_SIZE];
   int codeBufferSize;
   opt = GetDefaultOptions();

   while(1){

   /* Read input data into buffer */
   codeBufferSize = ReadInputData(codeBuffer, &opt);

   //do something with the input data

   }

}

【问题讨论】:

  • 我不确定您是否可以将\n 作为输入的一部分阅读。通常它是一个输入终止符。你可以让你的程序循环,在每个动作之后读取输入。
  • @Paul 他们当然可以阅读 '\n';实际上,这是一个常见的错误来源。
  • @JL2210 没有fp ;-)。您必须检查 read() 的返回值是否为 0。
  • 为什么不简单地用fgets逐行读取输入?
  • fgets 如果二进制数据由\n 分隔(您在问题中说),则可以很好地处理二进制数据。虽然用换行符分隔二进制数据有点奇怪和限制,因为数据本身将无法容纳\n 字符。

标签: c


【解决方案1】:

ReadInputData 中的代码有很多问题。最大的问题是您的输入不会在调用之间保留。 read 将获得所有可用的数据(直到您传入的计数)。因此,如果恰好有两条线可用,ReadInputData 会将它们都放入tmpBuffer。当tmpBuffer 超出范围时,您保存第一个,第二个被丢弃。

您需要一个跨调用保留的缓冲区。

另一个问题是这个测试:

else if (bytesReadTotal > DMTXWRITE_BUFFER_SIZE)

如果DMTXWRITE_BUFFER_SIZE 非常小,以至于前两行相加大于此值,则会出现致命错误。我很确定你真的需要这样的东西:

if (bytesReadTotal == BUFFER_SIZE)
{
    FatalError(EX_DATAERR, _("Message to be encoded is too large"));
}
bytesRead = read(fd, startOfBlock, min(DMTXWRITE_BUFFER_SIZE BUFFER_SIZE, BUFFER_SIZE - bytesReadTotal));

即您需要确保在进行读取之前 缓冲区中有空间。此外,阅读的内容超出您的允许范围是没有意义的。


回到第一个问题,有两种方法可以解决。

  1. 制作tmpBufferbytesReadTotal静态变量

  2. 将它们放在struct 中并将指向它的指针传递给调用

struct ReadBuffer
{
    char tmpBuffer[BUFFER_SIZE];
    size_t bytesReadTotal;
}

int main(int argc, char *argv[])
{
    struct ReadBuffer buffer;
    buffer.bytesReadTotal = 0;

    // stuff 

    codeBufferSize = ReadInputData(codeBuffer, &opt, &buffer);

    // stuff

}

static size_t ReadInputData(unsigned char *codeBuffer, UserOptions *opt, struct ReadBuffer *buffer)
{
    // stuff

    startOfBlock = buffer.tmpBuffer + buffer.bytesReadTotal;
    // and so on
}

然后你需要添加你所有的代码来管理缓冲区等等,但是这样的东西已经存在于C库中,它被称为FILE。如果您使用缓冲的标准库FILE API 而不是原始的 Unix 系统调用,您的生活会轻松得多。甚至还有一个函数(很多 cmets 告诉你要使用它,它读取输入并将其返回到下一个 \n。它被称为 fgets()


刚刚发现另一个问题。每次调用ReadInputData 时都会打开文件。这对stdin 来说不是问题,因为您只使用文件描述符 0。但是,对于任何其他文件,您每次只需读取第一行,直到您的进程用完文件描述符。在我上面的解决方案中,您必须在main 中打开文件并将文件描述符添加到ReadBuffer 结构中。如果你这样做,你肯定是在重新发明 C 库FILE

这是一个粗略的重新实现

int main(int argc, char *argv[])
{
   UserOptions opt;
   DmtxEncode *enc;
   unsigned char codeBuffer[BUFFER_SIZE];
   ssize_t codeBufferSize;
   opt = GetDefaultOptions();

   FILE* input = opt->inputPath == NULL ? stdin : open(opt->inputPath, "r");
   if (input == NULL)
   {
       // Handle the error
   } 

   while(1)
   {

       /* Read input data into buffer */
       codeBufferSize = fgets(codeBuffer, BUFFER_SIZE, input);
       if (codeBufferSize == -1)
       {
           // Handfle IO error
       }
       else if (codeBufferSize == 0) 
       { 
           // reached end of file
       }
       else if (codeBuffer[codeBufferSize - 1] != '\n')
       {
           // Handle input truncated because line was too long
       }
       else 
       { 
           //do something with the input dat
       }
   }
   if (input != NULL)  
   {
       fclose(input);
   }
}


【讨论】:

    猜你喜欢
    • 2015-01-22
    • 1970-01-01
    • 2016-07-17
    • 1970-01-01
    • 2014-08-19
    • 2021-11-06
    • 1970-01-01
    • 2012-09-18
    • 1970-01-01
    相关资源
    最近更新 更多