Мне нужно иметь возможность читать строку со стандартного ввода

Я пытаюсь прочитать некоторые данные из стандартного ввода. Это будут числа (любое количество цифр), разделенные пробелом. Проблема в том, что я не знаю длину заранее. Я хочу иметь возможность читать со стандартного ввода и использовать его для управления чем-либо, это повторяется до тех пор, пока не будет нажата ^d.

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

int 
main(){

char input[] = scanf("%s", &input);

for(int i=0; i<sizeof(&input);i++){

//Do something

}

}

Это не работает, но как я могу изменить его, чтобы он работал?

Если это числа, почему вы не используете %d вместо %s?

Barmar 10.04.2019 20:31

Синтаксис вашего кода неверен. Я предлагаю вам открыть хорошую книгу по C и изучить основы.

machine_1 10.04.2019 20:34

@Unholysheep, может быть, но мне трудно понять весь ответ.

Rogelio 10.04.2019 20:49

@ Бармар, да, спасибо, это была опечатка.

Rogelio 10.04.2019 20:49

@machine_1 спасибо, "гений", ты ОЧЕНЬ полезен ?

Rogelio 10.04.2019 20:49
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
5
543
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

попробуйте сначала прочитать строку, а затем прочитать целые числа в строке

char *x ,*s ;
int d ;
while (fgets(input, sizeof(input), stdin)) {
x = input;
for (x = input; ; x = s) {
    d = strtol(x, &s, 10);
    if (x == s)
        break;        
  }
}
Ответ принят как подходящий

его пример. Вам нужно добавить проверку результатов malloc и realloc (я не делал этого для простоты)

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

#define CHUNK   32 

char *readline(void)
{
    size_t csize = CHUNK;
    size_t cpos = 0;
    char *str = malloc(CHUNK);
    int ch;
    while((ch = fgetc(stdin)) != '\n' && ch != '\r')
    {
        str[cpos++] = ch;
        if (cpos == csize)
        {
            csize += CHUNK;
            str = realloc(str, csize);
        }
    }
    str[cpos] = 0;
    return str;
}

Тогда у вас есть строка, которую вы можете sscanf. После использования освободить память

И преобразовать его в массив целых чисел (поскольку вы не знаете размер результирующего массива):

int *tointegers(char *str, char *delimiters, size_t *size)
{
    int *array = malloc(CHUNK * sizeof(*array));
    char *ptr;

    *size = 0; 
    ptr = strtok (str, delimiters);
    while (ptr != NULL)
    {
        array[(*size)++] = atoi(ptr);
        if (*size == CHUNK)
        {
            array = malloc((*size + CHUNK) * sizeof(*array));
        }
        ptr = strtok (NULL, delimiters);
    }
    return array;
}

Те же замечания о проверке результатов malloc и realloc и освобождении памяти

Основная проблема здесь в том, что вы не знаете количество элементов заранее, в этих случаях вам нужно зарезервировать место для хранения элементов с использованием динамической памяти, вы можете использовать очередь или вы можете использовать realloc, также избегайте использования scanf в этом образом, всегда ограничивайте длину строки:

char str[100];

scanf("%99s", str); /* buffer overflow protection */

и всегда проверяйте результат:

if (scanf("%99s", str) != 1) {
    /* something went wrong */
}

Пример использования fgets (в качестве альтернативы scanf) и strtol с сохранением данных в очереди:

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

struct node {
    struct node *next;
    int data;
};

void *enqueue(struct node **root, int data)
{
    struct node *node;

    node = malloc(sizeof *node);
    if (node == NULL) {
        return NULL;
    }
    if (*root == NULL) {
        node->next = node;
    } else {
        node->next = (*root)->next;
        (*root)->next = node;
    }
    node->data = data;
    *root = node;
    return node;
}

