Задать вопрос
@Fortunato28

Почему getchar() читает все символы из stdin?

Вопрос из области легендарного "объясните как работает программа, которую я написал".
Суть: необходимо читать из stdin с помощью epoll, пока пользователь не отправит EOF. А как отправит - завершить работу. Т.е. как cat, запущенная без аргументов.
Мой код:
int epfd = epoll_create(1);
    if(epfd == -1)
    {
        perror("epoll_create is bad");
        exit(EXIT_FAILURE);
    }

    static struct epoll_event ev;

    ev.events = EPOLLIN;
    ev.data.fd = STDIN_FILENO;
    if(epoll_ctl(epfd, EPOLL_CTL_ADD, STDIN_FILENO, &ev) == -1)
    {
        perror("epoll_ctl connSock is bad");
        exit(EXIT_FAILURE);
    }

    string buffLine = "";
    vector<string> urls;
    char buffer;
    char c;
while(true)
    {
        memset(&ev, 0, sizeof(ev));
        int nfds = epoll_wait(epfd, &ev, 1, 3000);
        if(nfds == -1)
        {
            perror("epoll_wait is bad");
            exit(EXIT_FAILURE);
        }

        if((ev.data.fd == STDIN_FILENO) && (ev.events == EPOLLIN))
        {
            /* Не понимаю, почему всё хорошо работает.
             * Почему getchar() считывает все символы.*/
            c = getchar();
            cout << "C = " << c << endl;
            if(c == EOF) // Пользователь передал EOF
                break;
            if(c == '\n') // Пользователь завершил ввод текущей url
            {
                urls.push_back(buffLine);
                buffLine.clear();
            }
            else
                buffLine += c;
        }


Я полагал, что getchar() будет хватать один символ, а там придётся ещё вводить один цикл или вроде того. Пожалуйста, объясните, почему getchar() считывает правильно все символы и всё работает так, как нужно.
  • Вопрос задан
  • 323 просмотра
Подписаться 2 Простой Комментировать
Решения вопроса 1
getchar() выполняет буферизированный ввод, при вызове getchar() вызывается read(), который считывает данные в буфер, затем на последующих вызовах getchar() данные возвращаются из буфера без вызова read(). Вместе с select/poll/epoll правильней напрямую использовать read().
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
@pantoriy
Ну это так называемый канонический режим считывания с терминала. Ведь вы тут с него пытаетесь считать. Можно конечно перенастроить терминал и включить режим посимвольного считывания, но это может привести к краху терминала, если не сделать это аккуратно... Вообще не рекомендуется менять режимы терминала ибо с терминалом работают все проги в сессии... Вы просто завалите сессию. Это катастрофа... Аккуратно это как операцией delete. Не забудьте восстановить канонический режим когда это уже не надо! А так при первом запуске getchar() происходит считывание до конца строки, вместе с концом строки. Или пока весь буфер не будет заполнен. Размер буфера устанавливается в системе и он мб 4 кб 8 или 16 или сколько вы там установите. Менять эту величину не стоит. Особенно в сторону уменьшения. Др проги об этом не знают. Обычно строка намного короче. Вот тут дан совет, попробовать read. Говорю сразу, не прокатит! Будет тоже самое. Вообще это не имеет отношения к тому каким образом вы будете считывать. Потому что режим работы терминала не изменится!!! Что делаю я? Я когда считал нужное кол-во символов просто сбрасываю остаток. И это самое разумное. Я не нашёл спецфункции для этого. Надо просто считывать пока не считаешь конец строки. Есть др функция, getline. Она сразу выдаёт всю строку. Но ей надо подсунуть адрес указателя на строку предварительно установленный в NULL. Она выделит память под нужное кол-во и заполнит его вместе с концом строки. Вы получите сразу 2 геморроя: 1 в конце память под строку надо освободить или вы автоматом получите утечку памяти. Второе возня с символом конца строки. Ну и сами понимаете это неэффективно... особенно если это в цикле, а так и бывает. Я выяснил, самое лучшее это записать типа scanf(" %[^\n]%*c", str). str обычная стековая строка с фиксированым размером. Для страховки можно указать макс длину считывания. Но потом сбр буфер можно той же командой scanf("%*[^\n]%*c") правда не уверен, что она не зациклится(не проверял...). Кстати в винде такой проблемы нет. И если написать на др языке тоже. На фортране к примеру.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

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