Как считать стандартный вход в строковую переменную до EOF в C?

Я получаю "Ошибку шины", пытающуюся читать stdin в a 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 в переменную? Как Вы видите в коде, я просто хочу распечатать размер входа, прибывающего через stdin, в этом случае это должно быть равно размеру файла test.txt.

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

8
задан lpapp 27 September 2013 в 04:40
поделиться

4 ответа

Во-первых, вы передаете неинициализированные указатели, что означает, что 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);
}

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

17
ответ дан 5 December 2019 в 05:18
поделиться

Проблема здесь в том, что вы ссылаетесь на переменную-указатель, которой не выделена память через 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, если нет, у вас утечка памяти!

Edit: Спасибо David Gelhar за его указание на мой глюк! Я исправил код выше, чтобы отразить исправления... конечно, в реальной ситуации, возможно, фиксированное значение 100 может быть изменено на #define, чтобы было легко расширить буфер, удвоив объем памяти через realloc и обрезать его до размера...

1
ответ дан 5 December 2019 в 05:18
поделиться

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

int    c;
size_t s = 0;

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

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

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

7
ответ дан 5 December 2019 в 05:18
поделиться

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

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

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

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

7
ответ дан 5 December 2019 в 05:18
поделиться
Другие вопросы по тегам:

Похожие вопросы: