Как прочитать стандартный ввод в строковую переменную до EOF в C? - PullRequest
8 голосов
/ 23 марта 2010

Я получаю «Ошибка шины» при попытке прочитать stdin в переменную char*. Я просто хочу прочитать весь материал, приходящий за stdin и поместить его сначала в переменную, а затем продолжить работу над переменной.

Мой код выглядит следующим образом:

char* content;
char* c;
while( scanf( "%c", c)) {
 strcat( content, c);
}

fprintf( stdout, "Size: %d", strlen( content));

Но почему-то я всегда получаю сообщение об ошибке шины, вызывая cat test.txt | myapp, где myapp - это скомпилированный код выше.

Мой вопрос: как мне прочитать stdin до EOF в переменную? Как вы видите в коде, я просто хочу напечатать размер ввода, поступающего через стандартный ввод, в этом случае он должен быть равен размеру файла test.txt.

Я думал, что достаточно просто использовать scanf, может быть, буферизованный способ чтения stdin?

Ответы [ 5 ]

17 голосов
/ 23 марта 2010

Во-первых, вы передаете неинициализированные указатели, что означает, что scanf и strcat будут записывать память, которой вы не владеете. Во-вторых, strcat ожидает две строки с нулевым символом в конце, а c - просто символ. Это снова заставит его читать память, которой вы не владеете. Вам не нужен scanf, потому что вы не выполняете никакой реальной обработки. Наконец, чтение одного символа за раз происходит излишне медленно. Вот начало решения с использованием изменяемого размера буфера для последней строки и фиксированного буфера для вызова fgets

#define BUF_SIZE 1024
char buffer[BUF_SIZE];
size_t contentSize = 1; // includes NULL
/* Preallocate space.  We could just allocate one char here, 
but that wouldn't be efficient. */
char *content = malloc(sizeof(char) * BUF_SIZE);
if(content == NULL)
{
    perror("Failed to allocate content");
    exit(1);
}
content[0] = '\0'; // make null-terminated
while(fgets(buffer, BUF_SIZE, stdin))
{
    char *old = content;
    contentSize += strlen(buffer);
    content = realloc(content, contentSize);
    if(content == NULL)
    {
        perror("Failed to reallocate content");
        free(old);
        exit(2);
    }
    strcat(content, buffer);
}

if(ferror(stdin))
{
    free(content);
    perror("Error reading from stdin.");
    exit(3);
}

РЕДАКТИРОВАТЬ: Как намекал Вулфер, значение NULL в вашем входе приведет к преждевременному завершению строки при использовании fgets. getline - лучший выбор, если он доступен, поскольку он обрабатывает выделение памяти и не имеет проблем с вводом NUL.

7 голосов
/ 23 марта 2010

Ваша проблема в том, что вы никогда не выделяли c и content, поэтому они не указывают нигде в определенном месте - скорее всего, они указывают на какую-то нераспределенную память или что-то, чего вообще не существует.А потом вы помещаете данные в них.Вы должны выделить их в первую очередь.(Это то, что обычно означает ошибка шины; вы пытались сделать доступ к памяти недопустимым.)

(С другой стороны, поскольку c всегда содержит только один символ, вы можете объявить его как char c и передайте &c в scanf. Не нужно объявлять строку символов, когда вы это сделаете.)

Как только вы это сделаете, вы столкнетесь с проблемой проверки того, что contentдостаточно долго, чтобы держать все входные данные.Либо вам нужно угадать, какой объем входных данных вы ожидаете, и распределить его как минимум так долго (а затем вывести ошибку, если вы его превысите), либо вам нужна стратегия, чтобы перераспределить его в большем размере, если он недостаточно длинный.

О, и вы также столкнетесь с проблемой, что strcat ожидает строку, а не один символ.Даже если вы оставите c как char*, вызов scanf не сделает его строкой.Односимвольная строка - это (в памяти) символ, за которым следует нулевой символ для обозначения конца строки.scanf, при поиске одного символа, не будет вставлять нулевой символ после него.В результате strcpy не будет знать, где находится конец строки, и будет блуждать по памяти в поисках нулевого символа.

6 голосов
/ 23 марта 2010

Поскольку вас не интересует реальное содержимое, зачем создавать строку? Я бы также использовал getchar():

int    c;
size_t s = 0;

while ((c = getchar()) != EOF)
{
  s++;
}

printf("Size: %z\n", s);

Этот код будет корректно обрабатывать случаи, когда в вашем файле содержится '\0' символов.

1 голос
/ 23 марта 2010

Проблема здесь в том, что вы ссылаетесь на переменную-указатель, которая не распределяет память через malloc, следовательно, результаты будут неопределенными, а не только при использовании strcat для неопределенного указателя, который может указывать на что-либо , вы закончили с ошибкой шины!

Это будет фиксированный код, необходимый ....

char* content = malloc (100 * sizeof(char));
char c;
if (content != NULL){
   content[0] = '\0'; // Thanks David!
   while ((c = getchar()) != EOF)
   {
       if (strlen(content) < 100){
           strcat(content, c);
           content[strlen(content)-1] = '\0';
       }
   }
}
/* When done with the variable */
free(content);

Код подчеркивает ответственность программиста за управление памятью - для каждого malloc есть free, если нет, у вас есть утечка памяти!

Редактировать: Благодарю Дэвид Гелхар за то, что он указал на мой глюк! Я исправил приведенный выше код, чтобы отразить исправления ... конечно, в реальной ситуации, возможно, фиксированное значение 100 можно изменить на, возможно, #define, чтобы упростить расширение буфера путем удвоения по сравнению с объем памяти через realloc и обрезать его до размера ...

0 голосов
/ 06 июня 2014

Предполагая, что вы хотите получить (короче, чем MAXL-1 chars) строки, а не обрабатывать ваш file char по char, я сделал следующее:

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...