"\b" is added to the string as a character - c++

In the below code the "\b" removes a char from the string, but it increases its size as if the char could be inside it but not visible.
while (true) {
c = _getch();
if (c=='\r') {break;}
else if (c=='\b') { cout<<"\b"<<" "<<"\b"; s+="\b \b"; }
else {cout<<"*"; s=s+c;}
}
For instance the the size of this string (abc"\b"d), "c is removed and replaced by d", is still 5.
I would like to know how to efficiently handle the backspace in this circumstance.

If you are reading character by character into a string, you could do something like this:
std::string mystr;
while (true) {
c = _getch();
if (c=='\r') {break;}
if(c == '\b')
{
// This will remove last character from your string
if(mystr.size () > 0)
{
cout<<"\b"<<" "<<"\b";
mystr.resize (mystr.size () - 1);
// or mystr.pop_back() in C++11
}
}
else
{
cout<<"*";
mystr += c;
}
}

You need to "physically" remove the last character from the string when you get a backspace:
while (true) {
c = _getch();
if (c=='\r') {
break;
}
if (c=='\b') {
cout<<"\b"<<" "<<"\b";
if (s.length() > 0) {
s = s.substring(0, s.length()-1);
}
}
else {cout<<"*"; s=s+c;}
}
As an optimization, we can trim s instead of reassigning, as suggested by Jason:
s.resize(s.size() -1);
(While we're at it, we could save s.length() (or s.size()) into a local variable to avoid the extra call - assuming the compiler, knowing about std::string, doesn't do it already).

for(char c=_getch(); c!='\r'; c=_getch())
if(c=='\b')
mystr.pop_back();
else
mystr.push_back(c);

Related

Checking both ends of a string not working

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.

C++ separate string by selected commas

I was reading the following question Parsing a comma-delimited std::string on how to split a string by a comma (Someone gave me the link from my previous question) and one of the answers was:
stringstream ss( "1,1,1,1, or something else ,1,1,1,0" );
vector<string> result;
while( ss.good() )
{
string substr;
getline( ss, substr, ',' );
result.push_back( substr );
}
But what if my string was like the following, and I wanted to separate values only by the bold commas and ignoring what appears inside <>?
<a,b>,<c,d>,,<d,l>,
I want to get:
<a,b>
<c,d>
"" //Empty string
<d,l>
""
Given:<a,b>,,<c,d> It should return: <a,b> and "" and <c,d>
Given:<a,b>,<c,d> It should return:<a,b> and <c,d>
Given:<a,b>, It should return:<a,b> and ""
Given:<a,b>,,,<c,d> It should return:<a,b> and "" and "" and <c,d>
In other words, my program should behave just like the given solution above separated by , (Supposing there is no other , except the bold ones)
Here are some suggested solution and their problems:
Delete all bold commas: This will result in treating the following 2 inputs the same way while they shouldn't
<a,b>,<c,d>
<a,b>,,<c,d>
Replace all bold commas with some char and use the above algorithm: I can't select some char to replace the commas with since any value could appear in the rest of my string
Adding to #Carlos' answer, apart from regex (take a look at my comment); you can implement the substitution like the following (Here, I actually build a new string):
#include <algorithm>
#include <iostream>
#include <string>
int main() {
std::string str;
getline(std::cin,str);
std::string str_builder;
for (auto it = str.begin(); it != str.end(); it++) {
static bool flag = false;
if (*it == '<') {
flag = true;
}
else if (*it == '>') {
flag = false;
str_builder += *it;
}
if (flag) {
str_builder += *it;
}
}
}
Why not replace one set of commas with some known-to-not-clash character, then split it by the other commas, then reverse the replacement?
So replace the commas that are inside the <> with something, do the string split, replace again.
I think what you want is something like this:
vector<string> result;
string s = "<a,b>,,<c,d>"
int in_string = 0;
int latest_comma = 0;
for (int i = 0; i < s.size(); i++) {
if(s[i] == '<'){
result.push_back(s[i]);
in_string = 1;
latest_comma = 0;
}
else if(s[i] == '>'){
result.push_back(s[i]);
in_string = 0;
}
else if(!in_string && s[i] == ','){
if(latest_comma == 1)
result.push_back('\n');
else
latest_comma = 1;
}
else
result.push_back(s[i]);
}
Here is a possible code that scans a string one char at a time and splits it on commas (',') unless they are masked between brackets ('<' and '>').
Algo:
assume starting outside brackets
loop for each character:
if not a comma, or if inside brackets
store the character in the current item
if a < bracket: note that we are inside brackets
if a > bracket: note that we are outside brackets
else (an unmasked comma)
store the current item as a string into the resulting vector
clear the current item
store the last item into the resulting vector
Only 10 lines and my rubber duck agreed that it should work...
C++ implementation: I will use a vector to handle the current item because it is easier to build it one character at a time
std::vector<std::string> parse(const std::string& str) {
std::vector<std::string> result;
bool masked = false;
std::vector<char> current; // stores chars of the current item
for (const char c : str) {
if (masked || (c != ',')) {
current.push_back(c);
switch (c) {
case '<': masked = true; break;
case '>': masked = false;
}
}
else { // unmasked comma: store item and prepare next
current.push_back('\0'); // a terminating null for the vector data
result.push_back(std::string(&current[0]));
current.clear();
}
}
// do not forget the last item...
current.push_back('\0');
result.push_back(std::string(&current[0]));
return result;
}
I tested it with all your example strings and it gives the expected results.
Seems quite straight forward to me.
vector<string> customSplit(string s)
{
vector<string> results;
int level = 0;
std::stringstream ss;
for (char c : s)
{
switch (c)
{
case ',':
if (level == 0)
{
results.push_back(ss.str());
stringstream temp;
ss.swap(temp); // Clear ss for the new string.
}
else
{
ss << c;
}
break;
case '<':
level += 2;
case '>':
level -= 1;
default:
ss << c;
}
}
results.push_back(ss.str());
return results;
}