int dequeue(struct node **root)
{
    struct node *node;
    int data = 0;

    node = *root;
    if (node != NULL) {
        node = node->next;
        data = node->data;
        if (*root == node) {
            *root = NULL;
        } else {
            (*root)->next = node->next;
        }
        free(node);
    }
    return data;
}

int main(void)
{
    struct node *node = NULL;
    char str[512];
    char *ptr;
    int data;

    ptr = fgets(str, sizeof str, stdin);
    if (ptr != NULL) {
        while (*ptr) {
            data = (int)strtol(ptr, &ptr, 10);
            if (!isspace(*ptr)) { // If we don't have a blank space
                break;            // exit the loop
            }
            enqueue(&node, data);
            ptr++;
        }
    }
    while (node != NULL) {
        data = dequeue(&node);
        printf("%d\n", data);
    }
    return 0;
}

Вход

123 456 -789

Выход

123
456
-789

Что делать, если строка длиннее 99 символов? Если это 1000, 100000, 10000000?

0___________ 10.04.2019 21:49

Использование списка для хранения целых чисел, которые являются излишними

0___________ 10.04.2019 21:50

@P_J, вы можете использовать fgets в цикле, пока не найдете новую строку.

David Ranieri 10.04.2019 21:54

Почему перебор?, для этого и составляются списки.

David Ranieri 10.04.2019 21:55

Чтобы прочитать (и сохранить) неизвестное количество символов из stdin, у вас есть два основных варианта:

  1. если доступно, используйте POSIX getline() для считывания всех символов в буфер (getline перераспределит память по мере необходимости для хранения всей строки ввода, включая завершающий символ *nul), или
  2. если вам нужно написать действительно переносимый код, обработайте начальное выделение памяти и перераспределение по мере необходимости, используя malloc и realloc.

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

