코드에는 몇 가지 중요한 중요한 문제가 있습니다. 주된 하나는 파일을 검증하지 못했기 때문에 실제로 파일을 읽는 중입니다.. fopen
의 유효성을 검사하지 않으면 정의되지 않은 동작을 호출 할 때 잘못된 fin
포인터에서 읽으려고하는 다음 호출에서 알 수 없습니다.
Why is “while (!feof (file))” always wrong?을 설명하는 링크를 가리키고 있습니다. fgets
의 반환을 확인하는 것만이 필요한 것입니다.
다음으로 함수에 대한 매개 변수로 rows
에 대한 포인터를 전달하는 것이 좋지만 올바르게 업데이트하지는 않습니다.
rows++; /* this is wrong. this increments the address! (not the value) */
당신이 포인터를 통과하기 때문에, 당신은 그 주소에 저장된 값을 증가해야한다가 아니라 주소 자체, 예를 들면 : get_num_lines
에서 당신과 갱신을 시도
(*rows)++; /* note the use of (..) for correct C-operator precedence */
"실제로 포인터를 전달하는 이유는 무엇입니까?" 왜 단지 get_num_lines
의 뜻 깊은 반환을 이용하고 발신자에게 회선의 수를 단순히 반환합니까? size_t get_num_lines (FILE *fin)
참고 : 일반적인 관행 열고 파일이 호출하는 함수 (여기 main()
)에서 읽을 열려 있는지 확인하고 매개 변수가 아닌 파일 이름으로 FILE *
포인터를 전달하는 것입니다. 파일 이름을 전달하고 함수에서이 파일을 모두 처리하는 것은 잘못이 아닙니다. 일반적인 접근 방식이 아닙니다.
그러나 파일의 행 수를 계산하기 위해 단순히 fgets
을 호출 할 수는 없습니다. 줄 수를 늘리기 전에 버퍼에 줄이 맞는지 확인해야합니다 (예 : 전체 줄을 읽은 다음 더 긴 줄의 첫 번째 254
문자가 아님). 이를 수행하려면 fgets
으로 읽은 행의 길이를 확인하고 마지막으로 읽은 문자가 '\n'
인지 확인해야합니다.
파일이 파일의 비 POSIX의 끝 (이 최종 '\n'
누락 의미)가있는 경우, 라인 카운트가 1에 의해 너무 적은 원인이 될 것입니다 한 번 더 (불행하게도 공통) 문제가 있습니다. 이것은 최종 문자를 올바르게 검증하는 부작용이며 카운트 기능을 제대로 작동시키기 위해서는 '\n'
이 필요합니다. 파일에 최종 '\n'
이 없으면 마지막 행이 계산되지 않게됩니다. 고맙게도 이는 단순히 행의 끝이 읽히지 않았 음을 나타내는 플래그를 설정 한 다음 fgets
읽기 루프를 종료 한 후 플래그가 설정되어 있는지 확인하는 방식으로 처리됩니다.
가 size_t fgets_nlines (FILE *fp)
{
int noeof = 0;
size_t n = 0;
char buf[BUF_SIZE] = "";
while (fgets (buf, BUF_SIZE, fp)) { /* read until EOF */
size_t len = strlen (buf); /* get buf length */
if (len && buf[len-1] != '\n') { /* if not complete line */
noeof = 1; /* set flag no EOL found */
continue; /* read until all chars in line are read */
}
noeof = 0;
n++;
}
if (noeof) /* handle non-POSIX EOF (add 1 to count) */
n++;
return n;
}
POSIX 의해 제공 함수 배향 제 라인을 필요로하지 않는다 :
함수 오픈 FILE*
포인터를 가지고 읽기 및 본 될 수 행의 수를 반환 모두 그 조각을 씌우고 파일 검사의 끝은 POSIX getline
입니다. 또한 라인의 길이에 관계없이 충분한 스토리지를 할당 할 수있는 이점이 있습니다. (또한 단점으로 간주 될 수도 있음).
size_t getline_nlines (FILE *fp)
{
size_t lines = 0, n = 0;
char *buf = NULL;
while (getline (&buf, &n, fp) != -1)
lines++;
free (buf);
return lines;
}
중 다음과 같이 쓸 수있다 (당신은 함수 이름을 조정해야)를 사용하여 간단한 예제 프로그램을 : 당신은 비슷한으로 getline
과 같은 일을 할 수 있습니다. 프로그램의 첫 번째 인수로 읽을 파일 이름이 필요합니다 (또는 인수가없는 경우 기본적으로 stdin
에서 읽음). Linux에서 wc -l
과 비슷한 출력을 제공하고 행 번호 출력의 일부로 읽은 파일 이름을 추가합니다. name이 인수로 제공되었거나, 단지 stdin
에서 읽으면 라인 수만 출력합니다.
#include <stdio.h>
#include <stdlib.h> /* for free if using getline */
#include <string.h>
#ifndef BUF_SIZE /* fgets buffer size */
#define BUF_SIZE 8192
#endif
size_t fgets_nlines (FILE *fp); /* comment/uncomment as required */
// size_t getline_nlines (FILE *fp);
int main (int argc, char **argv) {
size_t nlines = 0;
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed.");
return 1;
}
nlines = fgets_nlines (fp);
// nlines = getline_nlines (fp); /* same note, comment/uncomment */
if (nlines) {
if (argc > 1)
printf ("%zu %s\n", nlines, argv[1]);
else
printf ("%zu\n", nlines);
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
return 0;
}
봐 일들을 통해, 관련 문제 및 fgets
및 getline
가 아닌 POSIX EOF 이유를 처리하는 방법의 차이에 대해 생각합니다. 추가 질문이 있으면 알려주십시오.
실패에 .csv는 명령 줄을 통해 입력됩니다. – jruo
'fopen'이 파일을 열 수없고 널 포인터를 반환하면 어떨까요? 그리고 Eric Lippert의 [작은 프로그램을 디버깅하는 방법] (https://ericlippert.com/2014/03/05/how-to-debug-small-programs/)을 읽고 시간을내어 디버거가 이와 같은 충돌을 잡을 수 있습니다. –
또한 [fgets'] (http://en.cppreference.com/w/c/io/fgets) *가 반환하는 것을 확인해야합니다. 배열'line'은 첫 번째 원소에 대한 포인터로 감쇠 될 것이고 결코 null 포인터가되지 않을 것입니다. 마지막으로 [while (! feof (file))]이 항상 잘못된 이유는 무엇입니까?] (https://stackoverflow.com/questions/5431941/why-is-while-feof-file-always-wrong) –