Pangram String Error On My Hackerrank Code

I found this question on Hackerrank where I have to write a method to say whether or not a given string is a pangram. A sentence is a pangram if it contains all 26 letters of the alphabet. The input will only contain characters that are alphabetical (uppercase or lowercase) and spaces.
Here's the code I've gotten so far, where I use a set to keep track of which letters are present in the string. However, the code just keeps running infinitely in the while loop below.
string pangrams(string s) {
set<char> set{};
int i=0;
while (i!=s.length()) {
if(s[i]!='\0') {
set.insert(tolower(s[i]));
}
}
if (set.size() == 27) {
return "pangram";
} else {
return "not pangram";
}
}
Your function needs a slight modification. Firstly, you aren't incrementing i which makes your function go into infinite loop. Other modification is explained in code below -
string pangrams(string s) {
set<char> set{};
int i=0;
while (i!=s.length()) {
if(s[i]!=' ') { # if character is space, ignore it
set.insert(tolower(s[i]));
}
i++; # Main reason of runtime error - you missed incrementing i
}
if (set.size() == 26) { # 26 and not 27. There may be strings without space that are pangrams. So we wont add space into our set.
return "pangram";
} else {
return "not pangram";
}
}
Also, you don't need to check s[i]!='\0' since a c++ string isn't terminated with \0 character. Only checking i!=s.length() will be enough.
Hope this clears your issue !
You're never incrementing i, so your code will run infinitely. I would recommend a for loop for (int i = 0; i < s.length(); i ++) or a for-each loop for (char c : s)
Instead of using a set, you could also try this, where each character corresponds to an index in a bool[]
bool exists[27];
for (char c : s) {
if ('a' <= c && c <= 'z') {
exists[c - 'a'] = true;
} else if ('A' <= c && c <= 'A') {
exists[c - 'A'] = true;
} else if (c == ' ') {
exists[26] = true;
}
}
for (bool b : exists) {
if (!b) return false;
}
return true;

How to tokenize special characters depending on whitespace (< > | & etc.)

I found a project done a few years ago found here that does some simple command line parsing. While I really like it's functionality, it does not support parsing special characters, such as <, >, &, etc. I went ahead and attempted to add some functionality to parse these characters specifically by adding some of the same conditions that the existing code used to look for whitespace, escape characters, and quotes:
bool _isQuote(char c) {
if (c == '\"')
return true;
else if (c == '\'')
return true;
return false;
}
bool _isEscape(char c) {
if (c == '\\')
return true;
return false;
}
bool _isWhitespace(char c) {
if (c == ' ')
return true;
else if(c == '\t')
return true;
return false;
}
.
.
.
What I added:
bool _isLeftCarrot(char c) {
if (c == '<')
return true;
return false;
}
bool _isRightCarrot(char c) {
if (c == '>')
return true;
return false;
}
and so on for the rest of the special characters.
I also tried the same approach as the existing code in the parse method:
std::list<string> parse(const std::string& args) {
std::stringstream ain(args); // iterates over the input string
ain >> std::noskipws; // ensures not to skip whitespace
std::list<std::string> oargs; // list of strings where we will store the tokens
std::stringstream currentArg("");
currentArg >> std::noskipws;
// current state
enum State {
InArg, // scanning the string currently
InArgQuote, // scanning the string that started with a quote currently
OutOfArg // not scanning the string currently
};
State currentState = OutOfArg;
char currentQuoteChar = '\0'; // used to differentiate between ' and "
// ex. "sample'text"
char c;
std::stringstream ss;
std::string s;
// iterate character by character through input string
while(!ain.eof() && (ain >> c)) {
// if current character is a quote
if(_isQuote(c)) {
switch(currentState) {
case OutOfArg:
currentArg.str(std::string());
case InArg:
currentState = InArgQuote;
currentQuoteChar = c;
break;
case InArgQuote:
if (c == currentQuoteChar)
currentState = InArg;
else
currentArg << c;
break;
}
}
// if current character is whitespace
else if (_isWhitespace(c)) {
switch(currentState) {
case InArg:
oargs.push_back(currentArg.str());
currentState = OutOfArg;
break;
case InArgQuote:
currentArg << c;
break;
case OutOfArg:
// nothing
break;
}
}
// if current character is escape character
else if (_isEscape(c)) {
switch(currentState) {
case OutOfArg:
currentArg.str(std::string());
currentState = InArg;
case InArg:
case InArgQuote:
if (ain.eof())
{
currentArg << c;
throw(std::runtime_error("Found Escape Character at end of file."));
}
else {
char c1 = c;
ain >> c;
if (c != '\"')
currentArg << c1;
ain.unget();
ain >> c;
currentArg << c;
}
break;
}
}
What I added in the parse method:
// if current character is left carrot (<)
else if(_isLeftCarrot(c)) {
// convert from char to string and push onto list
ss << c;
ss >> s;
oargs.push_back(s);
}
// if current character is right carrot (>)
else if(_isRightCarrot(c)) {
ss << c;
ss >> s;
oargs.push_back(s);
}
.
.
.
else {
switch(currentState) {
case InArg:
case InArgQuote:
currentArg << c;
break;
case OutOfArg:
currentArg.str(std::string());
currentArg << c;
currentState = InArg;
break;
}
}
}
if (currentState == InArg) {
oargs.push_back(currentArg.str());
s.clear();
}
else if (currentState == InArgQuote)
throw(std::runtime_error("Starting quote has no ending quote."));
return oargs;
}
parse will return a list of strings of the tokens.
However, I am running into issues with a specific test case when the special character is attached to the end of the input. For example, the input
foo-bar&
will return this list: [{&},{foo-bar}] instead of what I want: [{foo-bar},{&}]
I'm struggling to fix this issue. I am new to C++ so any advice along with some explanation would be great help.
When you handle one of your characters, you need to do the same sorts of things that the original code does when it encounters a space. You need to look at the currentState, then save the current argument if you are in the middle of one (and reset it since you no longer are in one).

