Encrypting a string with a stack - c++

I received an assignment for my C++ class last week. I think some of you will find it interesting! I managed to get most of the code down but I'm stuck and cannot figure this out for the life of me... Below are the guidelines for the encrypting process I must put into code:
The message sender inputs a four letter word, CCCC, and another four letter word,
XXXX.
The message sender then inputs the message to be encrypted.
The program scans the message one char at a time and each char is pushed in a stack until
either the scanned character is in the word CCCC or the end of the message is
encountered.
When the scanned character is one of the chars in CCCC, print that char and continue
to print and pop the chars at the top of the stack until either the stack is empty or the
char at the top of the stack is one of the chars in XXXX. When the end of the
message is encountered, print the character at the top of the stack and continue to pop
and print from the top of the stack until the stack is empty.
Here is a hint: "GOOD" "LUCK", it "SOUNDS SIMPLE TO ME", or as
your program would say: "OSDNOT EEM LPMIS SU"
So that is the actual assignment.
What I am having trouble with is the last bit:
When the end of the
message is encountered, print the character at the top of the stack and continue to pop
and print from the top of the stack until the stack is empty.
now here is the code I have so far:
#include <string>
#include <iostream>
using namespace std;
class Stack
{
private:
char Chars[50];
int top;
public:
int push(char);
char pop();
bool isEmpty();
bool isFull();
Stack()
{
top = 0;
}
};
int main()
{
Stack theStack;
char word1[4];
char word2[4];
for(int i=0; i < 4; i++){
word1[i] = ' ';
word2[i] = ' ';
}
char message[500];
cout << "Please enter a 4 letter word: ";
cin >> word1;
while(word1[4] || !word1[3])
{
cout << "Word must be 4 chars long. Try again: ";
cin >> word1;
}
cout << "Please enter another 4 letter word: ";
cin >> word2;
while(word2[4] || !word2[3])
{
cout << "Word must be 4 chars long. Try again: ";
cin >> word2;
}
cout << "Please enter the phrase to be encrypted (50 chars max): ";
cin.ignore(1000, '\n');
cin.getline(message,500);
int length = strlen(message);
int count = 0;
char finalMsg[length];
//scanner
for(int i = 0; i < length; i++)
{
if(message[i] == word1[0] ||
message[i] == word1[1] ||
message[i] == word1[2] ||
message[i] == word1[3])
{
finalMsg[count] = message[i];
count++;
if(message[i-1] != word2[0] ||
message[i-1] != word2[1] ||
message[i-1] != word2[2] ||
message[i-1] != word2[3])
{
finalMsg[count] = message[i-1];
count++;
}
}
else
{
theStack.push(message[i]);
}
}
cout << finalMsg << endl;
return 0;
}
int Stack::push(char data)
{
Chars[top] = data;
top++;
return top;
}
char Stack::pop()
{
char ret = Chars[top-1];
top--;
return ret;
}
bool Stack::isEmpty()
{
if(top <= 0)
return true;
else return false;
}
bool Stack::isFull()
{
if(top >= 50)
return true;
else return false;
}
When compiled, the final output gives me "OSDNOT" which is in the example provided by my professor, so I know I'm heading down the right track.. Any help would be great, I don't even know where to begin to examine the code.

Here is the corrected code. You didn't code the algorithm right. I have commented the changes i have made in the code.
first of all, you didn't pop out the elements of the stack when you encountered a character present in CCCC while scanning. Also at the end of scanning, you didn't empty the stack. Include cstring instead of string. As pointed out in the comments, your declaration for word1 and word2 is incorrect.
char finalMsg[200];
//scanner
for(int i = 0; i < length; i++)
{
if(message[i] == word1[0] ||
message[i] == word1[1] ||
message[i] == word1[2] ||
message[i] == word1[3])
{
finalMsg[count] = message[i];
count++;
//pop out elements from the stack till it is empty or an character of XXXX is encountered
while(!theStack.isEmpty())
{
char tmp=theStack.pop();
if(tmp==word2[0] ||
tmp==word2[1] ||
tmp==word2[2] ||
tmp==word2[3])
{
theStack.push(tmp);
break;
}
finalMsg[count++]=tmp;
}
}
else
{
theStack.push(message[i]);
}
}
//empty the stack
while(!theStack.isEmpty())
{
finalMsg[count++]=theStack.pop();
}
finalMsg[count++]=0;
cout << finalMsg << endl;
PS: Better use std::stack and std::string.

