getchar() не передает EOF, а Ctrl+Z не завершает программу на Cygwin

Вот простая программа, считающая строки, символы и слова.

С вычислениями все в порядке, использую Cygwin.

Но при запуске, после ввода значений, программа не печатает nc, nw, nl и ждет ввода дальнейших значений.

Изменение EOF на 13 (Enter) все равно не помогает.

ctrl+Z тоже полезен: программа останавливается, пишет [n]+ Stopped, где n всегда другое число.

Код

#include <stdio.h>
#define IN 1
#define OUT 0


int main () {

    char c;
    int state;
    int nc, nw, nl;
    state = OUT;    

    while ((c=getchar()) != EOF) {
        nc++;

        if (c == 'n') 
            nw++;


        if (c == '\n' || c == ' ' || c == '\t') 
            state = OUT;
        else if (state = OUT){
            state = IN;
            nw++;
        }

    }
    printf ("%d %d %d", nc, nl, nw);
}

person Daniel Chepenko    schedule 04.10.2014    source источник
comment
В чем вопрос?   -  person Spikatrix    schedule 04.10.2014
comment
Внимание: неиспользуемая переменная nl !   -  person Spikatrix    schedule 04.10.2014
comment
Должно быть int c;   -  person M.M    schedule 04.10.2014


Ответы (5)


getchar возвращает int, а не char. Это необходимо сделать, потому что он может либо возвращать символьное значение, либо возвращать специальное значение EOF, чтобы указать конец входного файла. Поскольку EOF должно отличаться от всех значений типа char, оно не имеет типа char. Поэтому вам нужно определить переменную, которая содержит возвращаемое значение, как int, а не как char.

На некоторых платформах тип char имеет знак, и функция getchar на самом деле возвращает не символьное значение, а символьное значение, завернутое в неотрицательное значение. На практике это означает, что символы ASCII не изменяются, но на некоторых платформах символы, отличные от ASCII, преобразуются в значения от 128 до 255, а не от -128 до -1. Таким образом, вам нужно преобразовать возвращаемое значение getchar в char, когда вы используете его как символ. На платформах, где тип char беззнаковый, это ничего не меняет, но на платформах, где тип char знаковый, необходимо, чтобы символ 255 не был ошибочно принят за конец файла.

int c;
…
while ((c = getchar()) != EOF) {
    …
    if (c == 'n') /* ok because 'n' is part of ASCII and thus always positive */
        …
    if ((char)c == some_other_char) /*cast needed in general*/
        …
}

В Cygwin, как и в других Unix-подобных системах, нажмите Ctrl+D в начале строки, чтобы обозначить конец ввода. В Windows нужно нажать Ctrl+Z (под Cygwin, опять же, как и в других Unix-подобных системах, что вместо этого приостанавливает программы: они остановлены, но могут быть возобновлены командой bg). Ни один из них не отправляет программе управляющий символ, они отправляют индикацию конца файла, которую реализация C getchar переводит в значение EOF.

person Gilles 'SO- stop being evil'    schedule 04.10.2014
comment
Спасибо за подробный ответ. Но в любом случае ctrl+D не помогает. Я правильно понял, что вы нажимаете ctrl+D, затем начинаете ввод, затем нажимаете Enter? - person Daniel Chepenko; 04.10.2014
comment
@DanielChepenko Нет, нажмите Enter, затем Ctrl+D. - person Gilles 'SO- stop being evil'; 04.10.2014
comment
@mafso Упс. Слишком долго занимался встраиваемыми вещами… (stdio? Слишком высокоуровневый!) Исправлено, спасибо. - person Gilles 'SO- stop being evil'; 05.10.2014

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

  int nc = 0, nw = 0, nl = 0;

Когда вы этого не сделаете, они получат случайные значения.

Кроме того, ваша логика подсчета слов неверна.

Под else if (state = OUT) вы, вероятно, имели в виду else if (state == OUT).

