Программа считает неправильные согласные - PullRequest
2 голосов
/ 07 марта 2020

Я пытаюсь создать программу, которая считает все гласные и все согласные в текстовом файле. Однако, если в файле есть такое слово, как cat, оно говорит, что есть 3 согласных и 1 гласный, когда должно быть 2 согласных и 1 гласный.


#include <string>
#include <cassert>
#include <cstdio>

using namespace std;

int main(void)
{
    int i, j;
    string inputFileName;

    ifstream fileIn;
    char ch;
    cout<<"Enter the name of the file of characters: ";
    cin>>inputFileName;
    fileIn.open(inputFileName.data());
    assert(fileIn.is_open());
    i=0;
    j=0;



    while(!(fileIn.eof())){
        ch=fileIn.get();

        if (ch == 'a'||ch == 'e'||ch == 'i'||ch == 'o'||ch == 'u'||ch == 'y'){
            i++;
        }

        else{
            j++;
        }
    }

    cout<<"The number of Consonants is: " << j << endl;
    cout<<"The number of Vowels is: " << i << endl;

    return 0;
}

Ответы [ 6 ]

1 голос
/ 07 марта 2020

Здесь вы проверяете, установлено ли состояние eof, затем попробуйте прочитать char. eof не будет установлен, пока вы не попытаетесь прочитать за пределами файла, поэтому чтение char не удастся, но вы все равно будете считать, что char:

while(!(fileIn.eof())){
        ch=fileIn.get();   // this fails and sets eof when you're at eof

Итак, если Ваш файл содержит только 3 chars, c, a и t, и вы прочитали t, вы обнаружите, что eof() не установлено. Он будет установлен при попытке прочитать следующее char.

Лучший способ - проверить, находится ли fileIn в хорошем состоянии после извлечения:

while(fileIn >> ch) {

С этим на месте счет должен сложиться. Все специальные символы будут считаться согласными. Чтобы улучшить это, вы можете проверить, что char это буква:

#include <cctype>

// ...

    while(fileIn >> ch) {
        if(std::isalpha(ch)) {        // only count letters
            ch = std::tolower(ch);    // makes it possible to count uppercase letters too
            if(ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u' || ch == 'y') {
                i++;
            } else {
                j++;
            }
        }
    }
1 голос
/ 07 марта 2020

Ваша программа не проверяет числа и специальные символы, а также заглавные буквы. Кроме того, .eof() используется не по назначению: он возвращается к последнему символу файла, повторяется снова , читает еще один символ, и только тогда он понимает, что находится в конце файла, генерируя дополнительный согласная проблема. Попробуйте использовать while((ch = inFile.get()) != EOF).

0 голосов
/ 07 марта 2020

Я знаю, что следующее будет трудно переварить. В любом случае, я хочу показать его, потому что это «более современное решение C ++».

Итак, сначала я подумаю и разработаю алгоритм, а затем использую элементы moderen C ++ для его реализации.

Сначала к алгоритму. Если мы используем код ASCII для кодирования букв, то мы увидим следующее:

Ascci Code for Letters

Мы увидим, что код ASCII для заглавных и строчных букв просто отличаются младшими 5 битами. Итак, если мы замаскируем код ASCII с 0x1F, то есть char c{'a'}; unsigned int x{c & 0x1F}, мы получим значения от 1 до 26. Таким образом, мы можем вычислить 5-битное значение для каждой буквы. Если мы теперь помечаем все гласные с 1, мы можем построить двоичное число, состоящее из 32 бит (без знака int) и установить бит в каждой позиции, где гласная истинна. Затем мы получаем что-то вроде

Bit position
3322 2222 2222 1111 1111 1100 0000 0000  
1098 7654 3210 9876 5432 1098 7654 3210  
Position with vowels:
0000 0000 0010 0000 1000 0010 0010 0010

Это число может быть преобразовано в 0x208222. И если теперь мы хотим выяснить, является ли буква (независимо от того, является ли она заглавной или прописной буквой) гласной, тогда мы замаскируем ненужные биты из символа (C & 1F) и сместим двоичное число вправо. столько места, сколько получен буквенный код. Если тогда бит установлен в позиции LSB, то у нас есть гласный. Это ноу-хау уже десятки лет.

Ага. Это не так просто, но будет работать для букв в кодировке ASCII.

Далее мы создадим лямбду, которая будет читать строку, состоящую исключительно из букв алфавита и считающую гласные. Что не является гласным, это согласный (потому что у нас есть только буквы).

Затем мы используем современные элементы C ++ для вычисления запрошенных значений:

В результате получается некоторый элегантный код C ++ с только несколько строк.

Пожалуйста, смотрите

#include <utility>
#include <algorithm>
#include <string>
#include <iostream>
#include <fstream>
#include <cctype>

int main() {

    // Lambda for counting vowels and consonants in a string consisting of letters only
    auto countVowelsAndConsonants = [](std::string& s) -> std::pair<size_t, size_t> {
        size_t numberOfVowels = std::count_if(s.begin(), s.end(), [](const char c) { return (0x208222 >> (c & 0x1f)) & 1; });
        return { numberOfVowels, s.size() - numberOfVowels }; };

    // Inform the user what to do: He should enter a valid filename
    std::cout << "\nCount vowels and consonants.\n\nEnter a valid filename with the source text:  ";

    // Read the filename
    if (std::string fileName{}; std::cin >> fileName) {

        // Now open the file and check, if it could be opened
        if (std::ifstream sourceFileStream(fileName); sourceFileStream) {

            // Read the complete source text file into a string. But only letters
            std::string completeSourceTextFile{};
            std::copy_if(std::istreambuf_iterator<char>(sourceFileStream), {}, std::back_inserter(completeSourceTextFile), std::isalpha);

            // Now count the corresponding vowels and consonants
            const auto [numberOfVowels, numberOfConsonants] = countVowelsAndConsonants(completeSourceTextFile);

            // Show result to user:
            std::cout << "\n\nNumber of vowels:     " << numberOfVowels << "\nNumber of consonants: " << numberOfConsonants << "\n\n";
        }
        else {
            std::cerr << "\n*** Error. Could not open source text file '" << fileName << "'\n\n";
        }
    }
    else {
        std::cerr << "\n*** Error. Could not get file name for source text file\n\n";
    }
    return 0;
}

Обратите внимание:

Существует миллион возможных решений. Каждый может делать то, что он хочет.

Некоторые люди все еще находятся в режиме C -Style, а другие любят больше программировать на C ++

0 голосов
/ 07 марта 2020

В дополнение к Why! .Eof () внутри условия al oop всегда неверно. , у вас есть еще один или два теста, которые вы должны выполнить для подсчета всех гласных и согласных. Как упомянуто в комментарии, вы захотите использовать tolower() (включив cctype), чтобы преобразовать каждый символ в нижний перед вашим оператором if, чтобы гарантировать, что вы классифицируете как верхний, так и нижний регистр гласных.

В дополнение к тестированию на гласные, вам необходимо пройти тест else if (isalpha(c)). Вы не хотите классифицировать пробелы или знаки препинания как согласные.

Кроме того, если вам не сказали трактовать 'y' как гласную, технически это не так ' т один. Я оставлю это на ваше усмотрение.

Добавив тесты, вы можете написать короткую реализацию в виде:

#include <iostream>
#include <fstream>
#include <string>
#include <cctype>

int main (void) {

    size_t cons = 0, vowels = 0;
    std::string ifname {};
    std::ifstream fin;

    std::cout << "enter filename: ";
    if (!(std::cin >> ifname)) {
        std::cerr << "(user canceled input)\n";
        exit (EXIT_FAILURE);
    }

    fin.open (ifname);
    if (!fin.is_open()) {
        std::cerr << "error: file open failed '" << ifname << "'\n";
        exit (EXIT_FAILURE);
    }

    /* loop reading each character in file */
    for (int c = fin.get(); !fin.eof(); c = fin.get()) {
        c = tolower(c);         /* convert to lower */
        if (c=='a' || c=='e' || c=='i' || c=='o' || c=='u')
            vowels++;
        else if (isalpha(c))    /* must be alpha to be consonant */
            cons++;
    }
    std::cout << "\nIn file " << ifname << " there are:\n  " << vowels 
                << " vowels, and\n  " << cons << " conansants\n";
}

(также стоит прочитать Почему «используется пространство имен std; ”Считается плохой практикой? )

Пример входного файла

$ cat dat/captnjack.txt
This is a tale
Of Captain Jack Sparrow
A Pirate So Brave
On the Seven Seas.

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

$ ./bin/vowelscons
enter filename: dat/captnjack.txt

In file dat/captnjack.txt there are:
  25 vowels, and
  34 conansants

Что, если вы подсчитаете и классифицируете каждый символ, даст правильный результат.

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

0 голосов
/ 07 марта 2020

Я бы использовал другой подход, ища строки:

const std::string vowels = "aeiou";
int vowel_quantity = 0;
int consonant_quantity = 0;
char c;
while (file >> c)
{
    if (isalpha(c))
    {
        if (vowels.find(c) != std::string::npos)
        {
            ++vowel_quantity;
        }
        else
        {
            ++consonant_quantity;
        }
    }
}

Примечание: во фрагменте кода выше, символ сначала проверяется на символы алфавита c. Символы не могут быть алфавитными, как точка или знак вопроса. Ваш код считает периоды как согласные.

Редактировать 1: символьные массивы
Если вам не разрешено использовать std::string, вы также можете использовать символьные массивы (также известные как C - Строки):

static const char vowels[] = "aeiou";  
int vowel_quantity = 0;
int consonant_quantity = 0;
char c;
while (file >> c)
{
    if (isalpha(c))
    {
        if (strchr(vowels, c) != NULL)
        {
            ++vowel_quantity;
        }
        else
        {
            ++consonant_quantity;
        }
    }
}
0 голосов
/ 07 марта 2020

Сначала я подумал, что мой самый первый комментарий к вашему вопросу был просто знаком, но на самом деле это причина того, что вы получаете результаты. Ваше чтение l oop

while(!(fileIn.eof())){
    ch=fileIn.get();
    // process ch
}

неверно. В конце файла вы будете проверять EOF с помощью !fileIn.eof(), но вы еще не прочитали до конца, поэтому ваша программа снова вводит l oop и fileIn.get() вернет EOF, что считаться согласной. Правильный способ чтения -

while ((ch = file.get()) != EOF) {
    // process ch
}

с ch, объявленным как int eger или

while (file >> ch) {
    // process ch 
}

с ch, объявленным как char. Чтобы ограничить область действия ch значением l oop, рассмотрите возможность использования for -l oop:

for (int ch{ file.get() }; ch != EOF; ch = file.get()) {
    // process ch;
}

Как @ TedLyngmo , указанное в комментарии EOF могут быть заменены на std::char_traits<char>::eof() для согласованности, хотя указывается, что они возвращают EOF.


Также ваша программа должна обрабатывать все, что не является буквой (цифры, знаки , управляйте символами, ...) в отличие от гласных и согласных. Посмотрите на функции в <cctype>.

...