fgets not reading whole line

余生长醉 提交于 2020-01-17 03:58:04

问题


I have a simple function, which is supposed to read line from standard input and put it into an char array, and I call this function in a loop till EOF is inputed. The problem is, that for extremely long lines (more than 10k characters) the fgets reads only a number of characters and stops, although it has not encountered any \n and the buffer has sufficient space, therefore next invoking of this function reads the rest of the line. Is there a reason for this behaviour (wrongly written code, some buffers I am unavare of)? Is it possible to fix it? If I have something wrong in the code I will be gratefull if you point it out.

static int getLine(char** line){
    if(feof(stdin)) return 0;
    int len=0;
    char* pointer=NULL;
    int max = 1;
    while(1){
        max+=400;
        *line=(char*)realloc( *line,max);
        if(pointer==NULL)
            pointer=*line;
        if(fgets(pointer, 401, stdin)==NULL)break;
        int len1=strlen(pointer);
        len+=len1;
        if(len1!=400 || pointer[len1]=='\n')break;
        pointer+=len1;
    }
    if(len==0)return 0;
    if((*line)[len-1]=='\n'){
    *line=(char*)realloc(*line, len); 
    (*line)[len-1]='\0';
    return len-1;}//without \n
    return len;
}

回答1:


I think it likely that your problem is the way you use pointer:

char* pointer=NULL;
int max = 1;
while(1){
    max+=400;
    *line=(char*)realloc( *line,max);
    if(pointer==NULL)
        pointer=*line;
    if(fgets(pointer, 401, stdin)==NULL)
        break;
    int len1=strlen(pointer);
    len+=len1;
    if(len1!=400 || pointer[len1]=='\n')
        break;
    pointer+=len1;
}

The trouble is that realloc() can change where the data is stored, but you fix it to the location you are first given. It is more likely that you'll have data move on reallocation if you handle large quantities of data. You can diagnose this by tracking the value of *line (print it after the realloc() on each iteration).

The fix is fairly simple: use an offset instead of a pointer as the authoritative length, and set pointer on each iteration:

enum { EXTRA_LEN = 400 };
size_t offset = 0;
int max = 1;
while (1)
{
    max += EXTRA_LEN;
    char *space = (char*)realloc(*line, max);  // Leak prevention
    if (space == 0)
        return len;
    *line = space;
    char *pointer = *line + offset;
    if (fgets(pointer, EXTRA_LEN + 1, stdin) == NULL)
        break;
    int len1 = strlen(pointer);
    len += len1;
    if (len1 != EXTRA_LEN || pointer[len1] == '\n')
        break;
    offset += len1;
}

I have reservations about the use of 401 rather than 400 in the call to fgets(), but I haven't the energy to expend establishing whether it is correct or not. I've done about the minimum changes to your code that I can; I would probably make more extensive changes if it were code I was polishing. (In particular, max would start at 0, not 1, and I would not use the +1 in the call to fgets().



来源:https://stackoverflow.com/questions/27152451/fgets-not-reading-whole-line

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!