Printing null character when input is odd character amount

I've been toying with this c program for a while, and I can't seem to figure out what I'm missing.
In the very bottom of my code, I have a function that replaces every other word with a "-".
My problem is that when I enter an odd numbered word, such as "Cat", "dog", "hamburger", it will place a "-" in what I think is the null character position, though I have not been able to debunk it.
Thank you for your help!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void replace(char w[]);
int main( )
{
char w[100], x[100], y[100];
int z = 0;
printf("Player 1, please enter the secret word: ");
fgets(x,100,stdin);
// system("clear");
while( strcmp(x,y) != 0 )
{
strcpy(w,x);
// printf("\nLength of String : %d", strlen(w)-1);
replace(w);
printf("Player 2, the word is %s\n",w);
printf("Player 2, please guess the word: ");
fgets(y,100,stdin);
z++;
if( strcmp(x,y) != 0 )
{
printf("Wrong. Try again.\n");
}
else
{
//system("clear");
printf("Correct!\n");
printf("It took you %d attempt(s).\n",z);
switch (z)
{
case 1 :
case 2 :
printf("A. Awesome work!");
{break;}
case 3 :
case 4 :
printf("B. Best, that was!");
{break;}
case 5 :
case 6 :
printf("C. Concentrate next time!");
{break;}
case 7 :
printf("D. Don't quit your day job.");
{break;}
default :
printf("F. Failure.");
{break;}
}
}
}
getch();
}
void replace(char w[])
{
int a;
a = 0;
while (w[a] != '\0')
{
if (a % 2 != 0)
{
w[a] = '-';
a++;
}
if (w[a] != '\0')
{
a++;
}
else
{
break;
}
}
}
From the fgets manual;
fgets() reads in at most one less than size characters from stream and stores them into the buffer pointed to by s. Reading stops after an EOF or a newline. If a newline is read, it is stored into the buffer. A terminating null byte (\0) is stored after the last character in the buffer.
The newline entered is what you're replacing.
You can implement like this...
int a;
int len;
a = 0;
len = strlen(w);
if(len%2 == 0)
len = len-1;
while (len!=a)
{
if (a % 2 != 0)
{
w[a] = '-';
a++;
}
if (w[a] != '\0')
{
a++;
}
else
{
break;
}
}
I think replacing fgets with just gets will work:
Try:
//fgets(x,100,stdin);
gets(x);
and
//fgets(y,100,stdin);
gets(y);
That will be enough I think.
The problem is caused by the additional '\n' character in the char array passed to the replace function.
For instance, when the input is "Cat", the passed char[] w contains {'C', 'a', 't', '\n', '\0'};
The additional '\n' also gets replaced with "-" character.
The following will solve this problem.
while (w[a] != '\0')
{
if (w[a] != '\0' && w[a] != '\n')
{
if (a % 2 != 0)
{
w[a] = '-';
}
a++;
}
else
{
break;
}
}
As a bit of an aside, can I suggest structuring your replace() code differently
void replace(char charw[])
{
int length=strlen(charw);
int i;
for (i=0;i<length;i++)
{
if (i%2==1) /*yes, i%2 would also work, but lets not get too clever*/
{charw[i]='-';}
}
}
This is far more readable. Breaking in the middle of a loop...not so much.