2014-12-06 1 views
6

Как я должен читать длинный ввод с использованием fgets(), я не совсем понимаю его.Как читать ввод неизвестной длины с помощью fgets

Я написал это

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

int main() 
{ 
    char buffer[10]; 
    char *input; 
    while (fgets(buffer,10,stdin)){ 
     input = malloc(strlen(buffer)*sizeof(char)); 
     strcpy(input,buffer); 
    } 
    printf("%s [%d]",input, (int)strlen(input)); 
    free(input); 
    return 0; 
} 
+2

Посмотрите на 'realloc'. И рассмотрите стратегию удвоения распределения каждый раз, а также прямое считывание в буфер. BTW: Формат printf для 'size_t' -'% zu'. – Deduplicator

+1

Если у вас есть это, рассмотрите возможность использования '' getline' '(http://linux.die.net/man/3/getline) вместо 'fgets'. – 5gon12eder

+0

Я знаю о getline, но я должен использовать fgets – lllook

ответ

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

int main(void) 
{ 
    char buffer[10]; 
    char *input = 0; 
    size_t cur_len = 0; 
    while (fgets(buffer, sizeof(buffer), stdin) != 0) 
    { 
     size_t buf_len = strlen(buffer); 
     char *extra = realloc(input, buf_len + cur_len + 1); 
     if (extra == 0) 
      break; 
     input = extra; 
     strcpy(input + cur_len, buffer); 
     cur_len += buf_len; 
    } 
    printf("%s [%d]", input, (int)strlen(input)); 
    free(input); 
    return 0; 
} 

Речь идет о минимальном наборе изменений, который даст вам полную строку ввода. Это увеличивает пространство до 9 байтов за раз; это не лучший способ сделать это, но есть дополнительная учетная запись, в которой задействованы лучшие способы (удвоение выделенного пространства и ведение записи о том, сколько средств выделено против того, сколько используется). Обратите внимание, что cur_len записывает длину строки в пространстве, на которое указывает input, за исключением терминала null. Также обратите внимание, что использование extra позволяет избежать утечки памяти при неспособности выделить.

strcpy() операция может быть законно заменена memmove(input + cur_len, buffer, buf_len + 1) (и в этом контексте, можно использовать memcpy() вместо memmove(), но это не всегда работает, а memmove() это всегда работает, так что надежнее использовать memmove()).


С длиной удвоением - в cur_max переменных записях, сколько пространства выделяются, и cur_len записи, сколько пространства используется.

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

int main(void) 
{ 
    char buffer[10]; 
    char *input = 0; 
    size_t cur_len = 0; 
    size_t cur_max = 0; 
    while (fgets(buffer, sizeof(buffer), stdin) != 0) 
    { 
     size_t buf_len = strlen(buffer); 
     if (cur_len + buf_len + 1 > cur_max) 
     { 
      size_t new_len = cur_max * 2 + 1; 
      if (buf_len + 1 > new_len) 
       new_len = buf_len + 1; 
      char *extra = realloc(input, new_len); 
      if (extra == 0) 
       break; 
      input = extra; 
      cur_max = new_len; 
     } 
     strcpy(input + cur_len, buffer); 
     cur_len += buf_len; 
    } 
    printf("%s [%d]", input, (int)strlen(input)); 
    free(input); 
    return 0; 
} 
+0

спасибо, поэтому, если бы я хотел прочитать строки, которые, как правило, очень длинные, я должен использовать изменение realloc в 2 раза больше, чем его предыдущий размер? – lllook

+0

Нет; проще всего использовать 'char buffer [4096];' и продолжать жизнь. Только JSON входит в строки длиннее, чем это, а затем вы можете прочитать весь файл в одной строке. (Я преувеличиваю, но не так сильно, как все это.) В противном случае идея состоит в том, что вы сначала распределяете 10 байт, затем перераспределяете 20, затем перераспределяете 40 (но читаете 2 единицы из 10 в дополнительное пространство), затем 80, затем 160, затем 320 ... Если вы не освободите место до того, как функция выйдет, и будет больше, чем, скажем, 64 байта, не используется, вы можете использовать 'realloc()', чтобы уменьшить выделение до требуемого размер. –

+0

Если вы не на крошечной крошечной машине, буфер 4 KiB не вызовет у вас горя. Если вы на такой крошечной машине, вы, вероятно, вообще не используете 'malloc()' или читаете такие длинные строки. –

3

Лучший подход заключается в использовании механизм ввода, который будет выделять для вас, таких как getline (или даже scanf). (. Примечание:scanf не выделяет во всех составителей Это делает в gcc/Linux, но не с Windows/Codeblocks/gcc)

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

int main() 
{ 
    char *input; 
    scanf ("%m[^\n]%*c", &input); 
    printf("\n %s [%d]\n\n",input, (int)strlen(input)); 
    free(input); 
    return 0; 
} 

выход:

$ ./bin/scanfinput 
This is my longer string. 

This is my longer string. [25] 

GetLine Пример

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

int main() 
{ 
    char *input = NULL;  /* input buffer, NULL forces getline to allocate */ 
    size_t n = 0;   /* maximum characters to read (0 - no limit  */ 
    ssize_t nchr = 0;  /* number of characters actually read   */ 

    if ((nchr = getline (&input, &n, stdin)) != -1) 
     input[--nchr] = 0; /* strip newline */ 

    printf ("\n %s [%zd]\n\n", input, nchr); 
    free(input); 

    return 0; 
} 
+0

спасибо, это дает мне предупреждение, неизвестный формат% m, im работает с c90 – lllook

+1

Если вы работаете в Windows, 'scanf' не будет выделять, если использовать' codeblocks' и т. Д., Используйте 'getline'. Я буду обновлять только для Linux. Я работал с 'gcc/C89' с расширениями gnu. –

+0

Я компилирую на linux, в кодовых блоках – lllook