I have made a quiz helper, but, as I want it for you to be able to input a new question without starting it, I made a do/while loop. The first run goes fine. When it asks you if you want to input another question, if you choose y, it runs the main program too at the same time, and the program registers y as a question. How do I separate this?Code:
#include <cctype>
#include <iostream>
#include <string>
#include "Quiz.h"
#include "Quiz2.h"
char choice;
int main()
{
do{
quiz();
std::cout << "Da li zelite da vam odgovorim na jos jedno pitanje?(y/n)" << std::endl;
std::cin >> choice;
} while(choice != 'n');
}
The first header file just includes the function to find words:
#ifndef QUIZ_H_INCLUDED
#define QUIZ_H_INCLUDED
bool contains_word(const std::string& sentence, const std::string& word)
{
size_t pos = 0;
while ((pos = sentence.substr(pos).find(word)) != std::string::npos) {
if (!(isalpha(sentence[pos - 1])) || !(isalpha(sentence[pos + word.size() + 1])))
return true;
}
return false;
}
#endif
And the other one contains the real code, it is partially in Serbian:
#ifndef QUIZ2_H_INCLUDED
#define QUIZ2_H_INCLUDED
int quiz()
{
std::string sentence;
std::cout << "Ukucajte pitanje ili kljucne reci: " << std::flush;
std::getline(std::cin, sentence);
std::string word ("Belgija");
std::string word2 ("regija");
std::string word3 ("Kanada");
std::string word4 ("teritorija");
std::string word5 ("Holandija");
std::string word6 ("delova");
if (contains_word(sentence, word) && contains_word(sentence, word2))
std::cout << "Odgovor je 3 regije." << std::endl;
else if (contains_word(sentence, word3) && contains_word(sentence, word4))
std::cout << "Odgovor je 3 teritorije." << std::endl;
else if (contains_word(sentence, word5) && contains_word(sentence, word6))
std::cout << "Odgovor je 3 dela." << std::endl;
else
std::cout << "Nisam mogao pronaci odgovor na to pitanje!" << std::endl;
return 0;
}
#endif
Any help is appreciated.
There is a newline character after a choice, so just add cin.ignore:
std::cin >> choice;
std::cin.ignore();
It ignores one character from input.
Alternative solution
Alternatively, you could discard all empty lines in quiz() function - substitute
std::getline(std::cin, sentence);
with
do {
std::getline(std::cin, sentence);
} while( sentence.empty() );
Other issue with the code
The contains_word function should be corrected. You shouldn't get value of sentence[pos + word.size() + 1] if it is possible for the word to be at the very end of sentence (subscript past the end of array).
Similar error in sentence[pos - 1] - what if pos is 0? You get some random stuff before the string.
You have to rework the condition also - certainly if( !(...) || !(...) ) is not what you wanted.
Should be something like this:
bool contains_word(const std::string& sentence, const std::string& word)
{
size_t pos = 0;
while ((pos = sentence.substr(pos).find(word)) != std::string::npos) {
if( (pos == 0 || isalpha(sentence[pos - 1])) &&
(pos + word.size() == sentence.size() || isalpha(sentence[pos + word.size()])) )
return true;
}
return false;
}
getline() and cin don't play well together. After a cin operation there is typically a newline left in the input stream that the getline reads and immediately concludes it is done. You can "solve" the problem by calling getline twice and ignoring the first result. However note that this only happens AFTER a call to cin, so the first time thru you should NOT use getline twice. This is why I say they "don't play well together".
Related
I'm having trouble with this project because when I put a sentence such as "cat is not a dog", it will not say "Didn't find repeated word" as intended. Instead, it will say "Found repeated word", as if I it was true. Also, each time it is run for the first time, the first letter is removed from the user input. Why?
#include <iostream>
#include <iomanip>
#include <string>
#include <fstream>
#include <time.h>
#include <stdio.h>
using namespace std;
int main()
{
int count = 0;
bool repeat = false;
char yn;
string input, newWord, firstword;
do
{
count = 0;
repeat = false;
cin.sync();
cout << "Please enter a sentence: ";
cin.ignore();
getline(cin, input);
while (input.at(count) != ' ')
count++;
firstword = input.substr(0, count);
input = input.substr(count++);
count = 0;
while(count < input.size() && repeat == false)
{
count++;
while (count < input.size() && input.at(count) != ' ')
count++;
newWord = input.substr(0, count);
if (firstword.compare(newWord) == 0)
input = input.substr(count++);
else
repeat = true;
}
if (repeat == true)
{
cout << "\nI found a repeated word!";
cout << "\nWould you like to try again? (y/n)";
cin >> yn;
}
else if(repeat == false)
{
cout << "\nI didn't find a repeated word.";
cout << "\nWould you like to try again? (y/n)";
cin >> yn;
}
} while (yn == 'y');
}
You program only checks if the first word is repeated annywhere else in the sentence...
Looking for a repeated word being: Each word must be checked against its immediate predecessor.
You're almost there. You forgot to reassign firstWord to newWord at te end of the parsing loop.
while(count < input.size() && repeat == false)
{
// ...
newWord = input.substr(0, count);
if (firstword.compare(newWord) == 0)
input = input.substr(count++);
else
repeat = true;
firstWord = newWord; // <-- Assign here.
}
Just an aside note, a trick of the trade, if you will.
if (repeat == true)
//...
else if(repeat == false) // <- Avoid doing that. Use plain else for booleans.
A bool can only have two values. And this kind of else if construct will bring unexpected surprises when plain ints are used for boolean equations. (if repeat was 3, which path does each cases of 0, 1, 3 follow?)
Have you tried removing the call to std::cin.sync() ?
Print your input after getline to be sure it's correctly saved. You probably don't need cin.ignore();. The first character is not saved.
An alternative sollution would be to use the following:
#include <iostream>
#include <string>
#include <fstream>
#include <map>
#include <sstream>
using namespace std;
int main()
{
int pos = 0;
string input;
cout << "Please enter a sentence: ";
getline(cin, input);
map<string, int> count_words;
stringstream ss(input);
string word;
while(getline(ss, word, ' '))
count_words[word]++;
map<string, int>::const_iterator it;
for(it = count_words.begin() ; it != count_words.end() ; ++it)
if( it->second == 2)
cout << "Found duplicate: " << it->first << endl;
return 0;
}
this answer(https://stackoverflow.com/a/236803/4388908) provides a good method to split your input.
The rest: Programming Pearls by Jon Bentley
I have a program that reverses the letters in a sentence but keeps the words in the same order. I need to change the code from an iostream library to an fstream library where the user inputs a sentence into an input file("input.txt") and the program outputs the reverse into an output text file.
example of input:
This problem is too easy for me. I am an amazing programmer. Do you agree?
Example of output:
sihT melborp si oot ysae rof em. I ma na gnizama remmargorp. oD uoy eerga?
The code I already have:
int main()
{
int i=0, j=0, k=0, l=0;
char x[14] = "I LOVE CODING";
char y[14] = {'\0'};
for(i=0; i<=14; i++) {
if(x[i]==' ' || x[i]=='\0') {
for(j=i-1; j>=l; j--)
y[k++] = x[j];
y[k++] = ' ';
l=i+1;
}
}
cout << y;
return 0;
}
I would use std::string to store the string, and benefit from std::vector and const_iterator to make better use of C++ features:
#include <string>
#include <vector>
int main()
{
std::string s("This problem is too easy for me. I am an amazing programmer. Do you agree?");
const char delim = ' ';
std::vector<std::string> v;
std::string tmp;
for(std::string::const_iterator i = s.begin(); i <= s.end(); ++i)
{
if(*i != delim && i != s.end())
{
tmp += *i;
}else
{
v.push_back(tmp);
tmp = "";
}
}
for(std::vector<std::string>::const_iterator it = v.begin(); it != v.end(); ++it)
{
std::string str = *it,b;
for(int i=str.size()-1;i>=0;i--)
b+=str[i];
std::cout << b << " ";
}
std::cout << std::endl;
}
Output:
sihT melborp si oot ysae rof .em I ma na gnizama .remmargorp oD uoy ?eerga
The code that you submitted looks much more like something from C rather than from C++. Not sure if you are familiar std::string and function calls. As the code you wrote is pretty sophisticated, I will assume that you are.
Here is an example of how to use fstream. I almost always you getline for the input because I find that it gets me into fewer problems.
I then almost always use stringstream for parsing the line because it neatly splits the lines at each space.
Finally, I try to figure out a while() or do{}while(); loop that will trigger off of the input from the getline() call.
Note that if the word ends in a punctuation character, to keep the punctuation at the end, the reverse_word() function has to look for non-alpha characters at the end and then save that aside. This could be done by only reversing runs of alphas.
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
///////////////////
/// return true if ch is alpha
/// return false for digits, punctuation, and all else
bool is_letter(char ch){
if((ch >= 'A' && ch <= 'Z') ||
(ch >= 'a' && ch <= 'z')) {
return true;
} else {
return false;
}
}
////////
// Only reverse the letter portion of each word
//
std::string reverse_word(std::string str)
{
std::string output_str; // Probably have to create a copy for output
output_str.reserve(str.length()); // reserve size equal to input string
// iterate through each letter of the string, backwards,
// and copy the letters to the new string
char save_non_alpha = 0;
for (auto it = str.rbegin(); it != str.rend(); it++) {
/// If the last character is punctuation, then save it to paste on the end
if(it == str.rbegin() && !is_letter(*it)) {
save_non_alpha = *it;
} else {
output_str += *it;
}
}
if(save_non_alpha != 0) {
output_str += save_non_alpha;
}
return output_str; // send string back to caller
}
int main()
{
std::string input_file_name{"input.txt"};
std::string output_file_name{"output.txt"};
std::string input_line;
std::ifstream inFile;
std::ofstream outFile;
inFile.open(input_file_name, std::ios::in);
outFile.open(output_file_name, std::ios::out);
// if the file open failed, then exit
if (!inFile.is_open() || !outFile.is_open()) {
std::cout << "File " << input_file_name
<< " or file " << output_file_name
<< " could not be opened...exiting\n";
return -1;
}
while (std::getline(inFile, input_line)) {
std::string word;
std::string sentence;
std::stringstream stream(input_line);
// I just like stringstreams. Process the input_line
// as a series of words from stringstream. Stringstream
// will split on whitespace. Punctuation will be reversed with the
// word that it is touching
while (stream >> word) {
if(!sentence.empty()) // add a space before all but the first word
sentence += " ";
word = reverse_word(word);
sentence += word;
}
outFile << sentence << std::endl;
}
inFile.close();
outFile.close();
return 0;
}
I want to hold a string with spaces therefore I used getline() but after it I want to get another string(no spaces) if there is a -e for example and the string after it in s2, but since in my code I lose the dash when using getline() I can't seem to achieve what I'm trying to do. any suggestions would be really helpful.
//example input: -f name -b blah blah -e email
//looking for output:
//name
//blah blah
//email
string s,s1,s2;
char check_character;
while (cin.peek() != '\n')
{
if (cin.get() == '-')
{
check_character = cin.get();
switch(check_character)
{
case 'f':
cin >> s;
break;
case 'b':
if(cin.peek() != '\n')
getline(cin, s1, '-');
else if(cin.peek() =='\n')
getline(cin, s1);
break;
case 'e':
cin>> s2;
break;
}
}
}
cout << s << endl << s1 << endl << s2 << endl;
return 0;
}
A better option would be to do a single call to getline() then parse the "command" string. There are many options of achieving this, from a simple split() on "-" or find('-')
getline() extracts characters from is and stores them into str until the delimitation character delim is found or the newline character, '\n'.
If the delimiter is found, it is extracted and discarded (i.e. it is not stored and the next input operation will begin after it).
I'm going to make a couple assumptions here:
You never expect a '\n' except at the end of the input string, even after "-b" (which your code will currently read in)
You expect to only accept 1 of each type of argument (cause your current code will stomp any previous entries)
A regex_search will handle this nicely with the regex:
(?:\s*-f\s+(\w+)|\s*-b\s+([^-]+)|\s*-e\s+(\w+))*
Live Example
You'll need to start by reading from cin into a variable, for example string input. This could be done like:
getline(cin, input)
Once you have your input you can simply do:
if(smatch m; regex_search(input, m, regex{ "(?:\\s*-f\\s+(\\w+)|\\s*-b\\s+([^-]+)|\\s*-e\\s+(\\w+))*" })) {
if(m[1].length() > 0U) {
cout << "-f " << m[1] << endl;
}
if(m[2].length() > 0U) {
cout << "-b " << m[2] << endl;
}
if(m[3].length() > 0U) {
cout << "-e " << m[3] << endl;
}
}
Live Example
If you go the approach of reading in the entire line, and you do not want to use Boost program options, or getopts, you could parse the line yourself (as has been suggested). Here would be one way of doing it, as an alternative of parsing on the fly in your code:
#include <iostream>
#include <string>
#include <tuple>
#include <vector>
using std::cout;
using std::endl;
using std::get;
using std::literals::string_literals::operator""s;
using std::make_tuple;
using std::string;
using std::tuple;
using std::vector;
static auto chunkLine(string const& line)
{
auto result = vector<string>{};
auto i = string::size_type{};
while (i != string::npos && i < line.size())
{
auto pos = line.find(" -", i);
auto count = pos == string::npos ? pos : (pos - i);
result.push_back(line.substr(i, count));
i = pos + (pos != string::npos ? 1 : 0);
}
return result;
}
static auto parseChunks(vector<string> const& chunks)
{
auto result = vector<tuple<string, string>>{};
for (auto const& chunk : chunks)
{
auto pos = chunk.find(" ");
if (pos != string::npos && chunk[0] == '-')
{
auto kv = make_tuple(chunk.substr(1, pos-1), chunk.substr(pos+1));
result.push_back(kv);
}
}
return result;
}
int main()
{
auto line = "-f name -b blah blah -e email"s;
auto keyValueTuples = parseChunks(chunkLine(line));
for (auto const& kv : keyValueTuples)
{
cout << get<1>(kv) << endl;
}
}
The way you parse the arguments could certainly be improved, but this answer is not about it. I think what you are looking for is to simply put the - char back into the stream after std::getline removed it. In this case you could just use .putback() method
if (std::cin.peek() != '\n')
{
std::getline(std::cin, s1, '-');
std::cin.putback('-');
}
I think you should put cin.ignore before typing getline as in your code:
`string s,s1,s2;
char check_character;
while (cin.peek() != '\n')
{
if (cin.get() == '-')
{
check_character = cin.get();
switch(check_character)
{
case 'f':
cin >> s;
break;
case 'b':
if(cin.peek() != '\n')
cin.ignore
getline(cin, s1, '-');
else if(cin.peek() =='\n')
cin.ignore
getline(cin, s1);
break;
case 'e':
cin>> s2;
break;
}
}
}
cout << s << endl << s1 << endl << s2 << endl;
return 0; `
Hi I'm trying to take a c-string from a user, input it into a queue, parse the data with a single space depending on its contents, and output the kind of data it is (int, float, word NOT string).
E.g. Bobby Joe is 12 in 3.5 months \n
Word: Bobby
Word: Joe
Word: is
Integer: 12
Word: in
Float: 3.5
Word: months
Here's my code so far:
int main()
{
const int maxSize = 100;
char cstring[maxSize];
std::cout << "\nPlease enter a string: ";
std::cin.getline(cstring, maxSize, '\n');
//Keyboard Buffer Function
buffer::keyboard_parser(cstring);
return EXIT_SUCCESS;
}
Function:
#include <queue>
#include <string>
#include <cstring>
#include <iostream>
#include <cstdlib>
#include <vector>
namespace buffer
{
std::string keyboard_parser(char* input)
{
//Declare Queue
std::queue<std::string> myQueue;
//Declare String
std::string str;
//Declare iStringStream
std::istringstream isstr(input);
//While Loop to Read iStringStream to Queue
while(isstr >> str)
{
//Push onto Queue
myQueue.push(str);
std::string foundDataType = " ";
//Determine if Int, Float, or Word
for(int index = 0; index < str.length(); index++)
{
if(str[index] >= '0' && str[index] <= '9')
{
foundDataType = "Integer";
}
else if(str[index] >= '0' && str[index] <= '9' || str[index] == '.')
{
foundDataType = "Float";
break;
}
else if(!(str[index] >= '0' && str[index] <= '9'))
{
foundDataType = "Word";
}
}
std::cout << "\n" << foundDataType << ": " << myQueue.front();
std::cout << "\n";
//Pop Off of Queue
myQueue.pop();
}
}
}
Right now with this code, it doesn't hit the cout statement, it dumps the core.
I've read about using the find member function and the substr member function, but I'm unsure of how exactly I need to implement it.
Note: This is homework.
Thanks in advance!
UPDATE: Okay everything seems to work! Fixed the float and integer issue with a break statement. Thanks to everyone for all the help!
Your queue is sensible: it contains std::strings. Unfortunately, each of those is initialised by you passing cstring in without any length information and, since you certainly aren't null-terminating the C-strings (in fact, you're going one-off-the-end of each one), that's seriously asking for trouble.
Read directly into a std::string.
std::istreams are very useful for parsing text in C++... often with an initial read of a line from a string, then further parsing from a std::istringstream constructed with the line content.
const char* token_type(const std::string& token)
{
// if I was really doing this, I'd use templates to avoid near-identical code
// but this is an easier-to-understand starting point...
{
std::istringstream iss(token);
int i;
char c;
if (iss >> i && !(iss >> c)) return "Integer";
}
{
std::istringstream iss(token);
float f;
char c; // used to check there's no trailing characters that aren't part
// of the float value... e.g. "1Q" is not a float (rather, "word").
if (iss >> f && !(iss >> c)) return "Float";
}
return "Word";
}
const int maxSize = 100; // Standard C++ won't let you create an array unless const
char cstring[maxSize];
std::cout << "\nPlease enter a string: ";
if (std::cin.getline(cstring, maxSize, '\n'))
{
std::istringstream iss(cstring);
std::string token;
while (iss >> token) // by default, streaming into std::string takes a space-...
token_queue.push(token); // ...separated word at a time
for (token_queue::const_iterator i = token_queue.begin();
i != token_queue.end(); ++i)
std::cout << token_type(*i) << ": " << *i << '\n';
}
I want to start of by saying that I am still learning and some might think that my code looks bad, but here it goes.
So I have this text file we can call example.txt.
A line in example.txt can look like this:
randomstuffhereitem=1234randomstuffhere
I want my program to take in the numbers that are next to the item= and I have started a bit on it using the following code.
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
string word;
int main()
{
ifstream readFile("example.txt", ios::app);
ofstream outfile("Found_Words.txt", ios::app);
bool found = false;
long int price;
cout << "Insert a number" << endl;
cout << "number:";
cin >> number;
system("cls");
outfile << "Here I start:";
while( readFile >> word )
{
if(word == "item=")
Here is the problem; first of all it only searchs for "item=" but to find it, it cannot be included with other letters. It has to be a standalone word.
It wont find:
helloitem=hello
It will find:
hello item= hello
It has to be separated with spaces which is also a problem.
Secondly I want to find numbers next to the item=. Like I want it to be able to find item=1234 and please note that 1234 can be any number like 6723.
And I dont want it to find what comes after the number, so when the number stops, it wont take in anymore data. Like item=1234hello has to be item=1234
{
cout <<"The word has been found." << endl;
outfile << word << "/" << number;
//outfile.close();
if(word == "item=")
{
outfile << ",";
}
found = true;
}
}
outfile << "finishes here" ;
outfile.close();
if( found = false){
cout <<"Not found" << endl;
}
system ("pause");
}
You can use a code like this:
bool get_price(std::string s, std::string & rest, int & value)
{
int pos = 0; //To track a position inside a string
do //loop through "item" entries in the string
{
pos = s.find("item", pos); //Get an index in the string where "item" is found
if (pos == s.npos) //No "item" in string
break;
pos += 4; //"item" length
while (pos < s.length() && s[pos] == ' ') ++pos; //Skip spaces between "item" and "="
if (pos < s.length() && s[pos] == '=') //Next char is "="
{
++pos; //Move forward one char, the "="
while (pos < s.length() && s[pos] == ' ') ++pos; //Skip spaces between "=" and digits
const char * value_place = s.c_str() + pos; //The number
if (*value_place < '0' || *value_place > '9') continue; //we have no number after =
value = atoi(value_place); //Convert as much digits to a number as possible
while (pos < s.length() && s[pos] >= '0' && s[pos] <= '9') ++pos; //skip number
rest = s.substr(pos); //Return the remainder of the string
return true; //The string matches
}
} while (1);
return false; //We did not find a match
}
Note that you should also change the way you read strings from file. You can either read to newline (std::getline) or to the end of stream, like mentioned here: stackoverflow question