Related

C++ Can't print certain words from Char array

Not sure how to phrase the question, but I'm making a program for an assignment, which we're not allowed to use pre-existing libraries besides input/output. We also can only use primitive data-types. I have to read a text file with words, remove all punctuation from the word, and then store those words in a 2D array of characters.
This problem seems to be that when a word starts with a non-alphabetic character, the whole word doesn't output when using cout << stack[top] but when I output each individual character with cout << stack[top][i], it produces the expected output.
'stack' is a 2D array which contains characters to make up words.
'top' is a variable to represent the length of stack
Code:
#include <iostream>
#include <fstream>
using namespace std;
// Function Prototypes
void push(char word[]);
char formatCharacter(char letter);
bool isAlphabet(char letter);
char toLowercase(char letter);
// Global Variables
const int STACK_SIZE = 50000;
const int WORD_SIZE = 30;
char stack[STACK_SIZE][WORD_SIZE];
int top = 0;
int words = 0;
int wordCount[STACK_SIZE];
int main(){
// Local Variables
char filename[20];
ifstream fin;
char word[WORD_SIZE];
// Get file input
cerr << "Please enter the name of the input file: ";
cin >> filename;
// Open file
fin.open(filename);
// Print error if file doesn't open, then quit the program.
if (!fin) {
cerr << "Error opening file " << filename << ". Program will exit." << endl;
return 0;
}
// Read the file into the stack
while (fin >> word) {
push(word);
}
// Close file
fin.close();
}
void push(char word[]){
if (top == STACK_SIZE) return;
int i = 0;
int j = 0;
do {
if (isAlphabet(word[i])){
word[i] = formatCharacter(word[i]);
stack[top][i] = word[i];
cout << stack[top][i]; // Output fine
j++;
}
i++;
} while (word[i]);
wordCount[words] = j;
//cout << stack[top] << ": " << wordCount[words] << endl; // Output incorrect
cout << endl;
top++;
words++;
return;
}
bool isAlphabet(char letter){
if ((letter < 'A' || letter > 'Z') && (letter < 'a' || letter > 'z')){
return false;
}
else{
return true;
}
}
char formatCharacter(char letter){
if ((letter < 'A' || letter > 'Z') && (letter < 'a' || letter > 'z')){
letter = '\0';
}
else{
if (letter >= 'A' && letter <= 'Z'){
letter = toLowercase(letter);
}
}
return letter;
}
char toLowercase(char letter){
letter = letter + 32;
return letter;
}
isAlphabet() just checks if it's an alphabetic character
formatCharacter() removes any punctuation by replacing the character with '\0', and also changes uppercase to lowercase.
Input:
Jabberwocky
'Twas brillig, and the slithy toves
Did gyre and gimble in the wabe:
All mimsy were the borogoves,
And the mome raths outgrabe.
Output when using cout << stack[top][i]:
jabberwocky
twas
brillig
and
the
slithy
toves
did
gyre
and
gimble
in
the
wabe
all
mimsy
were
the
borogoves
and
the
mome
raths
outgrabe
Output when using cout << stack[top]:
jabberwocky: 11
: 4
brillig: 7
and: 3
the: 3
slithy: 6
toves: 5
did: 3
gyre: 4
and: 3
gimble: 6
in: 2
the: 3
wabe: 4
all: 3
mimsy: 5
were: 4
the: 3
borogoves: 9
and: 3
the: 3
mome: 4
raths: 5
outgrabe: 8
Notice the word 'twas' is missing. I'd rather not loop through each character of each word to get the output I need. I'd appreciate any advice, thanks!
The simplest fix is to change:
stack[top][i] = word[i];
To:
stack[top][j] = word[i];
^ j not i here
This will ensure that the 'Twas ends up as twas and not \0twas.
Also, formatCharacter() should call isAlphabet() rather than repeat the condition.

Reading stack pop/push operations by text-file input

