簡體   English   中英

從文件或標准輸入讀取

[英]Read from file or stdin

我正在編寫一個實用程序,它接受文件名或從stdin讀取。

我想知道檢查stdin是否存在的最強大/最快的方式(數據是否通過管道傳輸到程序),如果是,則讀取該數據。如果不存在,則處理將在文件名上進行給出。 我已經嘗試使用以下測試stdin大小,但我相信因為它是一個流而不是一個實際的文件,它不起作用,因為我懷疑它會,它總是打印-1 我知道我總是可以一次讀取輸入1個字符!= EOF但是我想要一個更通用的解決方案,所以如果stdin存在我最終會得到fd或FILE *所以程序的其余部分將無縫運行。 我希望能夠知道它的大小,等待前一個程序關閉流。

long getSizeOfInput(FILE *input){
  long retvalue = 0;
  fseek(input, 0L, SEEK_END);
  retvalue = ftell(input);
  fseek(input, 0L, SEEK_SET);
  return retvalue;
}

int main(int argc, char **argv) {
  printf("Size of stdin: %ld\n", getSizeOfInput(stdin));
  exit(0);
}

終奌站:

$ echo "hi!" | myprog
Size of stdin: -1

你認為這是錯的。

你想做什么:

如果stdin存在使用它,否則檢查用戶是否提供了文件名。

你應該做什么代替:

如果用戶提供文件名,則使用文件名。 否則使用標准輸入。

您無法知道傳入流的總長度,除非您全部讀取並保持緩沖。 你只是不能向后尋找管道。 這是管道工作方式的限制。 管道不適合所有任務,有時需要中間文件。

首先,要求程序通過檢查errno設置的errno來告訴你出了什么問題,例如在fseekftell期間。

其他人(tonio和LatinSuD)解釋了處理stdin與檢查文件名時的錯誤。 即,首先檢查argc (參數計數)以查看是否存在指定的任何命令行參數if (argc > 1) ,處理-作為特殊情況表示stdin

如果沒有指定參數,則假設輸入來自stdin ,這是一個非文件 ,並且fseek函數在其上失敗。

對於流,您不能使用面向磁盤文件的庫函數(即fseekftell ),您只需計算讀取的字節數(包括尾隨換行符),直到接收到EOF (結束時)文件)。

對於大文件的使用,您可以通過將fgets用於char數組來加快速度,以便更有效地讀取(文本)文件中的字節。 對於二進制文件,您需要使用fopen(const char* filename, "rb")並使用fread而不是fgetc/fgets

您還可以在使用字節計數方法檢查從流中讀取時的任何錯誤時檢查for feof(stdin) / ferror(stdin)

以下示例應符合C99和便攜性。

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

long getSizeOfInput(FILE *input){
   long retvalue = 0;
   int c;

   if (input != stdin) {
      if (-1 == fseek(input, 0L, SEEK_END)) {
         fprintf(stderr, "Error seek end: %s\n", strerror(errno));
         exit(EXIT_FAILURE);
      }
      if (-1 == (retvalue = ftell(input))) {
         fprintf(stderr, "ftell failed: %s\n", strerror(errno));
         exit(EXIT_FAILURE);
      }
      if (-1 == fseek(input, 0L, SEEK_SET)) {
         fprintf(stderr, "Error seek start: %s\n", strerror(errno));
         exit(EXIT_FAILURE);
      }
   } else {
      /* for stdin, we need to read in the entire stream until EOF */
      while (EOF != (c = fgetc(input))) {
         retvalue++;
      }
   }

   return retvalue;
}

int main(int argc, char **argv) {
   FILE *input;

   if (argc > 1) {
      if(!strcmp(argv[1],"-")) {
         input = stdin;
      } else {
         input = fopen(argv[1],"r");
         if (NULL == input) {
            fprintf(stderr, "Unable to open '%s': %s\n",
                  argv[1], strerror(errno));
            exit(EXIT_FAILURE);
         }
      }
   } else {
      input = stdin;
   }

   printf("Size of file: %ld\n", getSizeOfInput(input));

   return EXIT_SUCCESS;
}

例如,您可能想要了解如何在cat實用程序中完成此操作。

請參閱此處的代碼 如果沒有文件名作為參數,或者它是“ - ”,則stdin用於輸入。 stdin將在那里,即使沒有數據被推送到它(但是,你的讀取呼叫可能永遠等待)。

除非用戶提供文件名,否則您只能從stdin讀取?

如果沒有,把特殊的“文件名” -意思是:“從標准輸入讀取”。 用戶必須像cat file | myprogram -一樣啟動程序 cat file | myprogram -如果他想管道數據, myprogam file如果他想要從文件讀取。

int main(int argc,char *argv[] ) {
  FILE *input;
  if(argc != 2) {
     usage();
     return 1;
   }
   if(!strcmp(argv[1],"-")) {
     input = stdin;
    } else {
      input = fopen(argv[1],"rb");
      //check for errors
    }

如果您使用* nix,則可以檢查stdin是否為fifo:

 struct stat st_info;
 if(fstat(0,&st_info) != 0)
   //error
  }
  if(S_ISFIFO(st_info.st_mode)) {
     //stdin is a pipe
  }

雖然這不會處理用戶做myprogram <file

您還可以檢查stdin是否是終端/控制台

if(isatty(0)) {
  //stdin is a terminal
}

請注意,您想要知道stdin是否連接到終端,而不是它是否存在。 它總是存在但是當你使用shell管道內容或讀取文件時,它沒有連接到終端。

您可以通過termios.h函數檢查文件描述符是否已連接到終端:

#include <termios.h>
#include <stdbool.h>

bool stdin_is_a_pipe(void)
{
    struct termios t;
    return (tcgetattr(STDIN_FILENO, &t) < 0);
}

這將嘗試獲取stdin的終端屬性。 如果它沒有連接到管道,它將附加到tty並且tcgetattr函數調用將成功。 為了檢測管道,我們檢查tcgetattr故障。

我想,只是用feof測試文件結尾。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM