Что не так с этой программой для сканирования файла и возврата количества строк / абзацев / слов?

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

Проблема в том, что сканирование не прекращается никогда. Он никогда не выходит из цикла while (nextChar! = '\ N'). Таким образом, функции processBlank и copyText никогда не перестают работать. Он никогда не попадает в EOF.

Я не знаю, что делать дальше. Я не могу найти решение, поэтому любая помощь приветствуется :)

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

void initialize(int *p1,int *p2,int *p3,int *p4)
{
    *p1=0;
    *p2=0;
    *p3=0;
    *p4=0;
}

void processBlank(char *nextChar,int *wordsinLine,FILE *ctPtr)
{
    while (*nextChar==' ')
    {
        printf("%c",*nextChar);
        *nextChar=fgetc(ctPtr);
    }
    *wordsinLine+=1;
}

void copyText(char *nextChar,FILE *ctPtr)
{
    while (*nextChar!=' ')
    {
        printf("%c",*nextChar);
        *nextChar=fgetc(ctPtr);
    }
}

void updateCount(int *numWords,int *wordsinLine,int *numParagraphs,int *numLines)
{
    *numWords+=*wordsinLine;
    if (*wordsinLine==0)
        *numParagraphs+=1;
    *wordsinLine=0;
    *numLines+=1;
}

void printTotal(int numWords,int numLines,int numParagraphs)
{
    printf("\n\n\n\nTotal number of words is: %d\n\n",numWords);
    printf("Total number of lines is: %d\n\n",numLines);
    printf("Total number of paragraphs is: %d\n\n\n\n",numParagraphs);
}

void main()
{
    int numWords,numLines,numParagraphs,wordsinLine;
    initialize(&numWords,&numLines,&numParagraphs,&wordsinLine);
    FILE *ctPtr;
    char nextChar;
    if ((ctPtr=fopen("Q2read.txt", "r"))==NULL)
        printf("File could not be opened\n");
    else
    {
        nextChar=fgetc(ctPtr);
        while (nextChar!=feof(ctPtr))
        {

            while (nextChar!='\n')
            {
                processBlank(&nextChar,&wordsinLine,ctPtr);
                copyText(&nextChar,ctPtr);
            }
            updateCount(&numWords,&wordsinLine,&numParagraphs,&numLines);
        }
        printTotal(numWords,numLines,numParagraphs);
        fclose(ctPtr);
    }
}

person FOIWATER    schedule 10.11.2014    source источник


Ответы (3)


  1. Основные: Измените nextChar!=feof(ctPtr) на nextChar != EOF.
    feof() верните "ненулевое значение, если и только если установлен индикатор конца файла"
    EOF - некоторое отрицательное число.
    2 разные вещи - не очень сопоставимы.

  2. Незначительное: изменить void main() на int main(void)

  3. Незначительное изменение char nextChar на int nextChar (также связанные с вызовами функций.)

  4. Major: copyText(): изменить на while (*nextChar != ' ' && *nextChar != EOF) {

  5. В main() измените на while (nextChar != '\n' && nextChar != EOF) { Добавить тест EOF.

person chux - Reinstate Monica    schedule 10.11.2014
comment
Спасибо за ответ. Изменение этих параметров не устраняет ошибку, хотя я их изменил. Спасибо! - person FOIWATER; 11.11.2014
comment
Ну, теперь он действительно выходит из цикла while! И это дает мне результат, единственная проблема в том, что он не дает правильного количества строк, абзацев и слов. Но я буду продолжать попытки. Спасибо за помощь, Chux. Он сообщает мне, что во всем документе есть только одна строка (на самом деле их 100). Поэтому я считаю, что он использует условие EOF для выхода из цикла while, а на самом деле не использует условие! = '\ N' вообще - person FOIWATER; 11.11.2014
comment
Я вижу проблему. Если я добавлю оператор if в copyText () или processBlank () с надписью printf ЗДЕСЬ !!!!! когда nextChar равен \ n, я вижу это сообщение в каждом пробеле. Это говорит мне, что даже если у меня есть условие не вводить эти функции, когда nextChar = \ n, он все еще попадает в эти циклы и увеличивается после \ n, поэтому цикл while в main никогда его не видит. Как я могу прервать функцию и вернуться в основную во время цикла while внутри copyText (), если nextChar = \ n? - person FOIWATER; 11.11.2014
comment
Идеи: 1) Определенному коду не нужен двойной цикл while в main() 2) В processBlank() и copyText(), подумайте, что вам нужно что-то вроде while (isblank(*nextChar)). - person chux - Reinstate Monica; 11.11.2014
comment
Пункт 3 - мэрский: если nextChar относится к типу char, это просто не может быть EOF. - person cmaster - reinstate monica; 12.11.2014
comment
@cmaster Согласитесь, № 3 важен. Многие (большинство?) char подписаны, а EOF часто -1, поэтому такой char может иметь то же значение, что и EOF. Из-за этого, к сожалению, EOF видимо работает. Конечно, это проблема, поскольку fgetc() возвращает unsigned char, а EOF и char не всегда подписан. Таким образом, char не может отчетливо представлять все char и EOF. - person chux - Reinstate Monica; 12.11.2014

Вместо того, чтобы читать ваш файл побайтно (НЕВЕРОЯТНО МЕДЛЕННО), найдите функцию fgets(). Это даст вам целую СТРОКУ текста, а затем вы сможете обработать его оттуда. Он ДЕЙСТВИТЕЛЬНО принимает максимальную длину строки, поэтому, если вы используете что-то разумное, например 1024, ВСЕГДА убедитесь, что вы проверяете свою строку на новую строку, если она НЕ находится в строке, вам нужно прочитать СНОВА и добавить, но прежде чем вы это сделаете проверьте, действительно ли вы находитесь в EOF, так как многие файлы просто могут НЕ ИМЕТЬ того окончательного \n, \r\n или \r, или любого другого безумия, которое использует ОС, которую вы используете ...

person LarryF    schedule 10.11.2014
comment
Что касается разницы в производительности fgets() и fgetc(), какие тесты поддерживают НЕВЕРОЯТНО МЕДЛЕННО? - person chux - Reinstate Monica; 11.11.2014
comment
У меня нет удобных тестов, но представьте себе это. Допустим, вы читаете очень большой файл, 1 мегабайт, со средней длиной строки 1024 символа. Это означает, что вы собираетесь вызывать эту функцию 1024 раза для КАЖДОЙ СТРОКИ в файле (в среднем), что само по себе не кажется большим делом, потому что большая часть файла ВЕРОЯТНО будет загружена в файловый кеш в любом случае. Однако сам вызов функции в C требует времени. Вещи должны быть помещены в стек, извлечены, затем выполнены, а затем возвращены. fgets () делает все это за ОДИН вызов функции. Увидеть разницу? - person LarryF; 11.11.2014
comment
@chux Дайте накладным расходам на вызов функции десять наносекунд (что вполне разумно), и у вас будут накладные расходы в десять секунд на чтение гигабайта байт за байтом. Это не большая проблема, но не стоит привыкать к этому. - person cmaster - reinstate monica; 12.11.2014
comment
Есть еще лучшая функция: getline(). Являясь частью стандарта POSIX-2008, он действительно получает всю строку, выделяя для нее достаточно места. Это позволяет избежать многих проблем с ограничениями буфера. - person cmaster - reinstate monica; 12.11.2014
comment
@cmaster Я видел реализации fgets(), который сам был серией вызовов fgetc(), поэтому сравнение производительности 1 fgets() и 1024 fgetc() с теоретической точки зрения может дать широкий диапазон ответов относительно того, как быстрее. Преждевременная оптимизация - плохая привычка. Порядок величины одинаков для любого подхода. Поскольку LarryF сказал НЕВЕРОЯТНО МЕДЛЕННО, я надеялся увидеть некоторые тестовые данные, чтобы количественно оценить степень разницы в производительности. - person chux - Reinstate Monica; 12.11.2014
comment
@chux Что ж, если fgets() реализован с использованием fgetc(), эта реализация либо полагается на встраивание компилятора, либо это посредственная реализация fgets(). Правда, во многих случаях люди не заметят разницы, но ее следует избегать при качественной реализации стандартной библиотеки C. Конечно, многие такие реализации неоптимальны (если не все), но это не должно быть оправданием для написания кода приложения, который явно неоптимален. После того, как вы написали эти fgetc() призывы, заменить их будет труднее, чем вообще их избежать. - person cmaster - reinstate monica; 12.11.2014

fgetc() возвращает int, а EOF обычно -1. Объявите nextChar как int и сравните его с EOF (не feof(); feof() - это функция, которая проверяет поток на eof)

Вместо этого вы можете проверить feof(stream) != 0 (скажем, с if (feof(stream))), чтобы узнать, находитесь ли вы в EOF, но я думаю, что лучше просто проверить возвращаемое значение fgetc().

person ComputerDruid    schedule 10.11.2014
comment
Спасибо за оперативный ответ! Я попробовал ваше предложение, и код работает точно так же, как и я его опубликовал. Я предполагаю, что он увидел бы конец файла, если бы только мог выйти из вложенного цикла while. похоже, что nextChar! = '\ n' никогда не является ложным. Я также попытался добавить while (nextChar! = 10) (код ascii для новой строки) также не выходит из цикла - person FOIWATER; 11.11.2014
comment
Не согласен с проверкой feof (stream) ›0. Функция feof возвращает ненулевое значение в конце файла, не обязательно положительное число. Предложите вместо if (feof(stream)). - person chux - Reinstate Monica; 11.11.2014
comment
@chux Я согласен и обновлю свой ответ. Я пытался избежать совпадения -1 (моя страница руководства для feof, похоже, думает, что это может указывать на ошибку), но игнорирование ошибки и продолжение не имеет большого смысла - person ComputerDruid; 11.11.2014