I have a text file with the following contents:
2
S 8
push 2 push 3 push 5 push 7 pop print push 6 print
S 4
pop print push 1 print
An assignment gives:
The first line of the input file means the number of test cases. For each test case, the first character means which container adapter (Stack or Queue) that you need to use Linked lists to implement. The correct output should be:
The values in the stack : 2 3 5
The values in the stack : 2 3 5 6
The values in the stack :
The values in the stack : 1
I've written some working functions for stack and queue struct, though I am working on the input of the stack function first.
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace std;
void push(int);
int pop();
void printStack();
struct Stack {
int s[100];
int top;
Stack() { top = -1; }
};
Stack st;
void push(int n) {
if (st.top == 99) {
cout << "Stack full" << endl;
}
else {
st.s[++st.top] = n;
}
}
int pop() {
if (st.top == -1) {
cout << "Stack is empty" << endl;
}
else {
return st.s[st.top--];
}
}
void printStack() {
cout << "Elements";
for (int i = 0;i <= st.top;i++) {
cout << st.s[i] << ' ';
}
cout << endl;
}
void clearStack() {
st.top = -1;
}
The main part of the code is giving me trouble. I want to read every token of the text file while keeping the line structure; e.g. being able to parse by order of line. However, I do not know the length of each line, only the number of lines. How may I read the file correctly and finish this assignment?
int main() {
std::ifstream file("input1.txt");
std::string item_name;
int numTestCases;
vector<string> file_content{};
while (std::getline(file, item_name))
{
//cout << item_name << "\n";
char str[252];
strcpy(str, item_name.c_str());
char* pch;
//cout << "str0:" << str[0] << "\n";
file_content.push_back(str);
}
cout << "printing file content:" << endl;
for (int i = 0;i < file_content.size(); i++) {
cout << file_content[i] << endl;
}
}
Okay, you basically have two distinct problems.
Read your input file so you know what actions to perform.
Implement both Stack and Queue.
So start by breaking them up. I looked at your stack code. Your problem says to use linked lists, which isn't what you're doing. Maybe that's just because you haven't gotten that far yet.
Code like this would give you the number of test cases.
std::string numCasesStr;
if (!getline(file, numCasesStr)) {
cout << "early end of file detected\n";
return;
}
int numCases = std::stoi(numCasesStr);
At this point, you can now do this:
for (int testCase = 0; testCase < numCases; ++testCase) {
std::string typeAndCountStr;
if (!getline(file, typeAndCountStr)) {
cout << "early end of file detected\n";
return;
}
char typeC = typeAndCountStr.at(0);
if (typeC == 'S') {
...
}
else if (typeC == 'Q') {
...
}
}
The harder part is parsing the next line. You're going to get input in a similar fashion, but then you have to break it into pieces. This is called tokenizing. Basically you split it at each space. What's useful is the find method on a string.
do {
size_t lastPos = 0;
size_t pos = str.find(' ', lastPos);
string thisArg;
if (pos != string::npos) {
thisArg = str.substr(lastPos, pos);
lastPos = pos + 1;
}
else {
thisArg = str.substr(lastPos);
}
// At this point, thisArg contains one argument. You still have more
// to do, but this is one way to split your string into pieces.
} while (lastPos != string::npos);
What I do with that is stuff the individual pieces into a std::vector<std::string> and now it's a lot easier to deal with. You can traverse the vector, looking at each string, and depending upon what it is, you know if you have to grab the next item in the list (like push 8 -- you get push and then you get the 8) or just use the current item.
Overall -- break the problem down into smaller pieces. For main:
Get the number of test cases
Loop from 0..testCaseCnt
Get the type of tests (stack or queue)
Get the next input
Split it into tokens broken at each space
Traverse the tokens and Do The Right Thing (tm).
Code for main to read inputs:
int main() {
int numTestCases;
vector<string> file_content{};
fstream ifs;
ifs.open("input2.txt");
if (!ifs.is_open()) {
cout << "Failed to open file.\n";
}
else {
ifs >> numTestCases;
char type;
int numberOps;
//ifs >> type;
cout << numTestCases;
for (int j = 0;j < numTestCases;j++) {
ifs >> type;
ifs >> numberOps;
if (type == 'S') {
Stack st;
clearStack();
for (int i = 0;i < numberOps;i++) {
string operation;
ifs >> operation;
if (operation == "push") {
int pushed;
ifs >> pushed;
push(pushed);
}
if (operation == "pop") {
pop();
}
if (operation == "print") {
printStack();
}
}
}
}
ifs.close();
}
}

write a code for checking valid email address,