person Igor    schedule 04.10.2014
comment
Изменена инициализация. Все равно не помогает. Что не так с логикой? - person Daniel Chepenko; 04.10.2014
comment
найдена ошибка в первом изменении условия nw на nl и в другом, если условие State==Out - person Daniel Chepenko; 04.10.2014
comment
Но это не помогает решить мою проблему - person Daniel Chepenko; 04.10.2014
comment
Я уверен, что это помогает. Какой смысл мне давать вам всю программу? Вы ничему не научитесь таким образом. - person Igor; 04.10.2014

#include <stdio.h>   
#define IN 1  
#define OUT 0
int main()
{
   int c, nl, nw, nc, state; //c is an int not a char        
   state = OUT;
   nl = nw = nc = 0;   // initialize variables to 0
   while ((c = getchar()) != EOF) {
       ++nc;
       if (c == '\n') // if c is a newline character 
           ++nl;     //increment no of lines
       if (c == ' ' || c == '\n' || c == '\t')
           state = OUT;
       else if (state == OUT) // == for comparing
       {
           state = IN;
           ++nw;
       }
    }
   printf("%d %d %d\n", nl, nw, nc);
return 0;
}

Ваша программа должна выглядеть так, как показано выше. c является int, а не char, потому что getchar преобразует входные данные в unsigned char и поскольку он не может содержать отрицательные значения, а EOF содержит -1, цикл while никогда не закончится.

Он заканчивается, когда вы нажимаете CTRL+Z (в Windows иначе CTRL+D), потому что нажатие этой кнопки имитирует EOF в stdin.

person Spikatrix    schedule 04.10.2014
comment
потому что char подписано по умолчанию - это зависит от реализации, подписано оно или нет. getchar и подобные функции преобразуют прочитанный символ в unsigned char, чтобы сделать его положительным и отличным от EOF (который обычно равен -1, но стандарт требует, чтобы он был только отрицательным). - person mafso; 05.10.2014
comment
@mafso, верно. Но теперь я в замешательстве! Преобразует ли getchar() ввод в unsigned char, и поскольку он не может содержать -1 (EOF), мы используем int? - person Spikatrix; 05.10.2014
comment
Да, точно. С 8-битным char символ в файле со значением -1 будет преобразован в 0xFF и, таким образом, будет отличаться от EOF (как int). - person mafso; 05.10.2014
comment
Завершится цикл или нет, зависит от того, является ли char подписанным или беззнаковым. Если он подписан, цикл OP заканчивается на EOF (но также, если в качестве входных данных задан символ со значением -1 (или что-то еще EOF), поэтому он может прерваться слишком рано); если он без знака (присваивание вызывает преобразование), это бесконечный цикл (по указанным вами причинам). В качестве еще одного примечания (я видел много вопросов об этом, поэтому следует упомянуть об этом, чтобы избежать путаницы), ctrl+Z (в MS Windows) работает только в начале строки, ctrl+D (для POSIX) должен быть нажмите дважды, если не в начале строки. - person mafso; 05.10.2014
comment
@mafso, спасибо за объяснение! Реально помог понять! +1 - person Spikatrix; 05.10.2014

У вас здесь мало ошибок.

int nc, nw, nl;

Должно быть

int nc = 0, nw = 0, nl = 0;


else if (state = OUT){

Я думаю, ты имел в виду

else if (state == OUT) {

ENTER - это 10, а не 13.

getchar() на cygwin возвращает -1 в конце файла (не знаю почему). Таким образом, вы можете заменить EOF на -1. Вы можете проверить свою программу следующим образом: ./program.exe ‹ inputfilename

person piotrl    schedule 04.10.2014

все еще есть эта проблема? возможно, нашел решение: если вы набираете какие-то символы и нажимаете ctrl-z, то этот ctrl-z считается обычным символом (код 26), но в начале строки (имеется в виду: вы не набираете ничего, кроме ctrl-z char) считается как EOF. так:

while ( (ch=getchar())!=26 ){
    if (ch==EOF) break;
}

таким образом, не имеет значения, где вы набираете символ ctrl-z, итерация заканчивается.

person Narasim    schedule 07.03.2017