2017-03-06 103 views
0

我想從Stdin讀取輸入。我在C中使用fork()方法。我有孩子和父母的過程。我的輸入是多行。父進程將簡單地等待子進程的終止.Child進程將只讀第一行。子進程終止後,父進程將繼續讀取。我想要打印線。例如;輸入 - >如何使用fork從Stdin讀取輸入

  1. 週一
  2. 週二
  3. 週三

子進程打印 '星期一',父進程打印 '星期二' 和 '日'。一旦文件結束爲 達到程序終止。

./程序< input.txt

+0

可能重複[與fgets()的調用重定向得到異常數據流(https://stackoverflow.com/questions/45656781/fgets-call-with -redirection-get-abnormal-data-stream) –

回答

0

OK;聽起來很簡單 - 至少,只要你輸入的是輸入。事實上,很難讓它出錯。你嘗試了什麼,當你嘗試時發生了什麼?請閱讀如何創建一個MCVE(Minimal, Complete, Verfiable Example)。

然而,當你從一個文件讀取時,它會變得很棘手。問題是,當你從終端讀取數據時,孩子的標準I/O例程會得到第一行數據,然後讓這個過程工作。孩子在沒有閱讀第二行的情況下退出,所以父母可以拿起孩子離開的地方。相反,如果您正在讀取文件,則標準I/O例程會讀取充滿數據的緩衝區,這可能是多行。孩子讀什麼,父母不能。所以,什麼與終端輸入的作品不適用於文件輸入。

如何避免此問題? '很難。一種可能被認爲是作弊的可能性是在分叉之前讓初始(父母)過程讀取一個字符。如果輸入來自終端,或者如果從文件讀取初始緩衝區已滿,則會用一行填充緩衝區。分叉後,子進程可以讀取第一行(fgets())並打印並退出。同時,父進程在其輸入緩衝區的副本中也有第一行數據(請記住,分叉後的子代和父代在分岔後幾乎完全相同),因此它可以讀取並忽略第一行(子進程正在處理該行) ,然後讀取並打印剩餘的行。然後它可以等待孩子死循環,最後讀取其餘的行。這導致:

/* SO 4263-5451 */ 

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <sys/wait.h> 
#include <unistd.h> 

int main(void) 
{ 
    pid_t pid; 
    int c = getchar(); 
    if (c == EOF) 
    { 
     fprintf(stderr, "Standard input was empty!\n"); 
     exit(1); 
    } 
    ungetc(c, stdin); 
    //setvbuf(stdin, 0, _IOLBF, 0); 

    if ((pid = fork()) < 0) 
    { 
     fprintf(stderr, "Failed to fork()!\n"); 
     exit(2); 
    } 
    if (pid == 0) 
    { 
     /* Child */ 
     char line[4096]; 
     if (fgets(line, sizeof(line), stdin) == 0) 
     { 
      fprintf(stderr, "Failed to read line in child\n"); 
      exit(3); 
     } 
     line[strcspn(line, "\n")] = '\0'; 
     printf("Child: read [%s]\n", line); 
     exit(0); 
    } 
    else 
    { 
     int corpse; 
     int status; 
     while ((corpse = wait(&status)) > 0 && corpse != pid) 
      printf("Parent: child %d died with status 0x%.4X\n", corpse, status); 
     char line[4096]; 
     if (fgets(line, sizeof(line), stdin) == 0) 
     { 
      fprintf(stderr, "Failed to read line in parent\n"); 
      exit(4); 
     } 
     while (fgets(line, sizeof(line), stdin) != 0) 
     { 
      line[strcspn(line, "\n")] = '\0'; 
      printf("Parent: read [%s]\n", line); 
     } 
    } 
    return 0; 
} 

這樣產生:

Child: read [Monday] 
Parent: read [Tuesday] 
Parent: read [Wednesday] 

我試着變體設置線緩衝帶setvbuf(stdin, 0, _IOLBF, 0);但這並不影響運行MacOS的塞拉利昂10.12.3文件輸入(在Mac上與GCC 6.3.0),但它也適用於終端輸入。

一種選擇是使用文件描述符代碼替換標準I/O函數,並使用read(STDIN_FILENO, &c, 1)一次讀取一個字符。這是比較慢(大量的系統調用),但可靠:

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <sys/wait.h> 
#include <unistd.h> 

static int input_char(int fd) 
{ 
    char c; 
    if (read(fd, &c, 1) != 1) 
     return EOF; 
    return c; 
} 

static size_t input_line(int fd, char *buffer, size_t buflen) 
{ 
    int c; 
    size_t bufcnt = 0; 
    while (bufcnt < buflen - 1 && (c = input_char(fd)) != EOF) 
    { 
     if (c == '\n') 
      break; 
     buffer[bufcnt++] = c; 
    } 
    buffer[bufcnt] = '\0'; 
    return bufcnt; 
} 

int main(void) 
{ 
    pid_t pid; 

    if ((pid = fork()) < 0) 
    { 
     fprintf(stderr, "Failed to fork()!\n"); 
     exit(2); 
    } 
    if (pid == 0) 
    { 
     char line[4096]; 
     if (input_line(STDIN_FILENO, line, sizeof(line)) == 0) 
     { 
      fprintf(stderr, "Failed to read line in child\n"); 
      exit(3); 
     } 

     printf("Child: read [%s]\n", line); 
     exit(0); 
    } 
    else 
    { 
     int corpse; 
     int status; 
     while ((corpse = wait(&status)) > 0 && corpse != pid) 
      printf("Parent: child %d died with status 0x%.4X\n", corpse, status); 
     char line[4096]; 
     while (input_line(STDIN_FILENO, line, sizeof(line)) != 0) 
     { 
      printf("Parent: read [%s]\n", line); 
     } 
    } 
    return 0; 
} 
+0

謝謝兄弟。 –

+0

它是更棘手的文件重定向:[fgets()調用與重定向獲取異常數據流](https://stackoverflow.com/questions/45656781/fgets-call-with-redirection-get-abnormal-data-stream) –