i was writing a code on code blocks for checking a valid email address by checking following conditions:
1.must have at least one uppercase.
2.should have characters above 8 and less than 50
3.should have # sign
i have used 3 while loops for checking individual condition , but after entering the email address the program gets stopped . here is my code ,does anyone know what is the problem?
enter code here
#include<iostream>
using namespace std;
#include<stdio.h>
#include<conio.h>
void check_mail()
{
int i = 0;
char email[25];
int measure = 0;
cout<<" \n \n \n enter an email address ::";
gets(email);
while(email[i] != '\0')//for checking uppercsae //
{
if( (int)email[i] >= 65 && (int)email[i] <= 90)
{
measure = 1;
}
if( measure != 1)
{
cout<<"\n there is no uppercase letter in the email address ";
break;
}
}
while(email[i] != '\0') //checking # sign//
{
if((int)email[i] == 64)
{
cout<<" \n found the # character at :: "<<i<<endl;
}
}
int counter = 0;
while(email[i] != '\0')
{
counter = counter +1 ;
}
if(counter >=8 && counter <=50)
{
cout<< "\n valid number of characters are present in the mail :: ";
}
else if(counter <8)
{
cout<<" \n number of characters are less than 8 ";
}
else if(counter >=51 )
{
cout<<"\n the elements are greater than 50 ";
}
else
{
cout<<"\n enter a valid email address::";
}
}
int main()
{
cout<<" \n \n enter a email address ";
check_mail();
return 0;
}
This code below is a working and a way better implementation of your code:
#include <iostream>
#include <string>
bool check_mail(const std::string email)
{
if (email.size() < 8 || email.size() > 50) return false;
int upper_letters = 0;
for (int i = 0; i < email.size(); i++)
{
if (std::isupper(email[i])) upper_letters++;
if (email[i] == '#')
{
if (i < 8) return false;
else if (upper_letters == 0) return false;
return true;
}
}
return false;
}
int main()
{
std::cout << " \n \n Enter an email address ";
std::string email; std::cin >> email;
std::cout << check_mail(email) << std::endl;
return 0;
}
If you need to know what exactly caused the email to get rejected, you can do the following:
#include <iostream>
#include <string>
enum email_states { correct, under_char, over_char, no_upper, no_at_the_rate };
email_states check_mail(const std::string email)
{
if (email.size() < 8) return email_states::under_char;
else if (email.size() > 5) return email_states::over_char;
int upper_letters = 0;
for (int i = 0; i < email.size(); i++)
{
if (std::isupper(email[i])) upper_letters++;
if (email[i] == '#')
{
if (i < 8) return email_states::under_char;
else if (upper_letters == 0) return email_states::no_upper;
return email_states::correct;
}
}
return email_states::no_at_the_rate;
}
int main()
{
std::cout << " \n \n Enter an email address ";
std::string email; std::cin >> email;
std::cout << check_mail(email) << std::endl;
return 0;
}
For the 2'nd code, if the output is:
0 - correct
1 - under_char
2 - over_char
3 - no_upper
4 - no_at_the_rate
Also, using namespace std is considered as a bad practice. For more info on this look up to why is "using namespace std" considered as a bad practice.
You could consider using std::string and utilizing the standard library:
#include <iostream>
#include <string>
constexpr int kMinEmailCharacters = 8;
constexpr int kMaxEmailCharacters = 50;
constexpr char kAtSign = '#';
bool IsValidEmail(const std::string &email) {
auto email_length = email.length();
auto contains_uppercase = std::count_if(email.begin(), email.end(), isupper);
auto contains_at_sign = email.find(kAtSign) != std::string::npos;
return email_length > kMinEmailCharacters &&
email_length < kMaxEmailCharacters && contains_uppercase &&
contains_at_sign;
}
int main() {
std::cout << "Enter email: ";
std::string user_email;
std::cin >> user_email;
auto valid_email = IsValidEmail(user_email);
std::cout << "Valid email: " << (valid_email ? "true" : "false") << '\n';
return 0;
}
Example Usage 1:
Enter email: tejaspatil#mail.com
Valid email: false
Example Usage 2:
Enter email: Tejaspatil#mail.com
Valid email: true
There are some logic errors in the code:
in the first loop you print "there is no uppercase letter in the email address" when the first character is not an uppercase letter. You need to check all letters before you know if there was an uppercase
after the first loop, i is already at the end of the string, the next loop will not have a single iteration.
Further
use std::string
conio.h is windows only. You don't need it when you can use std::cin.
while(email[i] != '\0') is error-prone (you need to corrceclty manage the index, which your code fails to do). Use a range based loop instead
try to avoid magic numbers.
don't use C-style casts
in particular there is no need to cast the characters to int. You can compare directly to 'A' and 'Z'.
after you checked if the size of the input is <8 or >=8 && <=50 or >51 there is no other case, the else is superfluous.
Your code with this fixes:
#include<iostream>
using namespace std;
void check_mail()
{
int i = 0;
std::string email;
int measure = 0;
cout<<" \n \n \n enter an email address ::";
//gets(email);
std::cin >> email;
for (char c : email) {
if( c >= 'A' && c <= 'Z') { measure = 1; }
}
if( measure != 1) {
cout<<"\n there is no uppercase letter in the email address ";
//break;
}
for (char c : email) {
if(c == '#') {
cout<<" \n found the # character at :: "<<i<<endl;
}
}
int counter = email.size();
if(counter >=8 && counter <=50) {
cout<< "\n valid number of characters are present in the mail :: ";
}
else if(counter <8) {
cout<<" \n number of characters are less than 8 ";
}
else if(counter >=51 ) {
cout<<"\n the elements are greater than 50 ";
}
}
int main()
{
cout<<" \n \n enter a email address ";
check_mail();
return 0;
}
Produces expected output for input "Foo#bar.com":
enter a email address
enter an email address ::
found the # character at :: 0
valid number of characters are present in the mail ::
I suppose for the last output you also wanted to add email.size() to the output.
You consider using Regex for check email:
// C++ program for the above approach
#include <iostream>
#include <regex>
using namespace std;
// Function to check the email id
// is valid or not
bool isValid(const string& email)
{
// Regular expression definition
const regex pattern(
"(\\w+)(\\.|_)?(\\w*)#(\\w+)(\\.(\\w+))+");
// Match the string pattern
// with regular expression
return regex_match(email, pattern);
}
// Driver Code
int main()
{
// Given string email
string email = "mail#gmail.com";
// Function Call
bool ans = isValid(email);
// Print the result
if (ans) {
cout << email << " : "
<< "valid" << endl;
}
else {
cout << email << " : "
<< "invalid" << endl;
}
}