Есть много способов приблизиться к чтению, используя функцию символьного ввода, такую ​​как getchar(), или используя некоторый буфер фиксированного размера и fgets для чтения фиксированного количества символов за раз. Это действительно зависит от вас. Избегайте scanf для одного символа, в этом нет необходимости. Базовое чтение буферизуется файловой системой, поэтому производительность не снижается независимо от того, что вы выберете. (Linux предоставляет 8192-байтовый буфер чтения размером с IO_BUFSIZ (теперь BUFSIZ сейчас, см. glibc/libio/stdio.h — #define BUFSIZ 8192 и _IO_BUFSIZ изменено на BUFSIZglibc фиксирует 9964a14579e5eef9), и Windows доказывает аналогичный 512-байтовый буфер)

Суть в том, чтобы делать это шаг за шагом, проверять каждое распределение и обрабатывать ошибки по мере необходимости. Вы используете временный указатель с realloc, потому что, если realloc терпит неудачу, он возвращает NULL, а если вы присваиваете возврат realloc исходному указателю, вы перезаписываете адрес исходного блока памяти с помощью NULL, создавая утечку памяти. При использовании временного указателя в случае сбоя realloc ваши существующие данные по-прежнему доступны через исходный указатель.

Например, чтобы удвоить размер текущего выделенного buffer с текущим размером выделенного buffersize, вы могли бы наивно сделать:

buffer = realloc (buffer, 2 * buffersize);  /* wrong - potential memory leak */
if (buffer == NULL) {           /* validate reallocation */
    perror ("realloc-buffer");  /* output error message */
    /* handle error */
}
buffersize *= 2;    /* increment buffersize */

Вместо этого вы будете делать:

void *tmp = realloc (buffer, 2 * buffersize);  /* use a temporary pointer */
if (tmp == NULL) {              /* validate reallocation */
    perror ("realloc-buffer");  /* output error message */
    /* handle error, buffer still points to original block */
}
buf = tmp;
buffersize *= 2;

Понять, как это работает, можно на минимальном простом примере. Следующее будет читать строку неизвестного размера из stdin с использованием переносимых getchar(), malloc и realloc с использованием схемы перераспределения, которая просто удваивает размер буфера каждый раз, когда вы заполняете его. (вы можете увеличить на любую дополнительную сумму, которую хотите, но избегайте перераспределения для каждого прочитанного символа - это было бы неэффективно, удвоение размера буфера или какое-либо подобное увеличение минимизирует количество раз, которое вы перераспределяете)

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

#define NCHR 8  /* initial number of characters to allocate */

int main (void) {

    int c;                      /* char to read from stdin */
    size_t  ndx = 0,            /* index/count of characters */
            nchr = NCHR;        /* number of characters allocated in buf */
    char *buf = malloc (nchr);  /* buffer allocated for nchr chars */

    if (buf == NULL) {          /* validate that allocation succeeds */
        perror ("malloc-buf");  /* otherwise handle error */
        return 1;               /* bail */
    }

    /* read chars from stdin until '\n' or EOF */
    while ((c = getchar()) != '\n' && c != EOF) {
        if (ndx == nchr - 1) {  /* check if reallocation is needed */
            void *tmp = realloc (buf, 2 * nchr);    /* double buf size */
            if (tmp == NULL) {  /* validate realloc succeeds */
                perror ("realloc-buf"); /* handle error */
                break;          /* break don't bail, buf holds chars read */
            }
            buf = tmp;      /* assign newly sized block of mem to buf */
            nchr *= 2;      /* update nchr to new allocation size */
        }
        buf[ndx++] = c;     /* assign char to buf, increment index */
    }
    buf[ndx] = 0;       /* nul-terminate buffer */

    if (c == EOF)       /* if read stopped on EOF */
        putchar ('\n'); /* tidy up outputting \n */

    printf ("length : %zu\ncontent: %s\n", ndx, buf);

    free (buf);     /* don't forget to free what you allocate */
}

(Примечание: проверка EOF, которая будет сгенерирована Ctrl + d (или Ctrl + z в Windows) и вывод дополнительного '\n' при обнаружении, иначе ваш следующий вывод начнется в конце вашего текущего ввода. Также обратите внимание nchr - 1 в if (ndx == nchr - 1) убедитесь, что есть всегда доступен 1 ​​символ для хранения нулевое завершение после выхода из цикла.)

Пример использования/вывода

$ ./bin/getchar_dyn
1234 5678 9012 3456 7890
length : 24
content: 1234 5678 9012 3456 7890

Использование памяти/проверка ошибок

В любом коде, который вы пишете, который динамически выделяет память, у вас есть 2 обязанности для любого выделенного блока памяти: (1) всегда сохраняйте указатель на начальный адрес для блока памяти, поэтому (2) это может быть освобожден, когда он больше не нужен.

Крайне важно, чтобы вы использовали программу проверки ошибок памяти, чтобы убедиться, что вы не пытаетесь получить доступ к памяти или писать за пределы/за границы вашего выделенного блока, пытаться читать или основывать условный переход на неинициализированное значение и, наконец, подтвердить что вы освобождаете всю память, которую вы выделили.

Для Linux valgrind — нормальный выбор. Для каждой платформы есть похожие средства проверки памяти. Все они просты в использовании, просто запустите через них свою программу.

$ valgrind ./bin/getchar_dyn
==28053== Memcheck, a memory error detector
==28053== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==28053== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==28053== Command: ./bin/getchar_dyn
==28053==
1234 5678 9012 3456 7890
length : 24
content: 1234 5678 9012 3456 7890
==28053==
==28053== HEAP SUMMARY:
==28053==     in use at exit: 0 bytes in 0 blocks
==28053==   total heap usage: 3 allocs, 3 frees, 56 bytes allocated
==28053==
==28053== All heap blocks were freed -- no leaks are possible
==28053==
==28053== For counts of detected and suppressed errors, rerun with: -v
==28053== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Всегда подтверждайте, что вы освободили всю выделенную память и что ошибок памяти нет.

Просмотрите все и дайте мне знать, если у вас есть дополнительные вопросы.

Другие вопросы по теме