Console input until . and enter - c++

Hello there I have a little problem with cin. How can I do cin while != '.' and '\n' ? Below is my code
#include <stdio.h>
#include <iostream>
using namespace std;
void reverse(char x[])
{
if(*x=='\0')
{
return;
}
else
{
reverse(x+1);
cout<<*x;
}
}
int main(){
char a[]="";
while (cin.get()!='\n'&&cin.get()!='.') {
cin>>a;
}
reverse(a);
}
Input: foo.
Output: .o
It cut me 2 last words

You shouldn't include both "stdio.h" (or better <cstdio>) and <iostream>. The former lets you use C standard IO functions, while the latter is the standard for C++. Pick one, for example as in your code you are actually using some <iostrem> facilities, include that.
The problem you are facing basically requieres 3 steps:
read standard input untill a '.' or a '\n' is entered.
store each character except '.' or '\n'.
send the character read to standard output in reverse order.
I think that second point is the key point. Where do you store the character you read? In your code you seem to use c-style null terminated char arrays, but you are not allocating the necessary memory for doing that.
Then, when you use cin.get() you are actually removing a character from to input stream and you do that 2 times before reading a.
I also don't think that using recursion for the last step is a good idea. Maybe it's mandatory in your assigned task, but use of recursion like this (and the tipical factorial example) better stay in books. If you want to use a stack to accomplish the task, it's better to do so explicitly, like here:
#include <iostream>
#include <stack>
int main() {
char c;
std::stack<char> sc;
while ( std::cin.get(c) && c!='\n' && c!='.' )
sc.push(c);
std::cout << std::endl;
while ( !sc.empty() ) {
std::cout << sc.top();
sc.pop();
}
std::cout << std::endl;
return 0;
}
A more "natural" way of doing this task is to store the chars in a std::string and show them in reverse order:
#include <iostream>
#include <string>
int main()
{
std::string a;
char c;
while ( std::cin.get(c) && c != '.' && c != '\n' )
a += c;
std::cout << std::endl;
// you can print what you read with: std::cout << a;
for ( std::string::reverse_iterator rit = a.rbegin(); rit != a.rend(); ++rit )
std::cout << *rit;
std::cout << std::endl;
return 0;
}
If you want to use an array of char to store the input, you have to preallocate enough memory for your needs.
#include <iostream>
#define MAX_CHARS 128
int main()
{
char a[MAX_CHARS + 1]; // enough to store 128 char and the '\0'
char c;
int counter = 0; // better knowing how many char you read
for ( counter = 0; counter < MAX_CHARS
&& std::cin.get(c) && c!='.' && c!='\n'; counter++ )
a[counter] = c;
a[counter] = '\0';
std::cout << std::endl;
// you can print what you have read with: std::cout << a;
while( counter > 0 ) {
--counter;
std::cout << a[counter];
}
std::cout << std::endl;
return 0;
}
If you are reading input from terminal you have to enter a whole line followed by newline in any case, because you are reading a buffered input. If you need unbuffered input and to stop reading characters right when a . or enter is pressed then there isn't a standard C++ solution, it depends on your environment. For sake of semplicity you can use the c-style (and deprecated) getch():
#include <cstdio>
#include "conio.h"
#define MAX_CHARS 128
int main()
{
char a[MAX_CHARS + 1]; // enough to store 128 char and the '\0'
char c;
int counter = 0; // better know how many char you read
while ( counter < MAX_CHARS && (c=getch())
&& c!='.' && c!='\n' && c!='\r' ) {
putchar(c);
a[counter] = c;
counter++
}
a[counter] = '\0';
printf("\n");
// you can print what you read with: printf("%s\n",a);
while( counter > 0 ) {
--counter;
putchar(a[counter]);
}
printf("\n");
return 0;
}

You can replace the first get with peek:
while (cin.peek() != '\n' && cin.get() != '.')
std::basic_istream::peek keeps the character in the buffer, so std::basic_istream::get reads the same, but compares it to other.
- wrong. That would keep \n in the buffer, but . not.
Right: Use a char variable into which you extract only once and compare it twice:
char c;
while (cin.get(c) && c != '\n' && c != '.')
...

