Подсчет вхождений буквы в файл - PullRequest
3 голосов
/ 05 марта 2011

Я пытаюсь подсчитать, сколько раз каждая буква появляется в файле. Когда я запускаю приведенный ниже код, он считается «Z» дважды. Кто-нибудь может объяснить, почему?

Данные испытаний:

АБВГДЕЖЗИКЛМНОПРСТУФХЧШЭЮЯ

АБВГДЕЖЗИКЛМНОПРСТУФХЧШЭЮЯ

#include <iostream>                 //Required if your program does any I/O
#include <iomanip>                  //Required for output formatting
#include <fstream>                  //Required for file I/O
#include <string>                   //Required if your program uses C++ strings
#include <cmath>                    //Required for complex math functions
#include <cctype>                   //Required for letter case conversion

using namespace std;                //Required for ANSI C++ 1998 standard.

int main ()
{
string reply;
string inputFileName;
ifstream inputFile;
char character;
int letterCount[127] = {};

cout << "Input file name: ";
getline(cin, inputFileName);

// Open the input file.
inputFile.open(inputFileName.c_str());      // Need .c_str() to convert a C++ string to a C-style string
// Check the file opened successfully.
if ( ! inputFile.is_open())
{
    cout << "Unable to open input file." << endl;
    cout << "Press enter to continue...";
    getline(cin, reply);
    exit(1);
}

while ( inputFile.peek() != EOF )
{
      inputFile >> character;
      //toupper(character);

      letterCount[static_cast<int>(character)]++;
}

for (int iteration = 0; iteration <= 127; iteration++)
{
    if ( letterCount[iteration] > 0 )
    {
         cout << static_cast<char>(iteration) << " " << letterCount[iteration] << endl;
    }
}

system("pause");
exit(0);
}

Ответы [ 5 ]

4 голосов
/ 05 марта 2011

Как уже отмечали другие, у вас есть два Q на входе. Причина, по которой у вас два Zs, заключается в том, что последний

inputFile >> character;

(возможно, когда в потоке остался только символ новой строки, следовательно, не EOF) не удается что-либо преобразовать, оставляя «Z» в глобальном «символе» из предыдущей итерации. Попробуйте проверить inputFile.fail (), чтобы увидеть это:

while (inputFile.peek() != EOF)
{
    inputFile >> character;

    if (!inputFile.fail())
    {
        letterCount[static_cast<int>(character)]++;
    }
}

Идиоматический способ написания цикла, который также исправляет вашу проблему 'Z', таков:

while (inputFile >> character)
{
      letterCount[static_cast<int>(character)]++;
}
2 голосов
/ 05 марта 2011

Ну, другие уже указали на ошибку в вашем коде.

Но вот один элегантный способ, которым вы можете прочитать файл и посчитать в нем буквы:

 struct letter_only: std::ctype<char> 
 {
    letter_only(): std::ctype<char>(get_table()) {}

    static std::ctype_base::mask const* get_table()
    {
       static std::vector<std::ctype_base::mask> 
             rc(std::ctype<char>::table_size,std::ctype_base::space);

       std::fill(&rc['A'], &rc['z'+1], std::ctype_base::alpha);
       return &rc[0];
    }
 };

struct Counter
{
    std::map<char, int> letterCount;
    void operator()(char  item) 
    { 
       if ( item != std::ctype_base::space)
         ++letterCount[tolower(item)]; //remove tolower if you want case-sensitive solution!
    }
    operator std::map<char, int>() { return letterCount ; }
};

int main()
{
     ifstream input;
     input.imbue(std::locale(std::locale(), new letter_only())); //enable reading only leters only!
     input.open("filename.txt");
     istream_iterator<char> start(input);
     istream_iterator<char> end;
     std::map<char, int> letterCount = std::for_each(start, end, Counter());
     for (std::map<char, int>::iterator it = letterCount.begin(); it != letterCount.end(); ++it)
     {
          cout << it->first <<" : "<< it->second << endl;
     }
 }

Этоявляется модифицированной (непроверенной) версией этого решения:

Элегантные способы подсчета частоты слов в файле

2 голосов
/ 05 марта 2011

В вашей заглавной строке есть два Q.Я полагаю, что причина, по которой вы получаете два счета за Z, заключается в том, что вы должны проверить EOF после прочтения символа, но не раньше, но я не уверен в этом.

1 голос
/ 05 марта 2011

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

int main(int argc, char **argv) { 
   std::ifstream infile(argv[1]);

    char ch;
    static int counts[26];

    while (infile >> ch)
       if (isalpha(ch))
           ++counts[tolower(ch)-'a'];

    for (int i=0; i<26; i++)
        std::cout << 'A'  + i << ": " << counts[i] <<"\n";
    return 0;
}

Конечно, есть еще несколько возможностей. По сравнению с кодом @ Nawaz (например), это, очевидно, немного короче и проще - но он также более ограничен (например, в нынешнем виде он только работает с английскими символами без акцента). Он в значительной степени ограничен основными буквами ASCII - кодировка EBCDIC, ISO 8859-x или Unicode полностью его сломает.

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

1 голос
/ 05 марта 2011

Во-первых, у вас есть два Q на входе.

Относительно Z, @Jeremiah, вероятно, прав в том, что он вдвойне засчитан, потому что это последний символ, а ваш код не обнаруживает EOFдолжным образом.Это можно легко проверить, например, изменив порядок ввода символов.

В качестве примечания, здесь

for (int iteration = 0; iteration <= 127; iteration++)

ваш индекс выходит за границы;либо условие цикла должно быть iteration < 127, либо ваш массив объявлен как int letterCount[128].

...