I'm currently learning about vectors and trying to make a palindrome program using them. This is a simple program and so far, I'm trying to make it identify "I am what am I." as a palindrome properly. This is my program so far:
#include <vector>
#include <string>
#include <iostream>
using namespace std;
vector <string> sentVec;
void getSent(string sent);
void readBackwards(string sent);
int main()
{
string sent;
getSent(sent);
readBackwards(sent);
return 0;
}
void getSent(string sent)
{
cout << "Enter your sentence:" << endl;
getline (cin,sent);
string currentWord, currentLetter;
for (int i = 0; i < sent.length(); i++)
{
currentLetter = sent[i];
if (currentLetter == " ") // inserts word
{
currentWord += sent[i];
sentVec.push_back(currentWord);
currentWord = "";
}
else if (currentLetter == ".") // inserts period
{
sentVec.push_back(currentWord);
currentWord = sent[i];
sentVec.push_back(currentWord);
}
else
{
currentWord += sent[i];
}
}
}
void readBackwards(string sent)
{
string sentForwards, sentBackwards;
// create sentence forwards and backwards without the period.
for (int i = 0; i < sentVec.size() - 1; i++)
{
sentForwards += sentVec[i];
}
for (int j = sentVec.size() - 2; j >= 0; j--)
{
sentBackwards += sentVec[j];
if (j == sentVec.size() - 2)
{
sentBackwards += " ";
}
}
cout << "Sentence forwards is: " << sentForwards << endl;
cout << "Sentence backwards is: " << sentBackwards << endl;
if (sentForwards == sentBackwards)
{
cout << "This sentence reads the same backwards as forwards." << endl;
}
else
{
cout << "This sentence does not read the same backwards as forwards." << endl;
}
}
When I run this program, it prints:
Enter your sentence:
I am what am I.
Sentence forwards is: I am what am I
Sentence backwards is: I am what am I
This sentence does not read the same backwards as forwards.
Why does this not trigger the if loop when comparing the two sentences?
Because sentBackwards isn't the same as sentForwards, because sentBackwards has a trailing whitespace at the end, and thus they aren't the same.
I am unsure how your program detects palindromes, but here is a simple iterative method:
#include <string>
bool isPalindrome(std::string in) {
for (int i = 0; i < in.size() / 2; i++) {
if (in[i] != in[in.size() - 1 - i]) {
return false;
}
}
return true;
}
It returns true if the string passed as an argument is a palindrome
You should not only learn about vector, but also the STL algorithm functions such as std::reverse.
As the other answer given pointed out, one vector has a trailing whitespace. You could have avoided all of that by simply taking the original vector, copying it to another vector, and calling std::reverse. There is no need to write a loop:
void readBackwards()
{
// copy the vector
std::vector<std::string> sentBackwards = sentVec;
// reverse it
std::reverse(sentBackwards.begin(), sentBackwards.end());
// see if they're equal
if (sentVec == sentBackwards)
cout << "This sentence reads the same backwards as forwards." << endl;
else
cout << "This sentence does not read the same backwards as forwards." << endl;
}
This works, since std::vector has an overloaded operator == that compares the items in each of the two vectors and returns true if all items are the same.
In addition to this, reading into a vector can be accomplished much more easily than what you attempted.
#include <sstream>
#include <algorithm>
//...
void getSent(string sent)
{
// remove the periods(s)
auto iter = std::remove_if(sent.begin(), sent.end(), [] (char ch) { return ch == '.';});
sent.erase(iter, sent.end());
// copy the data to a vector
std::istringstream iss(sent);
string currentword;
while ( iss >> currentword)
sentVec.push_back(currentword);
}
Note that we use the std::istringstream to serve as the space delimited parser, alleviating the need to write a loop looking for the space. Also, the std::remove_if algorithm is used to remove any period characters from the string before we start to store the individual strings into a vector.
So basically, the only loop in this whole setup is the while to read from the stream into the vector. Everything else is accomplished by using the algorithm functions, and taking advantage of the various member functions of std::vector (like the overloaded ==)
Related
I've searched all over to try to answer my own questions, but I'm hitting a wall here. I've been working on this same exercise for three days and getting frustrated, hence my very first post! This is a school assignment, but I really want to understand why this isn't working. When I use input "bob" it returns "bob is a palindrome" as expected. When I input "bobby" it returns "bobby is not a palindrome" as expected. All good there. It took me forever to figure out how to remove spaces from my input when using the sentence "never odd or even" but I managed to do that successfully, too. But here's the rub: (1) even after the spaces are removed, it seems to think that "neveroddoreven" is NOT a palindrome - why? What am I missing? Additionally, and this is probably a stupid question (but this is my first foray into programming and I'm a total newbie), how do I get it to output the original userInput before I removed the spaces in the final output? Currently the below code outputs "neveroddoreven is not a palindrome". Thanks in advance for any pointers you can give me.
#include <string>
#include <cctype>
using namespace std;
int main() {
string userInput;
int startInput;
bool isPalindrome = true;
getline (cin, userInput);
startInput = userInput.length();
for(int i = 0; i<userInput.length(); i++)
if(userInput[i] == ' ') userInput.erase(i,1);
for (int i = 0; i<(startInput / 2); i++){
if (userInput[i] != userInput[(startInput -1 ) -i])
isPalindrome = false;
}
if (isPalindrome){
cout << userInput << " is a palindrome" << endl;
}
else {
cout << userInput << " is not a palindrome" << endl;
}
return 0;
}
After you erase all the spaces, startInput no longer refers to the actual length of the string. That means this comparison:
if (userInput[i] != userInput[(startInput -1 ) -i])
is not comparing the correct characters.
You can fix this by adding this line:
startInput = userInput.length();
after doing the erasing.
Here's a demo.
Also, in your erase code, this comparison i<userInput.length() is not a good idea, since you are comparing a signed and unsigned value. Also, you don't erase adjacent spaces. A simpler way to do that would be:
userInput.erase(std::remove(std::begin(userInput), std::end(userInput), ' '),
std::end(userInput));
As others have pointed out, the problem is that startInput doesn't take into account that you erase some spaces. So move the the line startInput = userInput.length(); so that it is just after the erase-loop.
An alternative solution that will not change the original input could be:
#include <iostream>
#include <string>
int main()
{
std::string userInput;
std::string userInputNoSpace; // Use one more string
int startInput;
bool isPalindrome = true;
std::getline(std::cin, userInput);
// Copy all characters that are NOT space to the new string
for (auto c : userInput)
{
if (c != ' ')
{
userInputNoSpace += c;
}
}
startInput = userInputNoSpace.length();
for (int i = 0; i<(startInput / 2); i++)
{
if (userInputNoSpace[i] != userInputNoSpace[(startInput -1 ) -i])
{
isPalindrome = false;
break;
}
}
if (isPalindrome)
{
std::cout << userInput << " is a palindrome" << std::endl;
}
else
{
std::cout << userInput << " is not a palindrome" << std::endl;
}
return 0;
}
Input:
never odd or even
Output:
never odd or even is a palindrome
I need to write a function that gets a string and count how many words there are in the string and how many letters. And then calculate the average of it.
A word in a string is a sequence of letters and numbers separated by one or more spaces.
First of all I have to check if the string is correct. The string must contain only lowercase letters, uppercase letters, and numbers only.
i didnt menage to count all sort of words correctly and also my function doesnt count the last letter.
#include <iostream>
using namespace std;
#include <string.h>
#define SIZE 50
float checkString(char string[]) {
float wordCounter = 0;
float letterCounter = 0;
bool isLegit = true;
int i = 0;
while (isLegit) {
if (((string[i] >= 48 && string[i] <= 57) ||
(string[i] >= 65 && string[i] <= 90) ||
(string[i] >= 97 && string[i] <= 122 ))) {
for (int j = 0; j <= strlen(string); j++) {
if ((string[j - 1] != ' ' && string[j] == ' ' &&
string[i + 1] != ' ')
|| j == (strlen(string) - 1)) {
wordCounter++;
}
else if (string[j] != ' ') {
letterCounter++;
cout << string[j];
}
}
cout << " The avareage is : " << (letterCounter /
wordCounter) << endl;
isLegit = false;
}
else {
return -1;
isLegit = false;
}
}
cout << "Number of words " << wordCounter << endl;
cout << "Number of letters " <<letterCounter << endl;
}
int main() {
char string[SIZE];
cout << "please enter a sentence " << endl;
cin.getline(string, SIZE);
checkString(string);
}
Instead of using char[] for strings, I suggest that you use std::string which can grow and shrink dynamically. It's one of the most common types to use in the standard C++ library. You can also make use of stringstreams which lets you put a string inside it and then you can extract the contents of the stringstream using >>, just like when reading from std::cin.
Example with comments in the code:
#include <iostream>
#include <sstream> // std::stringstream
#include <string> // std::string
// use std::string instead of a char[]
float checkString(const std::string& string) {
// put the string in a stringstream to extract word-by-word
std::istringstream is(string);
unsigned words = 0;
unsigned letters = 0;
std::string word;
// extract one word at a time from the stringstream:
while(is >> word) {
// erase invalid characters:
for(auto it = word.begin(); it != word.end();) {
// Don't use magic numbers. Put the character literals in the code so
// everyone can see what you mean
if((*it>='0' && *it<='9')||(*it>='A' && *it<='Z')||(*it>='a' && *it<='z')) {
// it was a valid char
++it;
} else {
// it was an invalid char, erase it
it = word.erase(it);
}
}
// if the word still has some characters in it, make it count:
if(word.size()) {
++words;
letters += word.size();
std::cout << '\'' << word << "'\n"; // for debugging
}
}
std::cout << "Number of words " << words << "\n";
std::cout << "Number of letters " << letters << "\n";
std::cout << "The average number of letters per word is "
<< static_cast<float>(letters) / words << '\n';
return 0.f; // not sure what you are supposed to return, but since the function
// signature says that you should return a float, you must return a float.
}
int main() {
checkString(" Hello !!! World, now let's see if it works. ");
}
I would like to add an additional answer. This answer is based on "more-modern" C++ and the usage of algorithms. You want to solve 3 tasks:
Check, if string is OK and matched to your expectations
Count the number of words in the given string
Count the number of letters
Calculate the ratio of words/letters
For all this you may use existings algorithms from the C++ standard library. In the attached example code, you will see a one-liner for each task.
The statements are somehow very simple, so that I will not explain much more. If there should be a question, I am happy to answer.
Please see here one possible example code:
#include <iostream>
#include <string>
#include <iterator>
#include <regex>
#include <algorithm>
#include <tuple>
#include <cctype>
std::regex re("\\w+");
std::tuple<bool, int, int, double> checkString(const std::string& str) {
// Check if string consists only of allowed values, spaces or alpha numerical
bool stringOK{ std::all_of(str.begin(), str.end(), [](const char c) { return std::isalnum(c) || std::isspace(c); }) };
// Count the number of words
int numberOfWords{ std::distance(std::sregex_token_iterator(str.begin(),str.end(), re, 1), {}) };
// Count the number of letters
int numberOfLetters{ std::count_if(str.begin(), str.end(), isalnum) };
// Return all calculated values
return std::make_tuple(stringOK, numberOfWords, numberOfLetters, static_cast<double>(numberOfWords)/ numberOfLetters);
}
int main() {
// Ask user to input string
std::cout << "Please enter a sentence:\n";
// Get string from user
if (std::string str{}; std::getline(std::cin, str)) {
// Analyze string
auto [stringOk, numberOfWords, numberOfLetters, ratio] = checkString(str);
// SHow result
std::cout << "\nString content check: " << (stringOk ? "OK" : "NOK") << "\nNumber of words: "
<< numberOfWords << "\nNumber of letters: " << numberOfLetters << "\nRatio: " << ratio << "\n";
}
return 0;
}
Of course there are many more other possible solutions. But, because of the simplicity of this solution, I showed this variant.
When I print out text2 I see that it is definitely not the reverse of the string I gave it and I'm not sure why that is. When I put in "test" I get stuff like "ȍ\2200+". Can I use strncpy on char arrays? Maybe it needs to be done with a loop - not sure. Any help would be appreciated. :)
#include <iostream>
#include <string.h>
using namespace std;
int main()
{
char text[79], text2[79];
bool input = true;
while (input) {
cout << "Please give me a line of text to examine: ";
cin.getline(text, 79);
for(int i = 0; i < strlen(text); i++ )
cout << text[i];
// test to see if it is a palindrome
strncpy(text, text2, 80);
reverse(text2, text2 + strlen(text2));
printf("%s", text2); `// when I print this out I get something odd`
if (strcmp(text, text2) == 0)
cout << " is a palindrome!" << endl;
else
cout << " is not a palindrome." << endl;
if (strcmp(text, "END") == 0)
input = false;
else
cout << "\ntype END to exit the program" << endl;
} // end while loop
} // end main
It seems you're using strncpy in a wrong way: you probably want to copy text into text2, not the other way around.
There's a much simpler way to test whether a string is a palindrome, namely:
bool is_palindrome(const char* s, size_t n) {
size_t i, j;
i = 0, j = n-1;
while (i < j && s[i++] == s[j--])
;
return i >= j;
}
Why not use std::vector<char> and std::reverse from <algorithm> to handle your problem?
I would do something like below: (note that I'm using C++11 range-based for loop and auto which you can change to a regular for loop and use std::string line if you don't have a compiler supporting this).
int main()
{
cout << "Please give me a line of text to examine: ";
auto line = ""s;
getline(cin, line);
// Push back every character to the vector
vector<char> vtext;
for (const auto &elem : line)
vtext.push_back(elem);
// Create a copy of the vector<char> and reverse the copy
vector<char> vtext_reversed{vtext};
reverse(begin(vtext_reversed), end(vtext_reversed));
// Print the line reversed
cout << "\nThis is the line reversed: ";
for (const auto &elem : vtext_reversed)
cout << elem;
}
Typically you'll see this reversal technique for char*:
void reverse(char* s) {
if(!s) return;
size_t n = strlen(s);
for(size_t i = 0; i < n/2; ++i) {
char tmp = s[i];
s[i] = s[n - i - 1];
s[n - i - 1] = tmp;
}
}
This will not work, however, with non-ASCII characters. The reason is that non-ASCII characters require multiple bytes to represent.
You will need to use wide characters to handle multi-byte codepoints, but the logic should follow above.
I am writing a program right now that alters a C_String using pointers to the string. I have an implementation that works fine. The only problem I am running into is that when I reach the end of my program, if I try to delete the pointers I get an error.
My code:
void CStringSwitcher()
{
string input = "";
char* cStringArray = new char[ASIZE];
char* reversed = new char[ASIZE];
const char* originalReversed = reversed;
char* pointers[POINTER_SIZE];
memset(reversed, '\0', ASIZE);
memset(cStringArray, '\0', ASIZE);
memset(pointers, '\0', POINTER_SIZE);
char* current = cStringArray;
cout << "Enter a sentence on each line. Input a 0 to stop." << endl;
// Receives input from the user and stores it into cStringArray
int i = 0;
do
{
cout << ">";
cin.clear();
fflush(stdin);
input = "";
getline(cin, input);
if (input == "0")
break;
else
{
input.append("\n");
pointers[i] = current;
_CRT_SECURE_STRCPY(pointers[i], ASIZE - 1, input.c_str());
current += input.length();
i++;
}
} while(i < POINTER_SIZE);
char* end = current;
--i;
do
{
/// Check if done
if(i < 0)
break;
/// Copy from current to end
current = pointers[i];
do
{
*reversed++ = *current++;
}while(current < end);
/// Update end
end = pointers[i];
/// Update i
--i;
}while(true);
*reversed = '\0';
cout << endl << originalReversed << endl;
system("PAUSE");
//delete[] originalReversed;
//delete[] cStringArray;
return;
}
As it is written above the code works fine, however if I uncomment the two delete lines just before the return I was getting an error:
Project_06.exe has initiated a breakpoint
and the program crashes. Weird thing is I just ran the program again to get the exact wording of the error message and it runs with no error? Any ideas on why that is?
I'm guessing that this code is an educational/practice piece to try and solidify your knowledge of pointers, but to be frank with you: it's an absolute horror to read.
This answer is in the spirit of "teach a man to fish".
Start by removing all of the allocations and instead use fixed-sized arrays.
char cStringArray[ASIZE] = "";
char reversed[ASIZE] = "";
This eliminates the need for the memsets for now, this assignment actually sets the entire array to 0s (see http://ideone.com/WmLtQp).
Doing it this way makes it much easier to catch corruption while running it thru the debugger.
Then switch the arrays over to dynamic allocations.
Lastly, don't mix stdin and cin, doing so can invoke undefined behavior.
---- Edit ----
Here is a C++-refactoring of your code. This particular piece shows both how to do it by hand (manually copying the bytes) and using C++ features to reduce the amount of work we have to do ourselves.
ideone live demo: http://ideone.com/0KuGiB
#include <iostream>
#include <string>
#include <vector>
void CStringSwitcher()
{
std::vector<std::string> inputs;
size_t totalLength = 0;
std::cout << "Enter a sentence on each line. Input a 0 to stop." << std::endl;
inputs.reserve(16);
for ( ; /* until break */ ; ) {
std::cout << ">";
std::string input;
getline(std::cin, input);
if (input == "0")
break;
inputs.push_back(input);
totalLength += input.length() + 1; // for the '\n'
}
std::string reversed = "";
reversed.reserve(totalLength); // eliminate allocations
// walk backwards thru the list of strings.
for (auto inputsIt = inputs.rbegin(); inputsIt != inputs.rend(); ++inputsIt) {
const std::string& input = *(inputsIt);
#ifndef REAL_CODE
// educational, Do-It-Yourself way
const size_t length = input.length();
// walk backwards thru the characters
for (size_t i = 0; i < length; ++i) {
reversed += input[length - 1 - i];
}
#else
// call append with reversed iterators to do it for us.
reversed.append(input.rbegin(), input.rend());
#endif
// add the trailing '\n'
reversed += '\n';
}
std::cout << std::endl << reversed << std::endl;
// don't pause, set a break point at the end of the function
// or run without debugging.
return;
}
int main(int argc, const char* argv[])
{
CStringSwitcher();
return 0;
}
_CRT_SECURE_STRCPY(pointers[i], ASIZE - 1, input.c_str());
current += input.length();
ASIZE-=input.length();
ASIZE-=input.length();
Don't know why it helps to get rid of error. This should only prevent from overflow if size of new string > size of the remaining bytes. Looks like some Microsoft-Specific magic.
Also there are many errors in you code, but this is another story. Just consider using vector, unique_ptr.
---EDIT---
I came with some weird things.
_CRT_SECURE_STRCPY is define to strcpy_s
#include <iostream>
#include <numeric>
using namespace std;
int main()
{
char arr[10];
iota(arr, end(arr), 'A');
for (auto i : arr) cout << i << '\t'; cout << '\n';
strcpy_s(arr, "");
for (auto i : arr) cout << i << '\t'; cout << '\n';
}
My output is:
A B C D E F G H I J
(nonpritable characters)
It means that strcpy_s rewrites WHOLE destination buffer. Even if you pass one char.
I’m trying to make something that will take lines of input from the user, separate them into strings in a vector, then print them one at a time (8 per line).
so far this is what I’ve got:
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
int main(void)
{
using namespace std;
vector<string> svec1;
string temp;
while(getline(cin, temp)) //stores lines of text in temp
{
if(temp.empty()) //checks if temp is empty, exits loop if so.
break;
stringstream ss(temp);
string word;
while(ss >> word) //takes each word and stores it in a slot on the vector svec1
{
svec1.push_back(word);
}
}
}
I’m stuck on getting it to print them 8 at a time, the solutions I’ve tried keep getting subscript out of range errors.
Something like this:
for(int i = 0; i < svec1.size(); i++)
{
cout << svec1[i];
if ((i+1) % 8 == 0)
cout << endl;
else
cout << " ";
}
?
EDIT:
the solution above outputs extra space/newline at the end. It can be avoided by something like this:
for(int i = 0; i < svec1.size(); i++)
{
if (i == 0)
/*do nothing or output something at the beginning*/;
else if (i % 8 == 0)
cout << endl; /*separator between lines*/
else
cout << " "; /*separator between words in line*/
cout << svec1[i];
}
Walk over your vector with an index:
for (unsigned int idx = 0; idx < svec1.size(); ++idx) {
std::cout << svec[idx] << sep(idx); // sep(idx) is conceptual; described below
}
What is this sep(idx)? It is the separator to print after the idxth word. This is
A newline after having printed eight words on a line. idx will be 7, 15, 23, etc: One shy of an integer multiple of 8. In code, (idx+1)%8 == 0.
A newline for the last item in the vector; you probably want the last item to be followed with a newline. In code idx+1 == svec.size().
A space otherwise.
An easy way to do this is with the ternary operator:
for (unsigned int idx = 0; idx < svec1.size(); ++idx) {
const char * sep = (((idx+1)%8 == 0) || (idx+1 == svec.size())) ? "\n" : " ";
std::cout << svec[idx] << sep;
}
If you don't like that,
for (unsigned int idx = 0; idx < svec1.size(); ++idx) {
const char * sep;
if (((idx+1)%8 == 0) || (idx+1 == svec.size())) {
sep = "\n";
}
else {
sep = " ";
}
std::cout << svec[idx] << sep;
}
Normally you iterate over a vector using a for loop clause. So if you want to print all elements of your vector<string> you have to make something like this:
for(vector<string>::iterator it = myvec.begin(); it != myvec.end(); ++it) {
cout << *it;
}
EDIT: as Vlad has posted correctly, you can also use array indices, which are less efficient in lists, but equally efficient with vectors.