The purpose of the program is to read a phrase from a file into a vector and convert the phrase into Pig Latin. When the translated phrase is outputted in Pig Latin, an additional "ay" is added after the phrase (which is not supposed to happen). Can anyone spot why this is happening? It is important that I fix this because it affects the total letters and total characters of the Pig Latin phrase that I need to output. Also, I'm not asking anyone to write any code for me, but any tips on how to make my code less redundant. A portion of my grade for programs is efficiency, which I usually lose points on.
Here's the code:
#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
#include <sstream>
#include <cctype>
#include <stdio.h>
#include <ctype.h>
using namespace std;
int main()
{
ifstream in;
string word, fileName;
vector <string> phrase;
int length = 0, index = 0;
int totalWords = -1, totalLetters = -3, totalChars;
cout << "PIG LATIN PROGRAM" << endl;
cout << "Which file are you accessing? : ";
cin >> fileName;
fileName += ".txt";
in.open(fileName);
if (in.fail()) cout << "\nFile not found!" << endl;
while(getline(in, word)) phrase.push_back(word);
cout << "Original Phrase: " << phrase[0] << endl;
istringstream iss(phrase[0]);
cout << "Pig Latin phrase: ";
do {
string OGword;
string PLword;
for (int i=0; i < phrase.size(); i++){
iss >> OGword;
totalWords++;
}
if (OGword[0]=='a' || OGword[0]=='A' || OGword[0]=='e' || OGword[0]=='E' || OGword[0]=='i' || OGword[0]=='I' || OGword[0]=='o' || OGword[0]=='O' || OGword[0]=='u' || OGword[0]=='U'){
cout << OGword << "way" << " ";
totalLetters += (OGword.size() + 3);
}
else {
PLword = OGword.substr(index);
length = PLword.length();
PLword.insert(length, "ay");
PLword.insert(length, 1, OGword[index]);
PLword.erase(0, 1);
if (isupper(OGword[0])){
transform(PLword.begin(), PLword.end(), PLword.begin(), ::tolower);
(toupper(PLword[1]));
char upper;
upper = toupper(PLword[0]);
PLword.erase(0, 1);
cout << upper;
}
cout << PLword << " ";
totalLetters += PLword.size();
}
} while (iss);
totalChars = totalLetters + 1;
cout << "\n\nTotal words: " << totalWords << endl;
cout << "Total Letters: " << totalLetters << endl;
cout << "Total Characters: "<< totalChars << endl;
}
Problem
The core loop of the program looks like this (in pseudocode):
istringstream iss; // Contains line of text.
do {
string OGword;
get_OGword_and_count_totalWords(iss, OGword);
print_pig_latin_of_word(OGword);
} while (iss);
The loop runs as long as iss has not experienced an error. And in particular, iss does not experience an error until an extraction operation fails. So things happen in the loop like this:
OGword contains the last legitimate word on the line.
Print the last word.
The while clause is tested. iss is still good at this point because no error has occurred, even if iss is at the end of string.
Attempt to extract a word into OGword. This fails, and leaves OGword empty ("").
Print the Pig Latin version of "", which is "ay".
The while clause is tested. iss is in an error state, and the loop ends.
Fix
One possible fix out of many is to test iss for an error immediately after extracting a word.
std::istringstream iss; // Contains line of text
std::string OGword;
while (iss >> OGword) {
increment_word_total();
print_pig_latin_of_word(OGword);
}
In this version, the operation iss >> OGword returns iss, which is converted to bool. If there was an error during the immediately preceeding extraction, the loop ends without printing anything.
Other Advice
I think the best way to improve readability is to break the code up into smaller functions. For instance, take the if / else block that formats and prints the Pig Latin, and actually put it in a function:
int print_pig_latin_of_word_and_return_total_letters(string_view word);
Then, the code in that function can be further subdivided:
bool starts_with_vowel(std::string_view word);
int print_vowel_word_and_count_letters(std::string_view word);
int print_consonant_word_and_count_letters(std::string_view word);
int print_pig_latin_of_word_and_count_letters(std::string_view word) {
if (starts_with_vowel(word)) {
return print_vowel_word_and_count_letters(word);
} else {
return print_consonant_word_and_count_letters(word);
}
}
Odds and Ends
I would drop using namespace std and write all of the std library names as std::string, etc. This makes it clear which things are from the standard library.
The program has interesting behavior on input files that contain more than one line. There is a for loop that loops over phrase.size() which is number of input lines. This causes words to be skipped and totalWords to be incorrect.
This statement doesn't do anything, because the result of toupper is ignored:
(toupper(PLword[1]));
Related
Write and test a program that prompts the user to input a file name and a string to test for. Search the file for every occurrence of the specified string – when the string is found, display the line that contains it. When all occurrences of the string have been found display the number of times the string appeared in the file. Hint: You may use string member function find().
Here's my code, as I stated it works with some sentences but not all, it doesn't seem to have anything to do with the length either as if I repeat a line I know works, it won't run into any errors, can someone explain?
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <cctype>
using namespace std;
void find_common_words(string, int);
vector<string> words;
int main(){
vector<string> line;
string input;
char filename[50];
ifstream inputFile;
cout << "Enter File Name:" << endl;
cin.getline(filename, 50);
inputFile.open(filename);
//TESTING IF FILE IS OPEN
if (!inputFile.is_open()){
cout << "File Wasn't Opened!" << endl;
return 0;
}
while (getline(inputFile, input)){
line.push_back(input);
}
int size_of_array = static_cast<int>(line.size());
for (int i = 0; i < size_of_array; i++){
istringstream iss(line[i]);
int word_number = 0;
do {
string word;
iss >> word;
words.push_back(word);
word_number++;
} while (iss);
}
string commonword;
cout << "Enter a word to search for (without punctuation)" << endl;
cin >> commonword;
int size_of_array2 = static_cast<int>(words.size());
find_common_words(commonword, size_of_array2);
return 0;
}
void find_common_words(string search, int sizeofarray2) {
//CONVERSION TO STRING WITHOUT PUNCTUATION
for (int i = 0; i < sizeofarray2; i++){
string temp_string = words[i];
for (int j = 0, len = temp_string.size(); j < len; j++){
if (ispunct(temp_string[j])){
temp_string.erase(j--, 1);
len = temp_string.size();
}
}
words[i] = temp_string;
}
//SEARCHING FOR SAME WORDS
int line_number = 1;
int words_found = 0;
for (int i = 0; i < sizeofarray2; i++){
if (search == words[i]){
cout << search << " Was found on line " << line_number << endl;
words_found++;
}
if (words[i].length() == 0){
line_number++;
}
}
cout << line_number - 1 << " lines checked, " << words_found << " matches " << endl;
}
Heres my input file (randomly generated):
It's a very big deal.
Carl won the spelling bee and got a trophy!
Why is your cat so big?
Do you have a big bowl I can borrow?
It's a big company.
Penguins live in the Antarctica.
Don’t be silly, you're going to the game!
What are you talking about?
He threw up in the trash can!
You're so ratchet!
But she is a good caretaker.
Tom is looking for a bigger house to live in.
I get an error with Debug Assertion
Expression c> = -1 && c < = 255
I debugged the program and WhozCraig is right. In your input text in the line
Don’t be silly, you're going to the game!
In the word "Don’t", you do not have the normal apostrophe as you have in the word "It's" or "you're"
This special character is encoded as -110 in temp_string. In the description of std::ispunct you can read:
The behavior is undefined if the value of ch is not representable as unsigned char and is not equal to EOF.
-110 does not fit and causes a debug assertion error. If you cast it to unsigned char like WhozCraig proposed, it will work.
You can also fix your input file.
A very interesting and subtle error . . .
beginner here, Im getting this error within my program which is supposed to find a word in a string then, replace that word with any that you input. When I input multiple words into the str1, it says this:
terminate called after throwing an instance of 'std::out_of_range'
what(): basic_string::erase:__pos (which is 18446744074709551615) > this->size() (which is 9)
Heres my code:
#include <iostream>
#include <string>
using namespace std;
void findReplace(string& str1,string& str2,string& str3);
int main()
{
string str1, str2, str3;
cout << "Enter a sentence that you would like to analyze: \n";
cin >> str1;
cin.ignore();
cout << "Enter a word that you would like to search for: \n";
cin >> str2;
cin.ignore();
cout << "Enter a word that would replace the word that was found: \n";
cin >> str3;
cin.ignore();
findReplace(str1,str2,str3);
return 0;
}
void findReplace(string& str1,string& str2,string& str3)
{
int length= 0;
int str2len= 0;
int str3Length = 0;
length = str1.length();
str2len = str2.length();
str3Length = str3.length();
int found = str1.find(str2);
if ((found!= string::npos))
{
cout << str2 << " found at " << found << endl;
}
str1.erase(found, str2len);
str1.replace(found, str3Length, str3 );
cout << str1;
}
The error message contains two big hints: basic_string::erase, the exception was thrown in the call to erase and 18446744074709551615 is maximum 64 bit unsigned in int which on a modern 64 bit system matches the definition of npos
static const size_type npos = -1;
So let's take a look at what lead up to the call to erase:
int found = str1.find(str2); // find str2
if ((found!= string::npos))
{ // found str2
cout << str2 << " found at " << found << endl; // print that we found it
}
str1.erase(found, str2len); // erase it (even if we didn't find it)
str1.replace(found, str3Length, str3 ); // replace it (even if we didn't find it)
Fix:
auto found = str1.find(str2); // Note: Position ISN'T an int. auto will
// find and use correct type. If auto's not available
// size_t will do the job.
if ((found!= string::npos))
{ // found str2
cout << str2 << " found at " << found << endl; // print that we found it
str1.erase(found, str2len); // erase it
str1.replace(found, str3Length, str3 ); // replace it
}
In addition:
cin >> str1;
will read only one word. Since you need a sentence, use std::getline
You can discard all of the cin.ignore()s. They are useless here. >> will consume any leftover whitespace before the token tit finds. It will leave whitespace after the token, so watch out for Why does std::getline() skip input after a formatted extraction? if you place a std::getline after a >>.
I am attempting to write a program for homework which reads the contents of a notepad file and displays the contents and the number of words int he file. My code currently outputs nothing when I enter the name of the names of files I am using to test the program, and the input validation while loop I inserted does not function either.
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
//Declare needed variables
string fileName, contents;
int wordCount = 0;
ifstream inData;
//Display program info
cout << "*** A SIMPLE FILE PROCESSING PROGRAM ***" << endl;
//Prompt user input
cout << "Enter a filename or type quit to exit: ";
cin >> fileName;
inData.open(fileName.c_str());
//Inform the user when their input is invalid and ask them to input another
file name
while (!inData)
{
inData.clear();
inData.ignore(200, '\n');
cout << "File not found. Please type a correct file name." << endl;
cin >> fileName;
inData.open(fileName.c_str());
}
inData >> contents;
//Read and output the contents of the selected file
while (inData)
{
cout << fileName << " data\n";
cout << "***********************" << endl;
inData >> contents;
wordCount++;
cout << contents << endl;
inData >> contents;
}
//Display the number of words in the file
cout << "***********************" << endl;
cout << fileName << " has " << wordCount << " words." << endl;
inData.close();
return 0;
}
The code compiles in its current state [but does not produce the desired outcome.
I will show you one of the many possible solutions.
But I would not recomend, to check the validity of a filename in a loop. You will give the user no chance to escape. Hence, I propose to open the file, and, if that does not work, show an error message and quit.
Then, what sounds easy in the beginning like, count the words, is not really that easy. What is a word? Characters only, or characters mixed with digits or even an underscore in it like for C++ variable names? Needs to be defined.
Additionally you may have separators like commas or one and more other white spaces. So a line like "Hello,,,,World" cannot be so easily counted. If you try to read the 2 words, then you will see a surprise.
std::string s1{};
std::string s2{};
std::istringstream iss("Hello,,,,World");
iss >> s1 >> s2;
Will read everything in s1!
The solution is that we define clearly what a word is. And this we will do with a std::regex. In the below example we use characters, digits and _
Then we use the regex_iterator to find all occurences of the regex (the word) in the line. We substract the end from the beginning with std::distance, which will give us the count of the words.
Then we give an output to the user in whatever format.
It may seem complicated. But it is precise. And rather flexible. Try to anaylze line by line and you will understand it.
Please see:
#include <iostream>
#include <string>
#include <regex>
#include <fstream>
#include <iomanip>
int main()
{
// Get a filename from the user
std::cout << "Enter a filename:\n";
std::string filename{}; std::cin >> filename;
// Try to open and read the file
std::ifstream fileStream(filename);
if (fileStream) {
// We will count all words
size_t numberOfWordsOverall{ 0 };
// We will also count the lines in the file
size_t lineCounter{ 1 };
// Define, what a word is. In this case: Characters, Digits and _
std::regex regexForWord("[\\w\\d_]+");
// Read all lines in file
std::string line{};
while (std::getline(fileStream, line)) {
// Count the numbers of words in one line
const size_t numberOfWordsInLine = std::distance(
std::sregex_token_iterator(line.begin(), line.end(), regexForWord, 1),
std::sregex_token_iterator()
);
// Update the overall word counter
numberOfWordsOverall += numberOfWordsInLine;
// Show result to user
std::cout << "# " << std::left << std::setw(2) << lineCounter++ << " (Words in line: "<< std::setw(2) << numberOfWordsInLine <<
" Words overall: " << std::setw(4) << numberOfWordsOverall << ") Line content --> " << line << '\n';
}
}
else {
std::cerr << "Could not open file '" << filename << "'\n";
}
return 0;
}
Hope this helps . . .
So far, this is my code:
while(bet > remaining_money || bet < 100)
{
cout << "You may not bet lower than 100 or more than your current money. Characters are not accepted." << endl;
cout << "Please bet again: ";
cin >> bet;
}
It works fine but I'm trying to figure out how to make it loop if the user inputs anything that isn't a number as well.
When I press a letter or say a symbol/sign, the code just breaks.
Using the function
isdigit()
This function returns true if the argument is a decimal digit (0–9)
Don't forget to
#include <cctype>
I would use std::getline and std::string to read the whole line and then only break out of the loop when you can convert the entire line to a double.
#include <string>
#include <sstream>
int main()
{
std::string line;
double d;
while (std::getline(std::cin, line))
{
std::stringstream ss(line);
if (ss >> d)
{
if (ss.eof())
{ // Success
break;
}
}
std::cout << "Error!" << std::endl;
}
std::cout << "Finally: " << d << std::endl;
}
A good way of doing this is to take the input as a string. Now find the length of the string as:
int length = str.length();
Make sure to include string and cctype. Now, run a loop that checks the whole string and sees if there is a character that is not a digit.
bool isInt = true;
for (int i = 0; i < length; i++) {
if(!isdigit(str[i]))
isInt = false;
}
If any character is not a digit, isInt will be false. Now, if your input(a string) is all digits, convert it back to an integer as:
int integerForm = stoi(str);
Store integerForm in your array.
I'm working on homework for my c++ class, and it's been quite awhile since I've used it. I was wondering if there was a way to allow spaces in a string (instead of it nulling out and ending the string)
my current code is this:
int chapter10() {
string strinput;
char charstr[1000];
int numwords=1;
cout << "Enter a phrase ";
cin >> strinput;
cout << strinput;
const int size = strinput.size() + 1;
strcpy_s(charstr, strinput.c_str());
cout << strinput << endl;
for (int i = 0; i != size; i++) {
if (*(charstr + i) == ' ')
numwords++;
}
cout << "There are " << numwords << " words in that string." << endl;
return 0;
}
The problem I'm having, is for instance, if I type "Hello World" and press enter, it pops the next line (right after the cin) and says "Hello", and the space made it cut the rest of the phrase off.
How does one fix this issue? I don't want to use the str:: things as I barely know what they are, and have really never had to use them, and that would look a bit suspicious to the teacher :P
Update: If you've suggested using getline(cin, strinput); It doesn't work too well. I can from what I see, only type in the 10 to reach my function, but after I press enter, it thinks that I've presses something else, which makes it completely skip the cin to get the string value. But, there is something weird with this, if I type "10 hello world" it does everything correctly. Well, with the exception that it needs to be in the same line as the number to reach the function.
Solved: The use of getline(cin, strinput) works perfectly fine, if you're not using user input before hand. If you are, you're going to need a cin.ignore before the getline(). As stated in the comment by my best answer.
#include <iostream>
#include <iomanip>
#include <string>
#include <limits>
using namespace std;
//~~~Initialize all functions
int chapter10();
//~~~Initializing complete
int main() {
srand(time(0)); //makes rng thingy work instead of choose same numbers cause it doesn't do it on its own. lol
cout << "Enter the chapter number you need to look at: ";
int chapterNumber;
cin >> chapterNumber;
switch (chapterNumber) {
case 1: testingStuff(); break;
case 9: chapter9(); break;
case 10: chapter10(); break;
default: cout << "You chose an invalid chapter number, reload the program."; break;
}
system("pause");//So console doesn't close instantly cause that's not annoying at all...
}
int chapter10() {
string strinput;
char charstr[10000];
int numwords=1;
cout << "Enter a phrase." << endl;
cin.ignore(numeric_limits<streamsize>::max(), '\n');
getline(cin, strinput);
const int size = strinput.size() + 1;
strcpy_s(charstr, strinput.c_str());
for (int i = 0; i != size; i++) {
if (*(charstr + i) == ' ' & *(charstr + (i+1)) != ' ' )//the & is fail safe so multiple space no ++numwords
numwords++;
}
cout << "There are " << numwords << " words in that string." << endl;
return 0;
}
The way I have my code written was I used a switch/case to reach my function. This required user input, which in turn caused my program to 'think' I was still typing for the second input required in the chapter10 function.
Adding in the line of code: cin.ignore(numeric_limits<streamsize>::max(), '\n'); allowed me to cancel the input, and start a new one.
If you want to get all characters an end-user enters on a single line, use getline: instead of cin >> strinput write this:
getline(cin, strinput);
The fact that it is actually std::getline(std::cin, strinput) makes no difference, because your code uses std namespace anyway. In case you were wondering what std:: prefix is, it's a namespace of the Standard C++ library.
You can use getline() function
It copies into a string till a newline is reached or delimiter is found - so it will accept all the spaces till newline is reached
http://www.cplusplus.com/reference/string/string/getline/
or you can also use cin.getline() as shown here -
std::cin input with spaces?
use:
cin >> noskipws >> strinput;
Use std::getline() function. Example:
#include <iostream>
#include <vector>
#include <sstream>
void WordCounter(
const std::vector<std::string> & lines) {
for (int i = 0; i < lines.size(); ++i) {
std::istringstream iss(lines[i]);
std::string word;
int count = 0;
while (iss >> word) {
++count;
}
std::cout << "Line #" << i << " contains " << count << " words." <<
std::endl;
}
}
int main() {
std::string line;
std::vector<std::string> lines;
while (std::getline(std::cin, line)) {
lines.push_back(line);
}
WordCounter(lines);
return 0;
}