2010-08-16 6 views
21

파일 이름을 사용하거나 stdin에서 읽는 유틸리티를 쓰고 있습니다.파일 또는 표준 입력에서 읽기

stdin이 존재하는지 (데이터가 프로그램에 파이프되어 있는지) 검사하고 그 데이터를 읽는 경우 검사하는 가장 강력하고 빠른 방법을 알고 싶습니다. 존재하지 않으면 처리가 필요합니다 주어진 파일 이름에 놓으십시오. 나는 크기가 stdin 인 다음 테스트를 사용하려고 시도했지만 실제 파일이 아니기 때문에 스트림이라고 생각한다. 의심 스럽지만 작동하지 않는다. 항상 인쇄하고있다. -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 

답변

15

먼저 fseek 또는 ftell과 같이 실패시 설정되는 errno을 확인하여 프로그램에 잘못된 내용을 알려달라고 요청하십시오.

기타 (tonio & LatinSuD)는 stdin을 처리하는 것과 파일 이름을 확인하는 실수를 설명했습니다. 즉, 먼저 argc (인수 개수)을 확인하여 if (argc > 1)으로 지정된 명령 줄 매개 변수가 있는지 확인하고 -을 특별한 경우로 stdin으로 처리하십시오.

매개 변수를 지정하지 않으면, 입력 파일이 아닌 스트림stdin에서 온 (예정)하고, fseek 함수가 실패 가정합니다. 당신이 (줄 바꿈 문자 후행 포함) 읽기 바이트 수를 계산하는 파일에 디스크 중심의 라이브러리 함수 (즉, fseekftell)를, 당신은 단순히 한 사용할 수 없습니다 스트림의 경우

EOF 를 수신 할 때까지 (파일 끝).

대용량 파일을 사용하는 경우 (텍스트) 파일의 바이트를보다 효율적으로 읽으려면 문자 배열에 fgets을 사용하여 속도를 높일 수 있습니다. 이진 파일의 경우 fopen(const char* filename, "rb")을 사용해야하고 fgetc/fgets 대신 fread을 사용해야합니다.

스트림에서 읽을 때 오류를 감지하기 위해 바이트 계산 방법을 사용하는 경우 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; 
} 
0

그냥 할 것 feof와 파일의 끝 테스트, 나는 생각한다.

+1

'feof'는 이전에 스트림에서 읽으려고 시도 했으므로 까다로운 작업입니다. (그리고 그렇게하면 실패 이유를 확인할 수도 있습니다.)이 상황에서 어떻게 사용하는지 제안하는 방법은 분명하지 않습니다. – jamesdlin

5

예를 들어, cat 유틸리티에서이 작업을 수행하는 방법을 살펴볼 수 있습니다.

코드 here을 참조하십시오. 인수로 파일 이름이 없거나 "-"이면 stdin이 입력으로 사용됩니다. stdin은 아무 데이터도 푸시되지 않더라도 읽습니다 (그렇지만 읽기 호출은 영원히 기다릴 수 있음).

4

사용자가 파일 이름을 제공하지 않으면 stdin에서 읽을 수 있습니까?

그렇지 않은 경우 특수 "파일 이름"-을 "stdin에서 읽기"의미로 취급하십시오. 사용자가 데이터를 파이프하려는 경우 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에서 스크립트에 있다면, 당신은 표준 입력이 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 
} 
21

잘못 생각한 것 같습니다.

당신이 뭘 하려는지 : 표준 입력이 사용 존재하는 경우

을, 다른 사용자가 파일 이름을 공급 있는지 확인합니다. 사용자가 파일 이름을 제공

경우, 파일 이름을 사용 : 당신이 대신 일을해야 무엇

. 그렇지 않으면 stdin을 사용하십시오.

들어오는 스트림의 전체 길이를 알지 못하고 버퍼링을 유지해야합니다. 파이프로 거꾸로 탐색 할 수 없습니다. 이것은 파이프 작동 방식의 제한 사항입니다.파이프는 모든 작업에 적합하지 않으며 때때로 중간 파일이 필요합니다.

0

stdin이 터미널에 연결되어 있는지 아닌지 알고 있어야합니다 (존재하는 경우가 아니라). 항상 존재하지만 쉘을 사용하여 파일을 파이프하거나 파일을 읽으면 터미널에 연결되지 않습니다.

당신은 파일 기술자가 termios.h 기능을 통해 단자에 연결되어 있는지 확인할 수 있습니다

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

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

이 표준 입력의 단자 특성을 가져 오기 위해 노력할 것입니다. 파이프에 연결되어 있지 않으면 tty에 연결되고 tcgetattr 함수 호출이 성공합니다. 파이프를 감지하기 위해 tcgetattr 실패를 확인합니다.

+0

에 STDIN_FILENO가 정의되도록 #include 을 추가해야했습니다. –

관련 문제