Converting this program from 1 function to a modular, multi-function program?

I am very new to C++ and have a program which I included below.
The program I am working on reads text from an input file and counts the number of words and number of occurrences of each letter in the text and then prints the results. My program is working fine but the problem is all code is written in the main function and I need to break it up into a couple more functions to make the program modular, but I am unsure of how to go about doing this.
I am sure this is pretty simple but I'm not sure where to start. I was thinking of implementing two void functions, one for reading / interpreting what is read from the data file and another that displays the results; and then call them both in the main function, but I'm not sure what to take as arguments for those functions.
int main()
{
// Declaring variables
char c; // char that will store letters of alphabet found in the data file
int count[26] = {0}; // array that will store the # of occurences of each letter
int words = 1; // int that will store the # of words
string s; // declaring string found in data file
// Opening input file stream
ifstream in;
in.open("word_data.txt");
// Reading text from the data file
getline(in, s);
//cout << s << endl;
// If input file fails to open, displays an error message
if (in.fail())
{
cout << "Input file did not open correctly" << endl;
}
// For loop for interpreting what is read from the data file
for (int i = 0; i < s.length(); i++) {
// Increment word count if new line or space is found
if (s[i] == ' ' || s[i] == '\n')
words++;
//If upper case letter is found, convert to lower case.
if (s[i] >= 'A' && s[i] <= 'Z')
s[i] = (tolower(s[i]));
//If the letters are found, increment the counter for each letter.
if (s[i] >= 'a' && s[i] <= 'z')
count[s[i] - 97]++;
}
// Display the words count
cout << words << " words" << endl;
// Display the count of each letter
for (int i = 0; i < 26; i++) {
if (count[i] != 0) {
c = i + 97;
cout << count[i] << " " << c << endl;
}
}
// Always close opened files
in.close();
return 0;
}
I would rewrite it like:
class FileReader {
public:
FileReader() {
// Any init logic goes here...
}
~FileReader() {
// Always close opened files.
in.close();
}
void open(std::string &filePath) {
in.open(filePath);
}
std::string readLine() {
std::string s;
getline(in, s);
return s;
}
bool hasErrors() const { // remove const if you get compile-error here.
return in.fail();
}
private:
ifstream in;
};
class LetterCounter {
public:
void process(std::string &s) {
// For loop for interpreting what is read from the data file
for (int i = 0; i < s.length(); i++) {
// Increment word count if new line or space is found
if (s[i] == ' ' || s[i] == '\n')
words++;
//If upper case letter is found, convert to lower case.
if (s[i] >= 'A' && s[i] <= 'Z')
s[i] = (tolower(s[i]));
//If the letters are found, increment the counter for each letter.
if (s[i] >= 'a' && s[i] <= 'z')
count[s[i] - 97]++;
}
}
void logResult() {
char c; // char that will store letters of alphabet found in the data file.
// Display the words count
cout << words << " words" << endl;
// Display the count of each letter
for (int i = 0; i < 26; i++) {
if (count[i] != 0) {
c = i + 97;
cout << count[i] << " " << c << endl;
}
}
}
private:
int count[26] = {0}; // array that will store the # of occurences of each letter
int words = 1; // int that will store the # of words
};
int main()
{
// Opening input file stream.
FileReader reader;
reader.open("word_data.txt");
// Reading text from the data file.
std::string s = reader.readLine();
// If input file fails to open, displays an error message
if (reader.hasErrors()) {
cout << "Input file did not open correctly" << endl;
return -1;
}
LetterCounter counter;
counter.process(s);
// Display word and letter count.
counter.logResult();
return 0;
}
Note that I did write without testing (excuse any mistake),
but this should give you a general idea how it should be.

