Removing white space and special characters from a c string [duplicate] - c++

This question already has answers here:
Remove spaces from std::string in C++
(19 answers)
Closed 8 years ago.
so I am making a palindrome checker that ignores any white space or special characters. Below is part of my function. What it does is it takes a c string as an argument, then I create another c string in order to remove the whitespaces and special character from the orignal. When I output the second c string, it still will have the whitespaces or special characters. Could some explain why it is doing that? Thanks
bool isPalindrome(char *line)
{
//variables
bool palindrome = true;
int length = strlen(line);
int count = 0;
//copy line to line2 with no spaces and no punctuation
char *line2 = new char[length + 1];
int count2 = 0;
for(int i = 0; i < length; i++)
{
if(line[i] != ' ' && ispunct(line[i]) == false)
{
line2[count2] = line[i];
count2 ++;
}
}
for(int i = 0; i < count2; i++)
cout << line[i];
line2[length] = '\0';

You are null-terminating your second string at the original length:
line2[length] = '\0';
should be
line2[count2] = '\0';
As far as your original assignment goes, it is not necessary to create a copy of the string to check if it is a palindrome: all you need is a function that finds the next non-empty, non-punctuation character in a specific direction:
int nextValidChar(const char *str, int &pos, const int step) {
pos += step;
while (pos >= 0 && str[pos] != '\0') {
char c = str[i];
if (c != ' ' && !ispunct(c)) {
return c;
}
pos += step;
}
return -1;
}
With this function in hand, set up two indexes, at zero and at length-1, and call nextValidChar repeatedly to find valid characters at both ends.

The reason it is still outputting with spaces and special chars is because this
for(int i = 0; i < count2; i++)
cout << line[i];
should be
for(int i = 0; i < count2; i++)
cout << line2[i];

Related

Function to count the frequency of each word in a string using two parallel arrays

I am trying to count the frequency of each word in a given string using two arrays and WITHOUT using Maps or Vectors.
One array to store the words and the other to count the frequency of each word I believe.
I have been teaching myself C++ in my off time and this problem has given me more trouble than I'd like to admit and I've been stuck on it. Maps and Vectors are easier to me, but the problem says specifically not to use them.
This is the code that I used Maps to create pairs but now I need two strings to do the same thing basically.
void wordCounter(string str, string wordsArray[], int countArray[]){
map<string, int> passage;
string word = "";
for (int i = 0; i < str.size(); i++) {
if (str[i] == ' '){
if(passage.find(word) == passage.end()){
passage.insert(make_pair(word, 1));
word = "";
}else{
passage[word]++;
word = "";
}
}else
word += str[i];
}
if(passage.find(word) == passage.end())
passage.insert(make_pair(word, 1));
else
passage[word]++;
for(auto& it : passage) {
cout << it.first << " - " << it.second << endl;
}
}
output would be something like:
thisword - 2
thatword -3
anotherword - 1
etc..
void wordCounter(string str, string wordsArray[], int countArray[]) {
string word = "";
for (int i = 0; i < str.size(); i++) {
if (str[i] == ' ') {
bool found = false;
int spot = 0;
int finder = 0;
for (int j = 0; j < wordsArray.length; j++) {
String wd = wordsArray[j];
if (wd == word) {
spot = finder;
found = true;
break;
}
finder++;
}
if (found) {
countArray[spot] = countArray[spot] + 1;
} else {
// wordsArray[spot + 1] = word
// countArray[spot + 1] = 1
}
word = "";
} else
word += str[i];
}
// to print
// loop through wordsArray and countArray simulatenously
// do something like
for (int simul = 0, simul < wordsArray.length, simul++) {
printf("%s %s\n", wordsArray[simul], countArray[simul]);
}
}
Haven't used C++ in a little bit but essentially if you can't find the word in the arrays, you will want to append the new word to the end of the array and append 1 to the end of the countArray.

Removing stopwords from char array

I'm trying to make a function that removes a pre-defined word (a char array) from a char array. It sort of works and removes the word from a char array, but it only works if the words are separated by spaces. I want to make it so it removes the word from a group of words not separated by spaces, but I don't know how. I'm stuck on this and would appreciate any help.
int i, j = 0, k = 0, count = 0;
char str[1024] = "thisisthestringtobealtered."; // works using spaces
char key[256] = "the"; // I want "the" to be removed in str
char str1[10][20];
void removeWordFromString(){
/* Converts the string into 2D array */
for (i = 0; str[i] != '\0'; i++) {
if (str[i] == ' ') {
str1[k][j] = '\0';
k++;
j = 0;
}
else {
str1[k][j] = str[i];
j++;
}
}
str1[k][j] = '\0';
/* Compares the string with given word */
for (i = 0; i < k + 1; i++) {
if (strcmp(str1[i], key) == 0) {
for (j = i; j < k + 1; j++)
strcpy(str1[j], str1[j + 1]);
k--;
}
}
for (i = 0; i < k + 1; i++) {
printf("%s ", str1[i]);
}
}
A possible solution for this problem would be using strncmp().
This function allows you to compare substrings.
Start like this:
Compare the initial string with the key with num = length of the key.
If it does match cut the substring out
move one character into the initial string and compare again.
Loop until you have less characters in the initial string left than your key is long.
void removeWordFromString(){
/* Converts the string into 2D array */
int ckey=0;
i=0;
while(str[i] != '\0')
{
while(str[i] == key[ckey] && key[ckey] != '\0')
{
str1[k][j] = str[i];
j++;
i++;
ckey++;
}
if ( key[ckey] == '\0' )
{
str1[k][j-strlen(key)] = '\0';
k++;
j = 0;
ckey=0;
}
else{
str1[k][j] = str[i];
j++;
i++;
ckey=0;
}
}
str1[k][j] = '\0';
for (i = 0; i < k + 1; i++) {
printf("%s ", str1[i]);
}
}
Replace the above function in your code.
I would go for a readable algorithm.
Find the place in the string where the key occurs.
while I can find such a place, then
remove this occurrence
look for the next.
Or in code:
char str[1024] = "thisisthestringtobealtered."; // works using spaces
char key[256] = "the"; // I want "the" to be removed in str
void removeWordFromString(){
char* p = strstr(str, key);
while (p) {
// Move from end of key, to start of key, the number of characters we can
// find after the key, plus a null terminator. Memmove because the string
// overlaps itself.
memmove(p, p + strlen(key), strlen(p) - strlen(key) + 1);
p = strstr(str, key);
}
}
Note that this solution is very code-size-efficient and probably too clever to pass as first-year programmer code. I hope this makes it useful to you for learning while ineligible for handing in as a code exercise solution :-)

How can I reverse the words in a sentence without using built-in functions?

This was the interview question:
How to convert Dogs like cats to cats like Dogs ?
My code shows: cats like cats. Where am I making the mistakes?
#include <iostream>
using namespace std;
int main()
{
char sentence[] = ("dogs like cats");
cout << sentence << endl;
int len = 0;
for (int i = 0; sentence[i] != '\0'; i++)
{
len++;
}
cout << len << endl;
char reverse[len];
int k = 0;
for (int j = len - 1; j >= 0; j--)
{
reverse[k] = sentence[j];
k++;
}
cout << reverse << endl;
int words = 0;
char str[len];
for (int l = 0; reverse[l] != '\0'; l++)
{
if (reverse[l] == ' ' || reverse[l] == '\0') // not sure about this part
{
for (int m = l; m >= 0; m--)
{
str[words] = reverse[m];
words++;
}
}
}
cout << str;
return 0;
}
I know you can do this using pointers, stack, vectors... but interviewer was not interested in that!
This is a fixed version of your sample code:
Your principal problem is that every time you found and ' ' or '\0' you copy the bytes of the reverse string from the beginning to that point. Example in loop 5 you copy from index 0-5 (stac) from reverse to str in reverse order, but in in loop 10 you copy from index 0-10 (stac ekil) from reverse to str in reverse order, until here you have already the printed result string ('cats like cats'), and the same in loop 15 all of this incrementing the index of str, in the last loop you are written pass the end of the valid memory of str (and because of that not printed as output).
You need to keep track when end the last word reversed to reverse only the actual word, and not the string from the beginning to the actual index.
You don't want to count the special character (' ' and '\0') in the reversing of the words, you would end with cats like\0dogs
Modified sample code provided:
#include <iostream>
using namespace std;
int main() {
char sentence[] = ("dogs like cats");
cout << sentence << endl;
int len = 0;
for (int i = 0; sentence[i] != '\0'; i++) {
len++;
}
cout << len << endl;
char reverse[len];
int k = 0;
for (int j = len - 1; j >= 0; j--) {
reverse[k] = sentence[j];
k++;
}
cout << reverse << endl;
int words = 0;
char str[len];
// change here added last_l to track the end of the last word reversed, moved
// the check of the end condition to the end of loop body for handling the \0
// case
for (int l = 0, last_l = 0; ; l++) {
if (reverse[l] == ' ' || reverse[l] == '\0')
{
for (int m = l - 1; m >= last_l; m--) { // change here, using last_t to
str[words] = reverse[m]; // only reverse the last word
words++; // without the split character
}
last_l = l + 1; // update the end of the last
// word reversed
str[words] = reverse[l]; // copy the split character
words++;
}
if (reverse[l] == '\0') // break the loop
break;
}
cout << str << endl;
return 0;
}
Some code, written with the restriction of using the most simple features of the language.
#include <iostream>
// reverse any block of text.
void reverse(char* left, char* right) {
while (left < right) {
char tmp = *left;
*left = *right;
*right = tmp;
left++;
right--;
}
}
int main() {
char sentence[] = "dogs like cats";
std::cout << sentence << std::endl;
// The same length calculation as sample code.
int len = 0;
for (int i = 0; sentence[i] != '\0'; i++) {
len++;
}
std::cout << len << std::endl;
// reverse all the text (ex: 'stac ekil sgod')
reverse(sentence, sentence + len - 1);
// reverse word by word.
char* end = sentence;
char* begin = sentence;
while (end < sentence + len) {
if (*end != ' ')
end++;
if (end == sentence + len || *end == ' ') {
reverse(begin, end - 1);
begin = end + 1;
end = begin;
}
}
std::cout << sentence << std::endl;
return 0;
}
Dissecting your algorithm in pieces. First, you find the length of the string, not including the null char terminator. This is correct, though could be simplified.
size_t len = 0;
for (int i = 0; sentence[i] != '\0'; i++) {
len++;
}
cout << len << endl;
This could easily be written simply as:
size_t len = 0;
while (sentence[len])
++len;
Next, you reverse the entire string, but the first defect surfaces. The VLA (variable length array) you declare here, (which you don't need and shouldn't use, as it is a C++ extension and non-standard) does not account for, nor set, a terminating null-char.
char reverse[len]; // !! should be len+1
int k = 0;
for (int j = len - 1; j >= 0; j--) {
reverse[k] = sentence[j];
k++;
}
// !! Should have reverse[k] = 0; here.
cout << reverse << endl; // !! Undefined-behavior. no terminator.
This temporary buffer string is not needed at all. There is no reason you can't do this entire operation in-place. Once we calculate len correctly, you simply do something like the following to reverse the entire sequence, which retains the null char terminator in proper position:
// reverse entire sequence
int i = 0, j = len;
while (i < j--)
{
char c = sentence[i];
sentence[i++] = sentence[j];
sentence[j] = c;
}
Next we move to where you try to reverse each internal word. Again, just as before, the buffer length is not correct. It should be len+1. Worse (hard to imagine), you never remember where you left off when finding the end point of a word. That location should be the next point you start checking for, and skipping, whitespace. Without retaining that you copy from current point all the way back to the beginning of the string. which essentially blasts cats over dogs.
int words = 0;
char str[len]; // !! should be len+1
for (int l = 0; reverse[l] != '\0'; l++)
{
if (reverse[l] == ' ' || reverse[l] == '\0') // not sure about this part
{
for (int m = l; m >= 0; m--) {
str[words] = reverse[m];
words++;
}
}
}
cout << str; //!! Undefined behavior. non-terminated string.
Once again, this can be done in-place without difficulty at all. One such algorithm looks like this (and notice the loop that reverses the actual word is not-coincidentally the same algorithm as reversing our entire buffer):
// walk again, reversing each word.
i = 0;
while (sentence[i])
{
// skip ws; root 'i' at beginning of word
while (sentence[i] == ' ') // or use std::isspace(sentence[i])
++i;
// skip until ws or eos; root 'j' at one-past end of word
j = i;
while (sentence[j] && sentence[j] != ' ') // or use !std::isspace(sentence[j])
++j;
// remember the last position
size_t last = j;
// same reversal algorithm we had before
while (i < j--)
{
char c = sentence[i];
sentence[i++] = sentence[j];
sentence[j] = c;
}
// start at the termination point where we last stopped
i = last;
}
Putting It All Together
Though considerably simpler to use pointers than all these index variables, the following will do what you're attempting, in place.
#include <iostream>
int main()
{
char s[] = "dogs like cats";
std::cout << s << '\n';
size_t len = 0, i, j;
while (s[len])
++len;
// reverse entire sequence
i = 0, j = len;
while (i < j--)
{
char c = s[i]; // or use std::swap
s[i++] = s[j];
s[j] = c;
}
// walk again, reversing each word.
i = 0;
while (s[i])
{
// skip ws; root 'i' at beginning of word
while (s[i] == ' ') // or use std::isspace
++i;
// skip until ws or eos; root 'j' at one-past end of word
j = i;
while (s[j] && s[j] != ' ') // or use !std::isspace
++j;
// remember the last position
size_t last = j;
while (i < j--)
{
char c = s[i]; // or use std::swap
s[i++] = s[j];
s[j] = c;
}
// start at last-left posiion
i = last;
}
std::cout << s << '\n';
return 0;
}
Output
dogs like cats
cats like dogs
My advise would be to break up the original string into an array of words, reverse that array. Then add those words to your reversed sentence with a space in between.
Since they asked for no libraries, I assumed no std::string, no vectors, nothing at all and so I wrote it in C.. the only thing used is printf. Everything else is from scratch :l
The idea is that you reverse the array first. Then split the array by space and reverse each word.
Example: http://ideone.com/io6Bh9
Code:
#include <stdio.h>
int strlen(const char* s)
{
int l = 0;
while (*s++) ++l;
return l;
}
void reverse(char* str)
{
int i = 0, j = strlen(str) - 1;
for(; i < j; ++i, --j)
{
str[i] ^= str[j];
str[j] ^= str[i];
str[i] ^= str[j];
}
}
void nulltok(char* str, char tok, int* parts)
{
int i = 0, len = strlen(str);
*parts = 1;
for (; i < len; ++i)
{
if (str[i] == tok)
{
str[i] = '\0';
++(*parts);
}
}
}
char* reverse_sentence(char* str)
{
char* tmp = str;
reverse(str);
int i = 0, parts = 0, len = strlen(str);
nulltok(str, 0x20, &parts);
while(parts--)
{
reverse(str);
str += strlen(str) + 1;
}
for(; i < len; ++i)
if (tmp[i] == '\0')
tmp[i] = 0x20;
return tmp;
}
int main(void)
{
char str[] = "dogs like cats";
printf("%s", reverse_sentence(str));
return 0;
}
My solution
#include<iostream>
#include<string.h>
using namespace std;
int main()
{
string str;
cout<<"enter the sentence"<<endl;
getline(cin,str);
char* pch;
pch = strtok((char*)str.c_str()," ");
string rev = "";
while(NULL != pch)
{
rev.insert(0,pch);
rev.insert(0," ");
pch = strtok(NULL," ");
}
cout<<"the reversed string is :"<<rev<<endl;
return 0;
}

String error output [duplicate]

This question already has answers here:
C++ Remove punctuation from String
(12 answers)
Closed 9 years ago.
I got a code. It should give me an output that will erase the middle character between 'z' and 'p'. for example: zipZap("zipXzap"): expected [zpXzp] but found [z pXz p]
std::string zipZap(const std::string& str){
string a = str;
string b = "";
size_t len = str.length();
for (size_t i = 0; i < len; i++){
if (str[i] == 'z')
if (str[i+2] == 'p')
a[i+1] = ' ';
}
return a;
}
When i replaced a[i+1] = ''; it gave me an error.
You are not removing the chars, you are replacing them with ' '.
There are many ways to do this. One simple way is to build a new string, only adding chars when the proper conditions are met:
std::string zipZap(const std::string& str)
{
string a;
size_t len = str.length();
for (size_t i = 0; i < len; i++) {
// Always add first and last chars. As well as ones not between 'z' and 'p'
if (i == 0 || i == len-1 || (str[i-1] != 'z' && str[i+1] != 'p')) {
a += str[i];
}
}
return a;
}
Use string.erase() :
std::string zipZap(const std::string& str){
std::string a = str;
std::string b = "";
size_t len = str.length();
for (size_t i = 0; i < len; i++){
if (a[i] == 'z')
if (a[i+2] == 'p')
a.erase(i+1,1);
}
return a;
}
You're completely right that you cant replace an element of the string with ''.
A string is an array of chars, and '' is not a char at all. It is nothing.
If we look at the cplusplus page for a string
http://www.cplusplus.com/reference/string/string/
We see that we can use erase(iterator p) to "Erase characters from string (public member function)"
So if we change:
for (size_t i = 0; i < len; i++){
if (str[i] == 'z')
if (str[i+2] == 'p')
a.erase(a.begin() + i + 1);
We're closer now, but we can see that len is no longer the same as str.length(). the length of a is now actually 1 char shorter than len. To remedy this however we can simply add:
for (size_t i = 0; i < len; i++){
if (str[i] == 'z')
if (str[i+2] == 'p')
a.erase(a.begin() + i + 1);
len -= 1;
Hope that helps
If you #include <regex>, you can do a regular expression replacement.
std::string zipZap(const std::string& str){
regex exp("z.p");
string a = str;
a = regex_replace(a, exp "zp");
return a;
}

Remove spaces from a string in C++

I am currently learning C++. I am trying to code a method to remove white spaces form a string and return the string with no spaces
This is my code:
string removeSpaces(string input)
{
int length = input.length();
for (int i = 0; i < length; i++) {
if(input[i] == ' ')
input.erase(i, 1);
}
return input
}
But this has a bug as it won't remove double or triple white spaces.
I found this on the net
s.erase(remove(s.begin(),s.end(),' '),s.end());
but apparently this is returning an iterator (if I understand well)
Is there any way to convert the iterator back to my string input?
Most important is this the right approach?
std::string::erase returns an iterator, but you don't have to use it. Your original string is modified.
string removeSpaces(string input)
{
input.erase(std::remove(input.begin(),input.end(),' '),input.end());
return input;
}
std::remove_if along with erase would be much easier (see it live):
input.erase(remove_if(input.begin(), input.end(), isspace),input.end());
using std::isspace had the advantage it will capture all types of white space.
Let's assume your input has a double space, for example "c++[ ][ ]is[ ]fun" ([ ] represents a single space). The first space has index 3 (numeration starts from 0) and the second space, is of course index 4.
In your for loop, when you hit i == 3 you erase the first space. The next iteration of the loop takes i == 4 as the index. But is the second space at index 4 now ? No! Removing the first space changed the string into "c++[ ]is[ ]fun": the space to remove is at index 3, again!
The solution can be to remove spaces right-to-left:
for (int i = length-1; i >= 0; --i) {
if(input[i] == ' ')
input.erase(i, 1);
}
This solution has the benefit of being simple, but as Tony D points out, it's not efficient.
this should also work -- std::replace( input.begin(), input.end(), ' ', ''); You need to include <algorithm>
this code should work
string removeSpaces(string input)
{
int length = input.length();
for (int i = 0; i < length; i++) {
if(input[i] == ' ')
{
input.erase(i, 1);
length--;
i--;
}
}
return input
}
Reason: if it gets space in the string it will reduce the length of the string, so you have to change the variable: "length" accordingly.
I tried to write something to.
This function take a string and copy to another temporary string all the content without extra spaces.
std::string trim(std::string &str){
int i = 0;
int j = 0;
int size = str.length();
std::string newStr;
bool spaceFlag = false;
for(int i = 0;i < size; i++){
if(str[i] == ' ' && (i+1) < size && str[i+1] == ' '){
i++;
spaceFlag = true;
continue;
}
if(str[i] == ' '){
newStr += " ";
continue;
}
if(str[i] == '\t' && i != 0){
str[i] = ' ';
newStr += " ";
}
else{
newStr += str[i];
if(spaceFlag){
newStr += " ";
spaceFlag = false;
}
}
}
str = newStr;
return str;
}
#include<iostream>
#include<string.h>
using namespace std;
void trimSpace(char s[])
{
int i=0, count=0, j=0;
while(s[i])
{
if(s[i]!=' ')
s[count++]=s[i++];
else {
s[count++]=' ';
while(s[i]==' ')
i++;
}
}
s[count]='\0';
cout<<endl<<" Trimmed String : ";
puts(s);
}
int main()
{
char string[1000];
cout<<" Enter String : ";
gets(string);
trimSpace(string);
return 0;
}