Doing a get removes the character from the input stream, so the first two characters are removed in the while loop condition before you've even entered the loop. cin >> a; reads in the next word (space separated) and puts it into a. It does not read character by character. This puts "o." into a, as 'f'and 'o' have been removed from the input stream. You then read the newline in the condition check and you exit out of the loop.
Instead, you should call cin.get() once and store the return value as an int ready for using it ie.
int ch = cin.get();
while(ch!='\n' && ch != '.') {
// Do something with ch
//get the next character ready for the next iteration
ch = cin.get();
}
I should also mention that a only has enough space for one character. You should make a bigger, doing something like char a[200]; which creates space for 200 characters. You should also check you don't try to access anything past a[199].

Related

(C++) Why can't I rely on an extration operator to act as an iterator?

#include <iostream>
#include <fstream>
int main()
{
std::ifstream file("input.txt");
char currentChar;
int charCount = 0;
while (file >> currentChar)
{
charCount++;
if (currentChar == 'a')
{
std::cout << charCount;
}
}
in the above, the charCount that's printed is massively large. If I move charcount into the if statement and turn the input into repetitions of the character 'a', it counts correctly (or would count the number of a's correctly). Is "file >> currentChar" what's causing the charCount number to increment so highly? And if so, what's it doing? Why?
It's not "massively large". You're simply outputting the current count every time you encounter the letter a, and because you don't include any whitespace or newlines, then every number will be joined together and appear like a huge number.
Try this:
std::cout << charCount << std::endl;
And consider doing this just once after the loop. Unless for some reason you want to show all intermediate counts.

Program is counting consonants wrong

I'm trying to make a program that counts all the vowels and all the consonants in a text file. However, if the file has a word such as cat it says that there are 3 consonants and 1 vowel when there should be 2 consonants and 1 vowel.
#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;
}
Here you check if the eof state is set, then try to read a char. eof will not be set until you try to read beyond the end of the file, so reading a char fails, but you'll still count that char:
while(!(fileIn.eof())){
ch=fileIn.get(); // this fails and sets eof when you're at eof
So, if your file only contains 3 chars, c, a and t and you've read the t you'll find that eof() is not set. It'll be set when you try reading the next char.
A better way is to check if fileIn is still in a good state after the extraction:
while(fileIn >> ch) {
With that in place the counting should add up. All special characters will be counted as consonants though. To improve on that, you could check that the char is a letter:
#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++;
}
}
}
Your program doesn't check for numbers and special characters, as well as uppercase letters. Plus, the .eof() is misused: it gets to the last character of the file, loops again, reads one more character, and only then it realizes it is at the end of the file, generating the extra consonant problem. Consider using while((ch = inFile.get()) != EOF).
I would use a different approach, searching strings:
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;
}
}
}
Note: in the above code fragment, the character is first tested for an alphabetic characters. Characters may not be alphabetical like period or question mark. Your code counts periods as consonants.
Edit 1: character arrays
If you are not allowed to use std::string, you could also use character arrays (a.k.a. C-Strings):
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;
}
}
}
I first thought my very first comment to your question was just a sidenote, but in fact it's the reason for the results you're getting. Your reading loop
while(!(fileIn.eof())){
ch=fileIn.get();
// process ch
}
is flawed. At the end of the file you'll check for EOF with !fileIn.eof() but you haven't read past the end yet so your program enters the loop once again and fileIn.get() will return EOF which will be counted as a consonant. The correct way to read is
while ((ch = file.get()) != EOF) {
// process ch
}
with ch declared as integer or
while (file >> ch) {
// process ch
}
with ch declared as char. To limit the scope of ch to the loop consider using a for-loop:
for (int ch{ file.get() }; ch != EOF; ch = file.get()) {
// process ch;
}
As #TedLyngmo pointed out in the comments, EOF could be replaced by std::char_traits<char>::eof() for consistency although it is specified to return EOF.
Also your program should handle everything that isn't a letter (numbers, signs, control characters, ...) differently from vowels and consonants. Have a look at the functions in <cctype>.
In addition to Why !.eof() inside a loop condition is always wrong., you have another test or two you must implement to count all vowels and consonants. As mentioned in the comment, you will want to use tolower() (by including cctype) to convert each char to lower before your if statement to ensure you classify both upper and lower-case vowels.
In addition to testing for vowels, you need an else if (isalpha(c)) test. You don't want to classify whitespace or punctuation as consonants.
Additionally, unless you were told to treat 'y' as a vowel, it technically isn't one. I'll leave that up to you.
Adding the tests, you could write a short implementation as:
#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";
}
(also worth reading Why is “using namespace std;” considered bad practice?)
Example Input File
$ cat dat/captnjack.txt
This is a tale
Of Captain Jack Sparrow
A Pirate So Brave
On the Seven Seas.
Example Use/Output
$ ./bin/vowelscons
enter filename: dat/captnjack.txt
In file dat/captnjack.txt there are:
25 vowels, and
34 conansants
Which if you count and classify each character gives the correct result.
Look things over and let me know if you have any questions.
I know that the following will be hard to digest. I want to show it anyway, because it is the "more-modern C++"-solution.
So, I will first think and develop an algorithm, and then use moderen C++ elements to implement it.
First to the algorithm. If we use the ASCII code to encode letters, then we will see the following:
We see that the ASCII code for uppercase and lowercase letters just differ in the lower 5 bits. So, if we mask the ASCII code with 0x1F, so char c{'a'}; unsigned int x{c & 0x1F}, we will get values between 1 and 26. So, we can calculte a 5 bit value for each letter. If we now mark all vowels with a 1, we can build a binary number, consisting of 32bits (an unsigned int) and set a bit at each position, where the vowel is true. We then get something like
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
This numer can be converted to 0x208222. And if we now want to find out, if a letter (regardless whether upper- or lowercase) is a vowel, then we mask out the not necessary bits from the chararcter ( C & 1F ) and shift the binary number to the right as much position, as the resulting letter code has. If then the bit is set at the LSB position, then we have a vowel. This know how is decades old.
Aha. No so easy, but will work for ASCII coded letters.
Next, we create a Lambda, that will read a string that purely consists of alpha letters and counts the vowels. What is not a vowel, that is a consonant (because we have letters only).
Then we use modern C++ elements to calculate the requested values:
The result is some elegant C++ code with only a few lines.
Please see
#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;
}
Please note:
There are one million possible solutions. Everbody can do, what he wants.
Some people are still more in a C-Style mode and others do like more to program in C++

