2017-12-28 44 views
3

아래 코드를 실행할 때 분할 오류가 발생합니다.getline()/strsep() 조합으로 인해 세그먼트 화 오류가 발생합니다.

기본적으로 3M 라인 이상인 .csv 파일을 읽고 다른 것들은 나중에 (문제와 관련 없음) 읽어야하지만 207746 회 반복 후 세그먼트 화 오류가 반환됩니다. p = strsep(&line,"|");을 제거하고 line 전체를 인쇄하면> 3M 라인이 인쇄됩니다.

int ReadCSV (int argc, char *argv[]){ 

    char *line = NULL, *p; 
    unsigned long count = 0; 

    FILE *data; 
    if (argc < 2) return 1; 
    if((data = fopen(argv[1], "r")) == NULL){ 
     printf("the CSV file cannot be open"); 
     exit(0); 
    } 


    while (getline(&line, &len, data)>0) { 

     p = strsep(&line,"|"); 

     printf("Line number: %lu \t p: %s\n", count, p); 
     count++; 
    } 

    free(line); 
    fclose(data); 

    return 0; 
} 

메모리 할당과 관련이 있지만 문제를 해결하는 방법을 알 수는 없습니다.

+0

라인 # 207746에 구분 기호가 하나 이상 있습니까? strsep()는 그렇지 않은 경우 다르게 동작합니다. –

+3

'strsep'는 첫 번째 인수를 수정하므로, 처음으로 할당 된 버퍼'getline'의 시작을 잃어 버리는 것입니다. 그렇게하지 않으면,'strsep '를 사용해야한다면 별도의 임시 파일을 사용하십시오. 'p'가 NULL이 아닌지 확인하십시오. 'len'은 어디에 선언 되었습니까? 'getline'을 호출하기 전에 0입니까? – Useless

답변

6

getlinestrsep의 조합은 두 함수가 포인터로 포인터를 초기 인수로 전달하기 때문에 혼동을 일으키는 경우가 있습니다. strsep 통해 포인터를 다시 getline 위해 전달하면 두 번째 반복에 정의되지 않은 동작의 위험을 실행합니다.

예 : getlineline에 101 바이트를 할당하고 100 바이트 문자열을 읽습니다. len은 이제 101로 설정됩니다. strsep을 호출하면 문자열 중간에 '|'이 있으므로 lineline+50으로 지정합니다. 이제 getline으로 다시 전화하십시오. 다른 100 자 라인을보고, len이 여전히 101이기 때문에 버퍼에 복사하는 것이 좋다고 결론을 내립니다. 그러나 line은 버퍼의 중간을 가리키고 있으므로 100자를 쓰면 정의되지 않은 동작이됩니다.

strsep를 호출하기 전에 line의 복사본을 만듭니다 : 당신이 getline에 전달

while (getline(&line, &len, data)>0) { 
    char *copy = line; 
    p = strsep(&copy, "|"); 
    printf("Line number: %lu \t p: %s\n", count, p); 
    count++; 
} 
이제

line는 루프 반복 사이에 유지됩니다. 식 getline(&line, &len, data)에서

1

봐와 읽기 manpage : 라인 NULL로 설정하고 * LEN은 호출 전에 0으로 설정된다

* 경우 의 getline()가 광고를 저장하는 버퍼를 할당 할 . 이 버퍼 은 getline()이 실패한 경우에도 사용자 프로그램에 의해 해제되어야합니다.

(우리는 여기서 len 선언을 참조 그냥 당신의 진짜 코드가 제대로이 작업을 수행 가정하자 수는 없지만)를 호출하기 전에,

다른 방법이 루프 라운드 처음에 경우이어야한다 getline(), * line은 malloc (3) 할당 버퍼 * len 바이트의 포인터를 포함 할 수 있습니다. 버퍼가 라인을 보관할만큼 크지 않으면 getline()은을 realloc (3)으로 크기를 조정하고 필요에 따라 * line 및 * len을 업데이트합니다.

OK, 그것의 크기 lenmalloc 의해 할당 된 버퍼를 가리켜 야 line != NULL 그래서 만약. 첫 번째 호출에서 getline에 할당 된 버퍼 (위와 같음)가이를 만족시킵니다.

line어딘가에서으로 향하는 것은 좋지 않습니다. 버퍼는 시작이어야합니다.

이제 식 strsep(&line,"|")보고에 대한 manpage을 읽을 이 :

...이 토큰은 널 바이트 ('\ 0')로 구분 기호를 덮어 종료되고 * 라인은 이렇게 변경 그래서, 첫 번째 인수 (line가) 당신이 다시와 strsep를 호출 할 수있는 토큰

과거의 시점에 업데이트됩니다 동일한 첫 번째 인수는 다음 다음 토큰을 가져옵니다. 즉, 도 이제는 이 잘못 되었기 때문에 linegetline의 더 이상 유효한 인수가 아닙니다. 이는 malloc 버퍼의 시작이 아니기 때문입니다. 실제로

, 하나

  1. getline 버퍼 당신이 준,하지만 당신은 첫 번째 토큰의 길이에 의해 line을 진행하기 때문에, 그것은 당신의 할당 된 블록의 끝 떨어져 기록에 len 바이트를 읽으려고합니다 . 이것은 즉시 죽기보다는 힙을 손상시킬 수 있습니다.
  2. getline은 버퍼를 재 할당하려고 시도하지만 유효한 할당 된 블록이 아니기 때문에 힙 손상이 다시 발생합니다.

여기에도 불구하고 p은 NULL이 아니지만 손상된 line이 주된 문제인지 확인하지 않습니다.

아, 문제가 할당과 관련 있다고 생각되면 valgrind을 사용해보세요. 일반적으로 문제가 처음 발생하는 순간을 발견합니다.