So my task is to fill out my function to work with a test driver that feeds it a random string during every run. For this function I have to convert the first character of every word to a capital and everything else must be lower.
It mostly works but the issue i'm having with my code is that it won't capitalize the very first character and if there is a period before the word like:
.word
The 'w' in this case would remain lower.
Here is my source:
void camelCase(char line[])
{
int index = 0;
bool lineStart = true;
for (index;line[index]!='\0';index++)
{
if (lineStart)
{
line[index] = toupper(line[index]);
lineStart = false;
}
if (line[index] == ' ')
{
if (ispunct(line[index]))
{
index++;
line[index] = toupper(line[index]);
}
else
{
index++;
line[index] = toupper(line[index]);
}
}else
line[index] = tolower(line[index]);
}
lineStart = false;
}
Here's a solution that should work and is a bit less complicated in my opinion:
#include <iostream>
#include <cctype>
void camelCase(char line[]) {
bool active = true;
for(int i = 0; line[i] != '\0'; i++) {
if(std::isalpha(line[i])) {
if(active) {
line[i] = std::toupper(line[i]);
active = false;
} else {
line[i] = std::tolower(line[i]);
}
} else if(line[i] == ' ') {
active = true;
}
}
}
int main() {
char arr[] = "hELLO, wORLD!"; // Hello, World!
camelCase(arr);
std::cout << arr << '\n';
}
The variable active tracks whether the next letter should be transformed to an uppercase letter. As soon as we have transformed a letter to uppercase form, active becomes false and the program starts to transform letters into lowercase form. If there's a space, active is set to true and the whole process starts again.
Solution using std::string
void toCamelCase(std::string & s)
{
char previous = ' ';
auto f = [&](char current){
char result = (std::isblank(previous) && std::isalpha(current)) ? std::toupper(current) : std::tolower(current);
previous = current;
return result;
};
std::transform(s.begin(),s.end(),s.begin(),f);
}
Related
So writing a palindrome with pointers and boolean. I have it working with a single word but then I began building it to work with a sentence. The problem is I am unsure how to keep the new modified sentence after making it lowercase and getting rid of the spaces for it to return whether it is or isn't a palindrome. It keeps returning the palindrome as false and when I went to check why I see that the program ignores the modification and kept the original string. I can't use "&" on the parameter as I tested it out. Any hints or takes on what I can do to keep the new modified string?
int main()
{
userInput();
return 0;
}
void userInput()
{
char str[90];
std::cout<<"Please enter a string to check if it is a palindrome: ";
std::cin.getline(str, 90);
modifyString(str);
}
void modifyString(char *string)
{
int count = 0;
for (int i=0; i<strlen(string); i++)
{
putchar(tolower(string[i]));
}
for (int i = 0; string[i]; i++)
{
if (string[i] != ' ')
{
string[count++] = string[i];
}
}
string[count] = '\0';
std::cout<<string<<std::endl;
results(string);
}
bool checkPalindrome(char *string)
{
char *begin;
char *end;
begin = string;
end = (string + strlen(string)-1);
while(begin != end)
{
if ((*begin) == (*end))
{
begin ++;
end--;
}
else
{
return false;
}
}
return true;
}
void results(char *string)
{
bool isItPalindrome;
isItPalindrome = checkPalindrome(string);
if( isItPalindrome == true)
{
std::cout<<"\nCongrats, the string is a palindrome!";
}
else
{
std::cout<<"\nThis string is not a palindrome.";
}
}
For starters this definition of main
int main()
{
userInput();
return 0;
}
does not make a sense. According to the function name main the function should perform the main task that is to output whether the entered sentence is a palindrome or not.
This for loop
for (int i=0; i<strlen(string); i++)
{
putchar(tolower(string[i]));
}
does nothing useful. It just outputs the string in the lower case.
This statement
end = (string + strlen(string)-1);
can invoke undefined behavior if an empty string was passed.
This while loop
while(begin != end)
{
if ((*begin) == (*end))
{
begin ++;
end--;
}
else
{
return false;
}
}
also can invoke undefined behavior for a string containing an even number ofo characters because after this if statement
if ((*begin) == (*end))
{
begin ++;
end--;
}
if the two adjacent characters are equal then begin after incrementing will be greater than end after its decrementing. And as a result the loop will continue its iteration.
In general the approach when the original string is changed is just a bad approach.
Your program has too many functions. It is enough to write one function that will determine whether the passed string is a palindrome or not.
Here is a demonstrative program.
#include <iostream>
#include <cstring>
#include <cctype>
bool checkPalindrome( const char *s )
{
const char *t = s + std::strlen( s );
do
{
while ( s != t && std::isspace( ( unsigned char )*s ) ) ++ s;
while ( s != t && std::isspace( ( unsigned char )*--t ) );
} while ( s != t &&
std::tolower( ( unsigned char )*s ) == tolower( ( unsigned char ) *t ) &&
++s != t );
return s == t;
}
int main()
{
const size_t N = 100;
char s[N] = "";
std::cout << "Please enter a string to check if it is a palindrome: ";
std::cin.getline( s, N );
std::cout << '\n';
if ( checkPalindrome( s ) )
{
std::cout << "Congrats, the string is a palindrome!\n";
}
else
{
std::cout << "This string is not a palindrome.\n";
}
return 0;
}
Its output might look like
Please enter a string to check if it is a palindrome: 1 23 456 6 54 321
Congrats, the string is a palindrome!
Okay, I solved it!
As one of the users on here brought up a point that my lowercase did not modify the string and only prints it out. I try my best to solve the problem and I think I found the solution and everything works perfectly fine. comment back to debug it if you like to see how it looks but what I did was create a for loop again for the lower case but made another pointer with it. here how it looks.
for (char *pt = string; *pt != '\0'; ++pt)
{
*pt = std::tolower(*pt);
++pt;
}
Now that definitely changes the string into a lower case and keeps it as a lower case.
so now the modified function looks like this and ready to take any sentence palindrome you give it. Example: A nUt fOr a jAr of tUNa. We make this all lowercase and take out space and boom palindrome and return true.
void modifyString(char *string)
{
int count = 0;
for (char *pt = string; *pt != '\0'; ++pt)
{
*pt = std::tolower(*pt);
++pt;
}
for (int i = 0; string[i]; i++)
{
if (string[i] != ' ')
{
string[count++] = string[i];
}
}
string[count] = '\0';
//take out the forward slash below to see how it looks after being modified
// std::cout<<std::endl<<string<<std::endl;
results(string);
}
I have a string like escape new lines\n. When printing it, it doesn't replace \n with a new line, it just prints escape new lines\n. I'm iterating over this std::string, and I want to replace \n with it's actual value:
bool seenSlash = false;
for (int index = 0; index < text.length(); ++index) {
if (text[index] == '\\') {
seenSlash = true;
continue;
}
if (seenSlash) {
// what to do here?
seenSlash = false;
}
}
It's not just \n, I want to support all of those escaping symbols. How can I do it?
WHat you could do is a switch statement or using a constant std::map<char,char>:
if (seenSlash) {
bool replace = false;
switch(text[index]) {
case 'n':
replace = true;
text[index-1] = '\n';
break;
case 't':
replace = true;
text[index-1] = '\t';
break;
// ... etc.
}
if(replace) {
text.erase(text.begin() + index); // erase the cuurent char, the
// backslash char was replaced
--index; // adapt the index for the next iteration
}
seenSlash = false;
}
const std::map<char,char> escaped_chars = {
{ 'n', '\n' } ,
{ 't', '\t' } ,
{ 'a', '\a' } ,
// ... etc.
};
if (seenSlash) {
bool replace = escaped_chars.find(text[i]) != escaped_chars.end();
if(replace) {
text[index - 1] = escaped_chars[text[i]];
text.erase(text.begin() + index); // erase the cuurent char, the
// backslash char was replaced
--index; // adapt the index for the next iteration
}
seenSlash = false;
}
I have written a simple tokenizer that will split a command line into seperate lines each containing a single word. I am trying to ...
Make the program close if the first word of a command line is "quit"
Recognize instructions such as "Pickup", "Save", and "Go" in which the compiler will then look to the next token.
My idea has been to use a simple switch with cases to check for these commands, but I cannot figure out where to place it.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char command[256];
int commandIndex;
char token[32];
int isWhiteSpace(char character) {
if (character == ' ') {
return 1;
}
else if(character == '\t') {
return 1;
}
else if(character < ' ') {
return 1;
}
else {
return 0;
}
} char* getToken() {
int index = 0; // Skip white spaces
while(commandIndex<256 && isWhiteSpace(command[commandIndex])) {
commandIndex ++;
} // If at end of line return empty token
if(commandIndex>=256) {
token[0] = 0;
return token;
} // Capture token
while(commandIndex<256 && !isWhiteSpace(command[commandIndex])) {
token[index] = command[commandIndex];
index++;
commandIndex ++;
}
token[index] = 0;
return token;
}
void main() {
printf("Zeta - Version 2.0\n");
while(1) {
printf("Command: ");
gets_s(command);
commandIndex = 0;
char* token = getToken();
while (strcmp(token,"") != 0) {
printf("%s\n", token);
token = getToken();
}
}
}
A little reorganization of the loop you have in main will do it.
int main() {
printf("Zeta - Version 2.0\n");
bool done = false;
while (!done) {
printf("Command: ");
gets_s(command);
commandIndex = 0;
char* token = getToken();
if (strcmp(token, "quit") == 0) {
done = true;
} else if (strcmp(token, "pickup") == 0) {
doPickup();
} else if (strcmp(token, "save") == 0) {
char * filename = getToken();
doSave(filename);
} ...
}
return 0;
}
You can't use a switch statement with strings, so you just use a bunch of if ... else if ... statements to check for each command. There are other approaches, but this one required the fewest changes from the code you already have.
In the example, under the handling for "save" I showed how you can just call getToken again to get the next token on the same command line.
(Note that I also fixed the return value for main. Some compilers will let you use void, but that's not standard so it's best if you don't do that.)
So essentially what I want to do is erase all the whitespace from an std::string object, however excluding parts within speech marks and quote marks (so basically strings), eg:
Hello, World! I am a string
Would result in:
Hello,World!Iamastring
However things within speech marks/quote marks would be ignored:
"Hello, World!" I am a string
Would result in:
"Hello, World!"Iamastring
Or:
Hello,' World! I' am a string
Would be:
Hello,' World! I'amastring
Is there a simple routine to perform this to a string, either one build into the standard library or an example of how to write my own? It doesn't have to be the most efficient one possible, as it will only be run once or twice every time the program runs.
No, there is not such a routine ready.
You may build your own though.
You have to loop over the string and you want to use a flag. If the flag is true, then you delete the spaces, if it is false, you ignore them. The flag is true when you are not in a part of quotes, else it's false.
Here is a naive, not widely tested example:
#include <string>
#include <iostream>
using namespace std;
int main() {
// we will copy the result in new string for simplicity
// of course you can do it inplace. This takes into account only
// double quotes. Easy to extent do single ones though!
string str("\"Hello, World!\" I am a string");
string new_str = "";
// flags for when to delete spaces or not
// 'start' helps you find if you are in an area of double quotes
// If you are, then don't delete the spaces, otherwise, do delete
bool delete_spaces = true, start = false;
for(unsigned int i = 0; i < str.size(); ++i) {
if(str[i] == '\"') {
start ? start = false : start = true;
if(start) {
delete_spaces = false;
}
}
if(!start) {
delete_spaces = true;
}
if(delete_spaces) {
if(str[i] != ' ') {
new_str += str[i];
}
} else {
new_str += str[i];
}
}
cout << "new_str=|" << new_str << "|\n";
return 0;
}
Output:
new_str=|"Hello, World!"Iamastring|
Here we go. I ended up iterating through the string, and if it finds either a " or a ', it will flip the ignore flag. If the ignore flag is true and the current character is not a " or a ', the iterator just increments until it either reaches the end of the string or finds another "/'. If the ignore flag is false, it will remove the current character if it's whitespace (either space, newline or tab).
EDIT: this code now supports ignoring escaped characters (\", \') and making sure a string starting with a " ends with a ", and a string starting with a ' ends with a ', ignoring anything else in between.
#include <iostream>
#include <string>
int main() {
std::string str("I am some code, with \"A string here\", but not here\\\". 'This sentence \" should not end yet', now it should. There is also 'a string here' too.\n");
std::string::iterator endVal = str.end(); // a kind of NULL pointer
std::string::iterator type = endVal; // either " or '
bool ignore = false; // whether to ignore the current character or not
for (std::string::iterator it=str.begin(); it!=str.end();)
{
// ignore escaped characters
if ((*it) == '\\')
{
it += 2;
}
else
{
if ((*it) == '"' || (*it) == '\'')
{
if (ignore) // within a string
{
if (type != endVal && (*it) == (*type))
{
// end of the string
ignore = false;
type = endVal;
}
}
else // outside of a string, so one must be starting.
{
type = it;
ignore = true;
}
it++;
//ignore ? ignore = false : ignore = true;
//type = it;
}
else
{
if (!ignore)
{
if ((*it) == ' ' || (*it) == '\n' || (*it) == '\t')
{
it = str.erase(it);
}
else
{
it++;
}
}
else
{
it++;
}
}
}
}
std::cout << "string now is: " << str << std::endl;
return 0;
}
Argh, and here I spent time writing this (simple) version:
#include <cctype>
#include <ciso646>
#include <iostream>
#include <string>
template <typename Predicate>
std::string remove_unquoted_chars( const std::string& s, Predicate p )
{
bool skip = false;
char q = '\0';
std::string result;
for (char c : s)
if (skip)
{
result.append( 1, c );
skip = false;
}
else if (q)
{
result.append( 1, c );
skip = (c == '\\');
if (c == q) q = '\0';
}
else
{
if (!std::isspace( c ))
result.append( 1, c );
q = p( c ) ? c : '\0';
}
return result;
}
std::string remove_unquoted_whitespace( const std::string& s )
{
return remove_unquoted_chars( s, []( char c ) -> bool { return (c == '"') or (c == '\''); } );
}
int main()
{
std::string s;
std::cout << "s? ";
std::getline( std::cin, s );
std::cout << remove_unquoted_whitespace( s ) << "\n";
}
Removes all characters identified by the given predicate except stuff inside a single-quoted or double-quoted C-style string, taking care to respect escaped characters.
you may use erase-remove idiom like this
#include <string>
#include <iostream>
#include <algorithm>
int main()
{
std::string str("\"Hello, World!\" I am a string");
std::size_t x = str.find_last_of("\"");
std::string split1 = str.substr(0, ++x);
std::string split2 = str.substr(x, str.size());
split1.erase(std::remove(split1.begin(), split1.end(), '\\'), split1.end());
split2.erase(std::remove(split2.begin(), split2.end(), ' '), split2.end());
std::cout << split1 + split2;
}
I have this function sentanceParse with a string input which returns a list. The input might be something like "Hello my name is Anton. What's your name?" and then the return value would be a list containing "Hello my name is Anton" and "What's your name?". However, this is not what happens. It seems as if the whitespaces in the sentences are treated like a separator and therefore the return is rather "Hello", "my", "name" etc instead of what I expected.
How would you propose I solve this?
As I am not a 100% sure the problem does not lie within my code, I will add that to the post as well:
Main:
list<string> mylist = sentanceParse(textCipher);
list<string>::iterator it;
for(it = mylist.begin(); it != mylist.end(); it++){
textCipher = *it;
cout << textCipher << endl; //This prints out the words separately instead of the entire sentances.
sentanceParse:
list<string> sentanceParse(string strParse){
list<string> strList;
int len = strParse.length();
int pos = 0;
int count = 0;
for(int i = 0; i < len; i++){
if(strParse.at(i) == '.' || strParse.at(i) == '!' || strParse.at(i) == '?'){
if(i < strParse.length() - 1){
while(i < strParse.length() - 1 && (strParse.at(i+1) == '.' || strParse.at(i+1) == '!' || strParse.at(i+1) == '?')){
if(strParse.at(i+1) == '?'){
strParse.replace(i, 1, "?");
}
strParse.erase(i+1, 1);
len -= 1;
}
}
char strTemp[2000];
int lenTemp = strParse.copy(strTemp, i - pos + 1, pos);
strTemp[lenTemp] = '\0';
std::string strAdd(strTemp);
strList.push_back(strAdd);
pos = i + 1;
count ++;
}
}
if(count == 0){
strList.push_back(strParse);
}
return strList;
}
Your implementation of sentence parse is wrong, here is a simpler correct solution.
std::list<std::string> sentence_parse(const std::string &str){
std::string temp;
std::list<std::string> t;
for(int x=0; x<str.size();++x){
if(str[x]=='.'||str[x]=='!'||str[x]=='?'){
if(temp!="")t.push_back(temp);//Handle special case of input with
//multiple punctuation Ex. Hi!!!!
temp="";
}else temp+=str[x];
}
return t;
}
EDIT:
Here is a full example program using this function. Type some sentences in your console, press enter and it will spit the sentences out with a newline separating them instead of punctuation.
#include <iostream>
#include <string>
#include <list>
std::list<std::string> sentence_parse(const std::string &str){
std::string temp;
std::list<std::string> t;
for(int x=0; x<str.size();++x){
if(str[x]=='.'||str[x]=='!'||str[x]=='?'){
if(temp!="")t.push_back(temp);//Handle special case of input with
//multiple punctuation Ex. Hi!!!!
temp="";
}else temp+=str[x];
}
return t;
}
int main (int argc, const char * argv[])
{
std::string s;
while (std::getline(std::cin,s)) {
std::list<std::string> t= sentence_parse(s);
std::list<std::string>::iterator x=t.begin();
while (x!=t.end()) {
std::cout<<*x<<"\n";
++x;
}
}
return 0;
}
// This function should be easy to adapt to any basic libary
// this is in Windows MFC
// pass in a string, a char and a stringarray
// returns an array of strings using char as the separator
void tokenizeString(CString theString, TCHAR theToken, CStringArray *theParameters)
{
CString temp = "";
int i = 0;
for(i = 0; i < theString.GetLength(); i++ )
{
if (theString.GetAt(i) != theToken)
{
temp += theString.GetAt(i);
}
else
{
theParameters->Add(temp);
temp = "";
}
if(i == theString.GetLength()-1)
theParameters->Add(temp);
}
}