Program prints only first three characters in array in C++

My program is to read characters into an array and display it to the console. But I don't know why it only reads first 3 characters.
#include <iostream>
using namespace std;
int main() {
int length=0,i;
char str[10];
cout<<"Enter a string"<<"\n";
for(i=0; str[i] != '\0'; i++) {
cin>>str[i];
}
for(int i=0; str[i]!='\0'; i++) {
cout<<str[i];
length++;
}
cout<<"\n"<<"Length of the string="<<""<<length<<"\n";
}
The output looks like:
Here are some of the issues I noticed:
You're checking the contents of str before you even initialize it.
You're assuming the string you're fetching from the stream will be null-terminated, but that's actually not the case.
You aren't checking whether cin is working correctly
You don't check to make sure the string is 10 characters or less, this could cause you to overflow past the end of the buffer
When you're checking the length of the array, again you assume the string is null-terminated
If you want to fix these issues and still use a char buffer, see user4581301's comprehensive answer. However, I'd suggest simply switching to std::string. For example:
#include<iostream>
using namespace std;
int main()
{
string str;
cout<<"Enter a string"<<"\n";
if (cin >> str) {
cout << str << endl;
cout << "Length of the string = " << str.length() << endl;
}
}
TL;DR version
std::string str; //use std::string. practically no reasons not to in C++
if (cin >> str) // read into string and test that read succeeded.
{
std::cout << str << '\n'
<< "Length of the string=" << str.length() << endl;
}
Explaining and salvaging Asker's version
for(i=0; str[i] != '\0'; i++){ // str[i] tested here
cin>>str[i]; // but it has no assigned value until here.
uses str[i] before it is assigned a value. The program likely found a null character in the block of memory allocated for str and stopped prematurely, but technically anything can happen if you use an uninitialized variable. For example, you got the expected result 3 times before finding the null. The program could never found a null and run forever. It could have rained unicorns. Anything.
int i = 0;
do {
cin>>str[i];
} while (str[i++] != '\0');
reads then tests. But lines of data in a stream are not C-style strings and are not terminated with null.
int i = 0;
do {
cin>>str[i];
} while (!std::isspace(str[i++]));
exits when whitespace is found, typically signalling the end of a word, rather than null.
But what if cin>>str[i]; failed for some reason?
int i = 0;
do {
cin>>str[i];
} while (cin && !std::isspace(str[i++]));
Adds a test to ensure something was read. But what if there are more than 10 characters and char str[10]; is overflowed?
int i = 0;
do {
cin>>str[i];
} while (cin && !std::isspace(str[i]) && ++i < sizeof(str));
unless I am reading That is legal unless I'm reading [expr.cond] and [expr.log.and] wrong, sequencing of when ++i occurs in !std::isspace(str[i]) && ++i < sizeof(str)) is guaranteed to not affect !std::isspace(str[i])
But what if you run out of space before find the null? str is unterminated and not a string! This ruins the for loop later in the program.
int i = 0;
do {
cin>>str[i];
} while (cin && !std::isspace(str[i]) && ++i < sizeof(str));
if (i == sizeof(str))
{
str[sizeof(str)-1] = '\0';
}
I think that covers everything you're likely to run into here.
You read in character by character and store it in str[i]; but then you increment i++ before checking str[i]!='\0'.
There are two issues with this approach: First, you check a value at a position that has not been written at that point in time. Second, cin>>str[i] will never write the string termination character - it just reads in valid characters, and if input is terminated (e.g. by EOF), nothing is written.
So you are approaching this the wrong way.
If you want to read at most 10 characters up to a new line (i.e. when the user presses enter), use fgets. Or - and this is the preferred option - use cin and write into an std::string-object.
int main()
{
std::string str;
cin >> str;
std::cout << str << std::endl;
}
str[i] != '\0' checks the pre-existing data stored at str[i], not the user-input value.
Add string initialization:
char str[10] = {'\0'};
and change the reading with:
char c;
for(int i = 0; cin>> c && c!=/*add your termination cond. here*/ && i < 10;++i)
str[i] = c;
So you can ensure that the string is filled with correct values and terminated appropriately.
But better solution would be to use std::string. In that case you dont have to check the sizes, because the string grows by itself.For example:
std::string str;
for(char c; cin>>c && c!=/*add your termination cond. here*/;)
str += c;

use of Range Based for loops in C++

I am trying to write a program that eliminates blank spaces using a range based for loop in C++. For eg, if the input is, "what is your name?" , the output should be "Whatisyourname?" however when i run the code below, the output it gives is "Whatisyourname?me?", why is that?
int main()
{
string s = "What is your name?";
int write_index = 0;
for (const char &c : s)
{
if (c != ' ')
{
s[write_index++] = c;
}
}
cout << s << endl;
system("pause");
}
Add after the loop the following statement
s.erase( write_index );
or
s.resize( write_index );
to remove redundant characters from the string.
The general approach to such tasks is the following
#include <algorithm>
#include <string>
//...
s.erase( std::remove( s.begin(), s.end(), ' ' ), s.end() );
The reason for this is because string s is still as long as the original string, "What is your name?". You wrote over top of every character in the string except for the last three. What you could do is erase the last three characters from the string after you're done removing the spaces. This is untested but something like this should work:
s.erase(write_index, s.length() - write_index)
Your range based for loop usage is correct. Just keep in mind that you're looping over all the input characters (as though you were looping with for (int i = 0; i < s.length(); i++), but you're not outputting as many characters as you're reading.
So the equivalent for loop would be like this:
for (int i = 0; i < s.length(); i++) {
const char& c = s[i];
if (c != ' ') {
s[write_index++] = c;
}
}
Here are two useful little functions:
template<class C, class F>
bool remove_erase_if( C& c, F&& f ) {
using std::begin; using std::end;
auto it = std::remove_if( begin(c), end(c), std::forward<F>(f) );
if ( it == c.end())
return false;
c.erase( it, c.end() );
return true;
}
template<class C, class T>
bool remove_erase( C& c, T&& t ) {
using std::begin; using std::end;
auto it = std::remove( begin(c), end(c), std::forward<T>(t) );
if ( it == c.end())
return false;
c.erase( it, c.end() );
return true;
}
these both take a container, and either a test or an element.
They then remove and erase any elements that pass the test, or equal the element.
Your code emulated the remove part of the above code, and did not do the erase part. So the characters remaining at the end ... remained.
remove (or your code) simply moves all the "kept" data to the front of the container. The stuff left over at the end ... stays there. The erase step then tells the container that the stuff after the stuff you kept should be discarded. If you don't discard it, it ... remains ... and you get your bug.
With the above two functions, you can do this:
int main() {
std::string s = "What is your name?";
remove_erase( s, ' ' );
std::cout << s << '\n';
}
and you are done.
As an aside, using namespace std; is often a bad idea. And std::endl forces a buffer-flush, so I prefer '\n'. Finally, system("pause") can be emulated by running your IDE in a mode that leaves you your command window open, instead of adding it to your code Ctrl-F5.
You can keep track of the number of spaces you have and resize the string at the end.
int main()
{
string s = "What is your name?";
int length = s.length();
int write_index = 0;
for (const char &c : s)
{
if (c != ' ')
{
s[write_index++] = c;
}
else
{
length -= 1;
}
}
s.resize(length);
cout << s << endl;
}
Try this:
#include <string.h>
#include <iostream>
using namespace std;
int main()
{
string s = "What is your name?";
std::string aux(s.size(),' ');
int write_index = 0;
for (const char &c : s)
{
if (c != ' ')
{
aux[write_index++] = c;
}
}
cout << s << endl;
cout << aux << endl;
system("pause");
}
Now, I don't personally code C++, but this looks eerily similar to a for-each loop in C#, Java, and JavaScript; so I'll give it a go.
Let's first break down your code to see what's going on
int main() {
// creates a string object which is essentially a glorified array of chars
string s = "What is your name?";
int write_index = 0;
// for evry char "c" in the char-array "s"
for (const char &c : s) {
// if c isn't a space
if (c != ' ') {
// write c to s at index "write_index" then increment "write_index"
s[write_index++] = c;
}
}
std::cout << s << std::endl;
system("pause");
}
The logic seems good, so why does "what is your name?" turn into "whatisyourname?me?"? Simple. Because you're overwriting the existing array.
"what is your name?" is 18 characters long, and since you're only writing a non-space character to the array if it's not a space you're essentially copying characters one space left for every space in your text.
For example here's what happens after you run this code over the first 7 characters: "whatiss your name?", and after the first 12: "whatisyourur name?", and finally after all 18: "whatisyourname?me?". The length of the string never really changes.
So you got a number of options to solve this issue:
Build a new string from the old one with a string-builder (if such a thing exists in C++) and return the freshly created string.
Count the number of spaces you encounter and return a substring that is that many characters shorter (original is 18 chars - 3 spaces = new is 15 chars).
Reduce the length of the string by the required amount of characters (Thanks Yakk for this one)
This is a basic application of the copy_if algorithm from the standard library.
#include <algorithm>
#include <cctype>
#include <iostream>
#include <iterator>
#include <string>
int main()
{
std::string s = "What is your name?";
std::copy_if(s.begin(), s.end(), std::ostream_iterator<char>(std::cout),
[](char c){ return !std::isspace(c); });
return 0;
}
outputs:
Whatisyourname?
If you actually need to remove them from the original string, then use the algorithm remove_if followed by erase.

Counting occurrences of letter in a file

I'm trying to count the number of times each letter appears in a file. When I run the code below it counts "Z" twice. Can anyone explain why?
The test data is:
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);
}
As others have pointed out, you have two Qs in the input. The reason you have two Zs is that the last
inputFile >> character;
(probably when there's just a newline character left in the stream, hence not EOF) fails to convert anything, leaving a 'Z' in the global 'character' from the previous iteration. Try inspecting inputFile.fail() afterwards to see this:
while (inputFile.peek() != EOF)
{
inputFile >> character;
if (!inputFile.fail())
{
letterCount[static_cast<int>(character)]++;
}
}
The idiomatic way to write the loop, and which also fixes your 'Z' problem, is:
while (inputFile >> character)
{
letterCount[static_cast<int>(character)]++;
}
There are two Q's in your uppercase string. I believe the reason you get two counts for Z is that you should check for EOF after reading the character, not before, but I am not sure about that.
Well, others already have pointed out the error in your code.
But here is one elegant way you can read the file and count the letters in it:
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;
}
}
This is modified (untested) version of this solution:
Elegant ways to count the frequency of words in a file
For one thing, you do have two Q's in the input.
Regarding Z, #Jeremiah is probably right in that it is doubly counted due to it being the last character, and your code not detecting EOF properly. This can be easily verified by e.g. changing the order of input characters.
As a side note, here
for (int iteration = 0; iteration <= 127; iteration++)
your index goes out of bounds; either the loop condition should be iteration < 127, or your array declared as int letterCount[128].
Given that you apparently only want to count English letters, it seems like you should be able to simplify your code considerably:
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;
}
Of course, there are quite a few more possibilities. Compared to #Nawaz's code (for example), this is obviously quite a bit shorter and simpler -- but it's also more limited (e.g., as it stands, it only works with un-accented English characters). It's pretty much restricted to the basic ASCII letters -- EBCDIC encoding, ISO 8859-x, or Unicode will break it completely.
His also makes it easy to apply the "letters only" filtration to any file. Choosing between them depends on whether you want/need/can use that flexibility or not. If you only care about the letters mentioned in the question, and only on typical machines that use some superset of ASCII, this code will handle the job more easily -- but if you need more than that, it's not suitable at all.