我正在尝试计算每个字母出现在文件中的次数。当我运行下面的代码时,它会计算两次“Z”。任何人都可以解释原因吗?
测试数据是:
ABCDEFGHIJKLMNOPQRSTUVWXYZ
ABCDEFGHIJKLMNOPQRSTUVWXYZ
#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);
}
答案 0 :(得分:4)
正如其他人所指出的那样,输入中有两个Q.你有两个Z的原因是最后一个
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)]++;
}
答案 1 :(得分:2)
大写字符串中有两个Q
。我相信你得到两个Z
计数的原因是你应该在阅读完字符后检查EOF
,而不是之前,但我不确定。
答案 2 :(得分:2)
好吧,其他人已经指出了代码中的错误。
但是这是一种优雅的方式,你可以读取文件并计算其中的字母:
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;
}
}
这是此解决方案的修改(未经测试)版本:
答案 3 :(得分:1)
首先,输入中确实有两个Q.
关于Z,@ Jeremiah可能是正确的,因为它是最后一个字符,并且你的代码没有正确检测EOF,因此它被加倍计算。这可以通过例如方便地验证。改变输入字符的顺序。
作为旁注,这里
for (int iteration = 0; iteration <= 127; iteration++)
你的指数超出范围;循环条件应为iteration < 127
,或者您的数组声明为int letterCount[128]
。
答案 4 :(得分:1)
鉴于您显然只想计算英文字母,您似乎应该能够大大简化您的代码:
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的典型机器,则此代码将更容易处理工作 - 但如果您需要更多,则根本不适合。