Palindromes in C++

I'm trying to make a program that uses stacks (w pop, push, etc.) to read in a text file with lots of sentences that are taken one at a time and outputs whether each line is a palindrome or not (words that are spelled the same forwards and backwards). I believe its very close to being a completed program, but it only returns false even when the string is a palindrome. I want it to return true when the string is in fact a palindrome.
EDIT: Tried a new method with three stacks instead. Still getting a false return from bool tf every time.
int main() {
Stack s(100); // Initialize two different stacks
Stack q(100);
Stack temp(100);
string line; // String to hold each individual line of the file
char letter;
char x; // For comparisons
char y;
// open the file
ifstream input;
input.open(READFILE);
// Check that it is open/readable
if (input.fail()) {
cout << endl << "Sorry, file not available, exiting program. Press enter";
cout << endl;
cin.get(); // Grab the enter
return 0;
}
while (getline(input, line)) { // Read the file line-by-line into "line"
cout << "The line: " << line << endl;
int length = line.length(); // Sets length equal to string length
for (int i =0; i<length; i++){ // Capitalizes string
line[i] = toupper(line[i]);
}
for (int i = 0; i < length; i++) { // Loop through for every letter in the line
if (line[i] == ' ' ) {
line.erase(i,1); // Takes spaces out of the line
length--;
}
if (ispunct(line[i])){
length--;
}
if (!ispunct(line[i])){ // Removes punctuation
letter = line[i]; // Push each letter onto the stack
s.push(letter);
}
}
for (int i = 0; i < length; i++) { // Popping half the letters off of the s stack
s.pop(letter); // and pushing them onto the q stack
q.push(letter);
temp.push(letter);
}
for (int i = 0; i < length; i++) {
temp.pop(letter);
s.push(letter);
}
bool tf = true; // Pop off the top of each stack and compare
while (!s.empty()) { // them to check for a palindrome
s.pop(x);
q.pop(y);
if (x == y);
else tf = false;
}
if (tf){
cout << "is a palindrome!" << endl;
}
if (!tf) {
cout << "is NOT a palindrome" << endl;
}
}
}
for (int i = 0; i < length/2; i++) // Popping half the letters off
//of the s stack
q.push(letter); // and pushing them onto the q
//stack
}
Here you're pushing the same letter over and over again.
Even if you rewrite as the comment states it will be wrong.
if you pop half of ABBA you have BA and AB and compare B=A
you need to rethink your strategy. Maybe push half of the string to s then loop backwards from length and push to q
Like someone else mentioned, even after fixing the for loop with the "q" stack, the general strategy is not correct. In fact you don't need two stacks. (or even one stack, but you can use a stack if desired.)
You do have the right idea in comparing the back half of the letters with the front half. In general, to find a palindrome you just need to see if the string is equal to the reversed string or that the first half is equal to the back half.
You can use a stack to store the string in reverse. All you need is the stack and the string. However, there is the extra problem here in that the lines of strings contain spaces and punctuation that you want to ignore. Using the erase() method reduces the length of the string as you go, so you need a temporary variable to rebuild the formatted string at the same time as the stack. EDIT: I saw your update to accounting for the reduced length; that's great -- it can save even the use of a temp variable to hold the formatted string so that the variable string line is all that is needed.
Here is another version of your while loop that uses one stack and a temp string variable. It uses half the formatted string to compare against the top of the stack (which represents the "back" of the string).
string cleanString;
//cout << "test3";
while (getline(input, line)) { // Read the file line-
//by-line into "line"
cout << "The line read was: " << line << endl;
int length = line.length(); // Sets length equal to
//string length
for (int i =0; i<length; i++) // Capitalizes string
line[i] = toupper(line[i]);
for (int i = 0; i < length; i++) // Loop through for //every letter in the line
if ( !(line[i] == ' ' || ispunct(line[i]))) { // Ignore space & punctuation
letter = line[i]; // Push each letter onto
s.push(letter); //the stack
cleanString.push_back(letter); //and to the "cleaned" string to compare with later
//cout << cleanString << endl; //test
}
length = cleanString.length();
bool tf = true;
for (int i = 0; i < length/2; i++) { // Pop off the top of stack
s.pop(x); // to compare with front of string
if ( cleanString[i] != x ) { //not a palindrome
tf = false;
break;
}
}
if (tf){
cout << "is a palindrome!" << endl;
}
if (!tf) {
cout << "is NOT a palindrome" << endl;
}
}
But it's simpler to skip the use of the stack altogether and instead just use the temp "cleaned" string, checking for a palindrome in a for loop with two counters: one for the the front and one for the back.
So after the capitalization:
// Instead of a stack, just build a string of chars to check for a palindrome
for (int i = 0; i < length; i++)
if ( !(line[i] == ' ' || ispunct(line[i]))) {
letter = line[i]; // Push each letter onto
cleanString.push_back(letter); // a temp string
}
length = cleanString.length(); //use length of formatted string
bool tf = true;
int front = 0; // first char of string
int back = length-1; // last char of string
for (; i < length/2; front++, back--)
if ( cleanString[front] != cleanString[back] ) { //not a palindrome
tf = false;
break;
}
Another option is to use the inbuilt reverse() function in the <algorithm> header file after building the temp string:
#include <algorithm> // reverse()
string cleanString;
string reversedCleanString;
//...
// Instead of a stack, just build a string of chars to check for a palindrome
for (int i = 0; i < length; i++)
if ( !(line[i] == ' ' || ispunct(line[i])))
cleanString.push_back(line[i]);
reversedCleanString = cleanString; // store copy of string to reverse
reverse(reversedCleanString.begin(), reversedCleanString.end() ); // reverse
bool tf = true;
if ( cleanString != reversedCleanString)
tf = false;
// ...
As moooeeep mentioned the comments, using std::string's reverse iterators simplifies this even further after the capitalization:
string cleanString;
//...
// Format line to test if palindrome
for (int i = 0; i < length; i++)
if ( !(line[i] == ' ' || ispunct(line[i])))
cleanString.push_back( line[i] );
bool tf = true;
if ( cleanString != string(cleanString.rbegin(), cleanString.rend() )
tf = false;
// ...
Also, like moooeeeep mentioned, encapsulating the different parts of the while loop into their own separate functions is a good idea to make not just debugging easier but also understanding the logical flow of the problem more intuitively.
For example the while loop could look like this:
while (getline(input, line)) { // Read the file line-by-line into "line"
//echo input
cout << "The line read was: " << line << endl;
// validate/format the line
extractChars( line ); //remove space/punctuation
capitalizeString( line ); // capitalize chars for uniformity
//check if formatted line is a palindrome and output result
if ( is_palindrome( line ) )
cout << "Line IS a palindrome << endl;
else
cout << "Line IS NOT a palindrome << endl;
}