Checking both ends of a string not working - c++

I am trying to the beginning of a string and the end. If the word has an uppercase letter we change it to lowercase. If the word has a space or '"' we erase the character. The first recursive call it should check and see that the end of the string has a capital letter and it should change it to lowercase. However when I output word[word.size()] it outputs a blank space, but it I output word[word.size() - 1] it will output the letter that I am looking for. I wasn't sure what the blank space is and how I should handle it as I don't want it in my string because it is causing comparison issues.
bool checkPalindrome(string word){
if (isupper(word[0]))
{
word[0] = tolower(word[0]);
}
if (isupper(word[word.size()]))
{
word[word.size()] = tolower(word[word.size()]);
}
//check if there is a space or "" if there is then delete that position from the string
if (word[0] == ' ' || word[0] == '"')
{
word.erase(1);
}
if (word[word.size()] == ' ' || word[word.size()] == '"')
{
word.pop_back();
}
if (word.size() > 1)
{
if (word[0] == word[word.size()])
{
word = word.substr(1, word.size() - 2);
return checkPalindrome(word, count);
}
else
{
return false;
}
}
else
{
return false;
}
int main()
{
ifstream inFile;
bool check = false;
string temp = "";
int count = 0;
vector<string> vect;
//Reading from a file line by line
inFile.open("words.txt");
if (inFile.is_open())
{
while (getline(inFile, temp))
{
vect.push_back(temp);
}
}
inFile.close();
for (auto i = 0; i < vect.size(); i++)
{
count = vect[i].size();
check = checkPalindrome(vect[1], count);
if (check == true)
{
cout << vect[i] << ", is a palindrome!\n";
}
else
{
cout << vect[i] << ", is not a palindrome.\n";
}
}
} return 0;

If the size of the string is 4, then there are only 4 elements: 0, 1, 2, and 3. There is no fifth element, so you cannot access element number four.
If a string's length is five:
Zero is the first element.
One is the second element.
Two is the third element.
Three is fourth element.
The fifth element is, of course Leeloo, which is not a character in the string. If a string's length is four, you should not attempt to access the fifth element (at least, not without her permission).
Ecto gamat.

Related

how to split a sentence into strings using recursion in c++

string strings[10];
void split(string s){
int curr=0,start=0,end=0,i=0;
while(i<=len(s)){
if(s[i]==' ' or i == len(s)){
end = i;
string sub;
sub.append(s,start,end-start);
strings[curr] = sub;
start = end + 1;
curr += 1 ;
}
i++;
}
}
for example if the input is " computer laptop screen desktop mouse " then the output string should be:
computer
laptop
screen
desktop
mouse
I have successfully tried using loops but failed using recursion,
can anyone help me solve split() using recursion.
Thank you
This solution assumes you want only words from the string to enter your array and that you want to split on some predetermined string delimiter like <space>" " or <double-dash>"--".
If you need to keep the void function signature, here is one:
void split_rec(string str_array[], size_t arr_index,
string s, string delimiter) {
if (s == "") {
return;
}
size_t str_index = s.find(delimiter);
string word = s.substr(0, str_index);
if (word != "") {
str_array[arr_index++] = word;
}
// find type functions return string::npos if they don't find.
str_index = s.find_first_not_of(delimiter, str_index);
if (str_index == string::npos) {
return;
}
return split_rec(str_array, arr_index, s.substr(str_index), delimiter);
}
But I would recommend returning the size of the array so you communicate what the function is doing more accurately. Like this:
size_t split_rec(string str_array[], size_t arr_index,
string s, string delimiter) {
if (s == "") {
return arr_index;
}
size_t str_index = s.find(delimiter);
string word = s.substr(0, str_index);
if (word != "") {
str_array[arr_index++] = word;
}
str_index = s.find_first_not_of(delimiter, str_index);
if (str_index == string::npos) {
return arr_index;
}
return split_rec(str_array, arr_index, s.substr(str_index), delimiter);
}
Then the call is like this:
string strings[10];
// I left some extra spaces in this string.
string str = " computer laptop screen desktop mouse ";
size_t strings_len = split_rec(strings, 0, str, " ");
cout << "Array is length " << strings_len << endl;
for (size_t i = 0; i < strings_len; i++) {
cout << strings[i] << endl;
}
Array is length 5
computer
laptop
screen
desktop
mouse

How do I parse comma-delimited string in C++ with some elements being quoted with commas?

I have a comma-delimited string that I want to store in a string vector. The string and vectors are:
string s = "1, 10, 'abc', 'test, 1'";
vector<string> v;
Ideally I want the strings 'abc' and 'test, 1' to be stored without the single quotes as below, but I can live with storing them with single quotes:
v[0] = "1";
v[1] = "10";
v[2] = "abc";
v[3] = "test, 1";
bool nextToken(const string &s, string::size_type &start, string &token)
{
token.clear();
start = s.find_first_not_of(" \t", start);
if (start == string::npos)
return false;
string::size_type end;
if (s[start] == '\'')
{
++start;
end = s.find('\'', start);
}
else
end = s.find_first_of(" \t,", start);
if (end == string::npos)
{
token = s.substr(start);
start = s.size();
}
else
{
token = s.substr(start, end-start);
if ((s[end] != ',') && ((end = s.find(',', end + 1)) == string::npos))
start = s.size();
else
start = end + 1;
}
return true;
}
string s = "1, 10, 'abc', 'test, 1'", token;
vector<string> v;
string::size_type start = 0;
while (nextToken(s, start, token))
v.push_back(token);
Demo
What you need to do here, is make yourself a parser that parses as you want it to. Here I have made a parsing function for you:
#include <string>
#include <vector>
using namespace std;
vector<string> parse_string(string master) {
char temp; //the current character
bool encountered = false; //for checking if there is a single quote
string curr_parse; //the current string
vector<string>result; //the return vector
for (int i = 0; i < master.size(); ++i) { //while still in the string
temp = master[i]; //current character
switch (temp) { //switch depending on the character
case '\'': //if the character is a single quote
if (encountered) encountered = false; //if we already found a single quote, reset encountered
else encountered = true; //if we haven't found a single quote, set encountered to true
[[fallthrough]];
case ',': //if it is a comma
if (!encountered) { //if we have not found a single quote
result.push_back(curr_parse); //put our current string into our vector
curr_parse = ""; //reset the current string
break; //go to next character
}//if we did find a single quote, go to the default, and push_back the comma
[[fallthrough]];
default: //if it is a normal character
if (encountered && isspace(temp)) curr_parse.push_back(temp); //if we have found a single quote put the whitespace, we don't care
else if (isspace(temp)) break; //if we haven't found a single quote, trash the whitespace and go to the next character
else if (temp == '\'') break; //if the current character is a single quote, trash it and go to the next character.
else curr_parse.push_back(temp); //if all of the above failed, put the character into the current string
break; //go to the next character
}
}
for (int i = 0; i < result.size(); ++i) {
if (result[i] == "") result.erase(result.begin() + i);
//check that there are no empty strings in the vector
//if there are, delete them
}
return result;
}
This parses your string as you want it to, and returns a vector. Then, you can use it in your program:
#include <iostream>
int main() {
string s = "1, 10, 'abc', 'test, 1'";
vector<string> v = parse_string(s);
for (int i = 0; i < v.size(); ++i) {
cout << v[i] << endl;
}
}
and it properly prints out:
1
10
abc
test, 1
A proper solution would require a parser implementation. If you need a quick hack, just write a cell reading function (demo). The c++14's std::quoted manipulator is of great help here. The only problem is the manipulator requires a stream. This is easily solved with istringstream - see the second function. Note that the format of your string is CELL COMMA CELL COMMA... CELL.
istream& get_cell(istream& is, string& s)
{
char c;
is >> c; // skips ws
is.unget(); // puts back in the stream the last read character
if (c == '\'')
return is >> quoted(s, '\'', '\\'); // the first character of the cell is ' - read quoted
else
return getline(is, s, ','), is.unget(); // read unqoted, but put back comma - we need it later, in get function
}
vector<string> get(const string& s)
{
istringstream iss{ s };
string cell;
vector<string> r;
while (get_cell(iss, cell))
{
r.push_back( cell );
char comma;
iss >> comma; // expect a cell separator
if (comma != ',')
break; // cell separator not found; we are at the end of stream/string - break the loop
}
if (char c; iss >> c) // we reached the end of what we understand - probe the end of stream
throw "ill formed";
return r;
}
And this is how you use it:
int main()
{
string s = "1, 10, 'abc', 'test, 1'";
try
{
auto v = get(s);
}
catch (const char* e)
{
cout << e;
}
}

Loop to split string by delimiter not respecting all deliminators

I'm splitting an std::string into words and append each to a doubly linked list. I've written an append_word method to add to the list, which I've verified is not the issue (adding words individually through this method is perfectly fine). My function is as follows:
int set_text(std::string text) {
u_int i, word_start, word_end;
i = word_start = word_end = 0;
while (i <= text.length()) {
if (text[i] == ' ' || text[i] == '\0') {
word_end = i;
std::string substr = text.substr(word_start, word_end);
// empty string argument to append_word has no effect
if (append_word("", substr) < 0)
return -1;
i = word_start = ++word_end;
} else {
i++
}
}
return 0;
}
However, calling this on "Lorem ipsum dolor" results in a first word of "Lorem", and a second word of "ipsum dolor" before a third word of "dolor". Why is the second space only treated as a delimiter in the third word but not the second?

Getline Randomly Stops Working

I am trying to write a very basic program to read in a CSV and display it. The function, as you can see below, is a very basic one. It uses getline to read in each row (until the newline chracter) before displaying each cell. It was working fine and I was finetuning the loop to display each cell when getline simply stopped working. Without changing any of the code to do with getline, I compiled it and it would not read from the file. row is always empty. However, the ifstream is valid because the "test" string (which I inserted in response) reads in from sheet fine. Can anyone help me?
PS: I have had similar problems with getline before. Is it something to do with my system? It seems to work on and off
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <string>
using namespace std;
int main(int argc, char *argv[])
{
ifstream sheet( "StockTake.csv", ios::app );
if ( ! sheet.is_open() )
{
cout << "Cannot Open File!\n";
return 0;
}
else
{
cout << "Opened File\n";
}
//Now print each row (find the newline chracter at the end of each line)
/*string test;
sheet >> test;
cout << test;*/
for ( string row; ! sheet.eof(); getline(sheet, row, '\n') )
{
if (row == "")
{
cout << "Bad Read. Exiting\n";
return 0;
}
//Print out each row with a tab space between each cell
string cell;
//find the beginning and the end of each cell
for (int i = 0, j = 0; ; )
{
/*Checks if the cell is enclosed in quotes
The first time, j == i hence the -2 and +2
the +2 is required to "skip" out the apostrophes if
they are found*/
if (row.at(i) == '"')
{
i++;
j = i-2;
do
{
j = row.find('"', j+2);
} while (row.at(j+1) == '"');
/*Check for the "" apostrophe sign*/
}
else
{
j = row.find(',', i);
}
/*Print the Cell*/
if (j == string::npos) //if this is the last cell
{
cell = row.substr(i, row.size() - i);
}
else if ( j-i != 0)
{
cell = row.substr(i, j-i);
}
else
{
cell = "";
}
/*Check for the "" apostrophe sign, and replace
with " for each instance*/
if ( cell.find("\"\"", 0) != string::npos )
{
int pos = -2; //must start at zero
do
{
//Skips out the "current" apostrophe
//if there are more than one
pos = cell.find("\"\"", pos+2);
cell = cell.substr(0, pos) +
cell.substr(pos+1, cell.size()- (pos+1) );
} while (cell.find("\"\"", pos+2) != string::npos);
}
/*Display the Cell, only the space will be displayed
if the cell is empty*/
cout << cell << " ";
/*Find the next cell*/
if ( row.at(j) == '"' )
{
i = j+2;
}
else
{
i = j+1;
}
}
cout << "\n"; //Print newline character at the end of each line
}
return 0;
}

C++ find special char and move to the end of a string

I am currently a student taking C++. My issue is that my nested if statement does not find the special chars if they are at the end of the word. From what I can tell, it does not run the function at all. If anyone has any idea what is wrong that will be great!
#include <iostream>
#include <string>
using namespace std;
bool isVowel(char ch);
string rotate(string pStr);
string pigLatinString(string pStr);
bool specialChar(char ch);
int main() {
string str, str2, pigsentence, finalsentence, orgstr, end;
int counter, length, lengtho;
counter = 1;
cout << "Enter a string: ";
getline (cin, str);
cout << endl;
orgstr = str;
//Add in option to move special chars
string::size_type space;
do {
space = str.find(' ', 0); //Finds the space(s)
if(space != string::npos){
str2 = str.substr(0, space); //Finds the word
if(specialChar(str[true])) { //Finds special char
end = str.substr(space - 1); //Stores special char as end
cout << end << endl; //Testing end
str.erase(space - 1); //Erases special car
}
str.erase(0, space + 1); //Erases the word plus the space
pigsentence = pigLatinString(str2); //converst the word
finalsentence = finalsentence + " " + pigsentence + end; //Adds converted word to final string
}else {
length = str.length();
str2 = str.substr(0, length); //Finds the word
if(specialChar(str[true])) { //Finds special char
end = str.substr(space - 1); //Stores special char as end
cout << end << endl; //Testing end
str.erase(space - 1); //Erases special car
}
str.erase(0, length); //Erases the word
pigsentence = pigLatinString(str2); //converst the word
finalsentence = finalsentence + " " + pigsentence + end; //Adds converted word to final string
counter = 0;
}
}while(counter != 0); //Loops until counter == 0
cout << "The pig Laten form of " << orgstr << " is: " << finalsentence << endl;
return 0;
}
The function that lists the specialChars is below
bool specialChar(char ch) {
switch(ch) {
case ',':
case ':':
case ';':
case '.':
case '?':
case '!':
return true;
default:
return false;
}
}
I do have other functions but they are working and just convert a word to piglatin.
your isSpecialChar takes a character as argument so str[index] would be something you could pass but instead you write str[true] which is not correct. If you want to check if there is a specialChar in your string you need to loop through the whole string and check each character.
It seems as if you want to split up a string into words so you could write something like this
char Seperator = ' ';
std::istringstream StrStream(str);
std::string Token;
std::vector<std::string> tokens;
while(std::getline(StrStream, Token, Seperator))
{
tokens.push_back(Token);
}
now that you have the words in a vector you can do whatever what you want
with them like checking for a special char
for (int i = 0; i < tokens.size(); ++i)
{
std::string& s = tokens[i];
for (int j = 0; j < s.length(); ++j)
{
if ( specialChar( s[j] )
{
...do whatever...
}
}
}
You're using true as your array index when passing arguments to the specialChar() function! Surely that isn't what you meant to do. Fix that and you might see some improvement.
Think of the function call broken down a little, like this, to help you keep track of the types:
// takes a char, returns a bool, so....
bool specialChar( char in )
{ ... }
for( int i = 0; i < str.size(); i++ )
{
char aChar = str[i];
// ...pass in a char, and receive a bool!
bool isSpecial = specialChar(aChar);
if( isSpecial )
{
...
}
}
There's generally no harm in writing the code in a way that makes it clearer to you what's going on, when compiled and optimised it will all likely be the same.