Remove substrings from a C-style string "in place" in C++ code - c++

I had an interview task to remove substring from a current string without using string functions or additional memory...I tried just with strlen but didn't find the way to change it in place...
#include <stdio.h>
#include <string.h>
#include <iostream>
using namespace std;
int main()
{
char * str = "this that there";
char * substr = "th";
removeSubstr(str,substr);
cout<<str; //should be now "is at ere"
return 0;
}
void removeSubstr(char * str, const char * substr){
}

Since you remove characters from the original string, the string is shrinking, so you do not need additional space. You just have to copy characters from a higher index (source) to a lower index (destination).
If the source index points to a position that starts with the searched substring, you must skip it.
The skip function simply compares the beginning of the source string against the substring, and returns the source (if it does not start with the substring) or the source plus the length of substring (if it starts with the substring).
Demo
#include <iostream>
char* skip_if( char* s, const char* ss )
{
char* p = s;
const char* pp = ss;
while( *p == *pp && *p )
p++, pp++;
return *pp ? s : p;
}
void remove( char* s, const char* ss )
{
char *ps = s; // source
char *pd = s; // destination
while( *ps )
{
ps = skip_if( ps, ss );
*pd++ = *ps++;
}
*pd = 0;
}
int main()
{
using namespace std;
char str[] = "this that there this that there";
const char* substr = "th";
remove( str, substr );
cout << str;
return 0;
}

The string functions exist for a reason. If you are not to use them, can you create them from scratch?
If so, here is my solution to the problem. Very educational to write custom string functions with the const requirements.
As mentioned in the comments, this won't work unless the string passed in can be modified, so I made it a non-constant string.
Let me know if this meets the interview challenge:
#include <iostream>
bool myStrnCmp(char *str1, const char *str2, int len) {
char *cptr1 = (char *) str1;
char *cptr2 = (char *) str2;
for (int i = 0; i < len; i++) {
if (*(cptr1 + i) != *(cptr2 + i))
return false;
}
return true;
}
int mystrlen(const char* str) {
int i = 0;
while(*(str + i) != '\0')
i++;
return i;
}
int findSubStr(char *str, const char *substr) {
int position = 0;
int len = mystrlen(substr);
while(*(str + position) != '\0') {
for (int i = 0; i < len; i++) {
if (myStrnCmp(str + position + i, substr, len))
return position + i;
}
++position;
}
return -1;
}
void myStrCpy(char *str, const char *substr) {
memmove(str, substr, mystrlen(substr) + 1);
}
void removeSubstr(char *str, const char *substr) {
int position = findSubStr(str, substr);
while(position >= 0) {
myStrCpy(str + position, str+position+mystrlen(substr));
position = findSubStr(str, substr);
}
}
int main() {
char str[]{"this that there"};
char* substr = "th";
removeSubstr(str,substr);
std::cout<<str; //should be now "is at ere"
return 0;
}

Related

Why it gives me an extra words?

Write and test your own function char * funct (char * str, int x) inverting (except for the character at position n) the string str and returning the modified str as the result. The use of the function funct could be:
This is main:
#include <iostream>
#include <cstring>
using namespace std;
char* funct(char *str, int x);
int main() {
char str1 [] = "Hello cpp";
cout << str1 << endl; // Hello cpp
cout << funct (str, 1) << endl; // pepC ollH // the character at position 1 ('e') stays in place
return 0;
}
This is my function:
char* funct(char *str, int x) {
int counter = 0;
do {
counter++;
str++;
} while (*str);
str--;
char *wskTmp = str;
for (int i = 0; i < counter ; i++) {
*wskTmp = *str;
str--;
wskTmp++;
}
*wskTmp = '\0';
wskTmp = wskTmp - counter;
for (int i = 0; i < counter - x -1; i++) {
wskTmp++;
}
char tmp;
for (int i = 0; i < counter-3; i++) {
tmp = *(wskTmp - 1);
*(wskTmp - 1) = *wskTmp;
*wskTmp = tmp;
wskTmp--;
}
return str;
}
Output:
Hello Cpp
Hello CppepC ollH
It should be:
Hello Cpp
pepC ollH
Why it gives me Hello Cp before "pepC ollH"?
Your code is very confusing and is a very roundabout way of accomplishing this task, so I restructured it a bit:
#include <cstring>
#include <iostream>
using namespace std;
char *funct(char *str, int x) {
// keep track of the original start
char *origStr = str;
// iterate through the string to find the end
do {
str++;
} while (*str);
// decrease the string so it's on the last byte, not the nullbyte
str--;
// create a start and end
char *start = origStr;
char *end = str;
if (start - origStr == x) {
start ++;
}
if (end - origStr == x) {
end--;
}
// if start >= end then we've finished
while (start < end) {
// swap values at start and end
char temp = *start;
*start = *end;
*end = temp;
// move the pointers closer to each other
start++;
end--;
// skip the index x
if (start - origStr == x) {
start++;
}
// skip the index x
if (end - origStr == x) {
end--;
}
}
// make sure to return the actual start
return origStr;
}
int main() {
char str1[] = "Hello cpp";
cout << str1 << endl; // Hello cpp
cout << funct(str1, 1) << endl; // pepC ollH // the character at position 1
// ('e') stays in place
return 0;
}

Return a pointer to the last appearance of a character in a C-Style string (C++)

Return a pointer to the last appearance of c
appearing inside s and nullptr (0) if c does not appear inside s.
#include <string>
#include <iostream>
#include <cassert>
using namespace std;
const char* myStrRChr(const char* s, char c)
{
int curIdx = 0;
char last;
while (s[curIdx] != '\0')
{
if (s[curIdx] == c)
last = s[curIdx];
curIdx++;
}
if (s[curIdx] == c)
return last;
else
// return '\0', nullptr, NULL
return "";
}
int main()
{
char cstr[50] = "Abadabadoo!";
char buf[10];
const char * cat = "cat";
char dog[] = "Labradoodle";
cout << "\nmyStrRChr(cstr, 'a') expects adoo!" << endl;
cout << " -- " << myStrRChr(cstr, 'a') << endl;
return 0;
}
This code returns "adabadoo!". I can't wrap my mind around as to how to get the last instance of "char c."
You can do this by obtaining a pointer to the end of the string and decrementing down the string searching for the character c, and a pointer to the beginning of the string to know where to stop looping:
const char *mystrrchr(const char *str, char c)
{
int len = strlen(str);
char *p = const_cast<char *>(&str[len-1]);
char *stop = const_cast<char *>(&str[0]);
while(p>=stop)
{
if(*p==c)
{
return p;
}
p--;
}
return nullptr;
}

Strcpy in c++ doesn't work

Can anyone tell why strcpy in this code returns an empty string?
#include <iostream>
char* strcpy(char* dest, const char* from) {
for ( ; *from; dest++, from++) {
*dest = *from;
}
return dest;
}
int main() {
char a[] = "aba";
char b[] = "hello";
std::cout << strcpy(a, b);
return 0;
}
The compiler I'm using is GNU G++11 4.9.2
upd: this doesn't work either
#include
char* strcpy(char* dest, const char* from) {
for ( ; *from; dest++, from++) {
*dest = *from;
}
*dest = '\0';
return dest;
}
int main() {
char a[] = "abaaa";
char b[] = "hello";
std::cout << strcpy(a, b);
return 0;
}
Try using temp pointer:
char* strcpy(char* dest, const char* from) {
char *tmp = dest;
for ( ; *from; tmp++, from++) {
*tmp = *from;
}
*tmp = '\0';
return dest;
}
Also consider allocate memory for the dest with appropriate number of characters.
The function and the program itself are invalid.:)
For example array a can not accomodate all characters from array b because its size is less than the size of b. (The size of a is equal to 4 while the size of b is equal to 6)
char a[] = "aba";
char b[] = "hello";
So the call strcpy(a, b) in this statement
std::cout << strcpy(a, b);
results in undefined behaviour.
As for the function then it does not copies the terminating zero from the sourse string to the destination string. And it does not return pointer to the first character of the destination string because inside the function pointer dest is changed (it was increased)
The correct function can look like
char* strcpy( char* dest, const char* from )
{
char *p = dest;
while ( *p++ = *from++ );
return dest;
}
Instead of incrementing dest and from in the loop, try this:
char *StrCpy (char *dest, const char *from){
int i=0;
for (i=0;i<strlen(from);i++){
*(dest+i) = *(from+i);
}
*(dest+(strlen(from)))='\0';
return dest;
}

Returning null from a function that returns a pointer

I'm writing a program that involves a function that will take 2 strings as input . The function should return a pointer to the occurrence of str2 in str1 if it exists , if not it has to return NULL .The function should basically return str2 if it was found in str1 . The code works but i cant figure out how to return NULL from the function if newstring has no elements. Any guidance on how to return NULL if the newstring wasn't assigned would be appreciated, Thanks!
#include<iostream>
using namespace std;
template <class T>
int lenstr(T str1) // Function that finds length of a string , instead of using the strlen or sizeof functions.
// this function will work for all data types and not just char.
{
int count = 0;
while(str1[count] != '\0')
{
count++;
}
return count;
}
char* mystrstr(char *str1 , const char* str2)
{
int len1 = lenstr(str1);
int len2 = lenstr(str2);
char *newstring = new char[len2];
int i = 0;
for (int j = 0;j<len1;j++)
{
if(str1[j] == str2[i])
{
newstring[i] = str1[j];
i++;
}
else
{
i = 0;
}
}
return newstring;
}
void main()
{
char str1[28] = {"this is a test program"};
char str2[15] = {"tes"};
int len2 = lenstr(str2);
char* returnstring;
returnstring = mystrstr(str1,str2);
for(int i = 0;i<len2;i++)
{
cout<<returnstring[i];
}
cout<<endl;
delete returnstring;
}
Your variant of strstr doesn't return a pointer to the occurance of str2 in str1. It just takes as many letters from the str1, that are in str2 and returns pointer to new string.
This is what you need if your question is right:
char* mystrstr(char *str1 , char* str2)
{
int len1 = lenstr(str1);
int len2 = lenstr(str2);
char *ptr1, *ptr2;
char *result;
for(ptr1 = str1, ptr2 = str2; *ptr1; ptr1++)
{
if(*ptr2 == 0)
break;
if(*ptr1 == *str2)
result = ptr1;
if(*ptr1 == *ptr2)
{
ptr2++;
}
else
{
ptr2 = str2;
result = NULL;
}
}
return result;
}
int main()
{
char str1[28] = {"this is a test program"};
char str2[15] = {"tes"};
int len2 = lenstr(str2);
char* returnstring;
returnstring = mystrstr(str1,str2);
if(returnstring != NULL)
{
cout << returnstring;
}
else
{
cout << "returnstring is NULL" << endl;
}
delete returnstring;
}
Try initializing newstr with all 0's, like so:
char *newstring = new char[len2];
for (int i = 0; i < len2; i++) {
newstring[i] = '0';
}
(newstr should by default be initialized with 0s, but I'm not quite sure on this, so I just posted that loop since I'm not 100% on that)
And then, at the end of your loop, if newstring still contains 0 at the first index, return
nullptr.

Palindrome finder: non-alphanumeric character deletion problems

So I'm having a substantial amount of trouble with this one bit of code. I've included the whole program for context, but my issue lies in the cleanUp function, wherein I (attempt to) remove all characters that are not 'A' through 'Z'.
Any tips?
#include <iostream>
#include <cstdlib>
#include <string>
#include <stdio.h>
#include <ctype.h>
using namespace std;
bool again(string title); // Checks if you want to run again.
void makeUpper(char word[]);
void getReverse(char word[], char reversed[]);
char * find(char *str, char what);
bool equal(char word[], char reversed[]);
int size(char word[]);
char * cleanUp(char *str);
int main()
{
char word[256] = "Hello?? There!", reversedWord[256];
do
{
cout<<"Please enter the string to check: ";
makeUpper(word);
cout << word;
cleanUp(word);
getReverse(word,reversedWord);
if(equal(word, reversedWord))
cout<<"You have a palindrome!"<<endl;
else
cout<<"You do not have a palindrome!"<<endl;
} while(again("Do you want to do this again? "));
return 0;
}
bool again(string title)
{
string answer;
cout<<endl<<title;
getline(cin,answer);
return toupper(answer[0]) == 'Y';
}
void makeUpper(char word[])
{
char *ptr = word;
while (*ptr) {
*ptr = toupper(*ptr);
ptr++;
}
cout << "In uppercase:: " << word << endl;
}
char * cleanUp(char * astrid)
{
char *new_astrid;
for (*astrid; *astrid != '\0'; astrid++)
{
cout << "First loop";
if (isalpha(*astrid))
{
*new_astrid = *astrid;
new_astrid = ++new_astrid;
cout << "Here!";
}
}
cout << *new_astrid;
return *new_astrid;
}
void getReverse(char word[], char reversed[])
{
char *ptr_ind = find(word, '\0'), *ptr_ind_2 = reversed;
while(ptr_ind != word-1)
{
*ptr_ind_2 = *ptr_ind;
ptr_ind--;
ptr_ind_2++;
}
*ptr_ind_2 = '\0';
}
char * find(char *str, char what)
{
char *ptr = str;
while(*ptr != what && *ptr != '\0')
ptr++;
return *ptr == what ? ptr : NULL;
}
bool equal(char word[], char reverse[])
{
int total;
char * ptr;
ptr = word;
if((total = size(word)) != size(reverse))
return false;
for(char * ptr2 = reverse; *ptr != '\0' && *ptr == *ptr2; ptr++, ptr2++);
return *ptr == '\0';
}
int size(char word[])
{
int total = 0;
char * ptr = word;
while(*ptr != '\0') //while(!ptr)
{
ptr++;
total++;
}
return total;
}
There are several errors in your code.
new_astrid is not initialized and when you call *new_astrid = *astrid you try to copy a character to uninitialized memory, which will crash the program.
You also return the dereferenced pointer to new_astrid but the function prototype of cleanUp says that you return a pointer to char.
You should initialize new_astrid with new char[strlen(astrid)]. But then your code will result in memory leaks, since you increase the pointer (new_astid = ++new_astrid). So you should store the pointer first, so you can delete it later.
Instead of using raw pointers, i would suggest you use std::strings.
My suggestion for a palindrome tester would be:
#include <iostream>
#include <string>
#include <locale>
bool isPalindrome(std::string word)
{
std::locale loc;
for (std::string::size_type i = 0; i < word.length() / 2 + 1; ++i)
{
if (std::toupper(word[i],loc) != std::toupper(word[word.length() - i - 1],loc))
{
return false;
}
}
return true;
}
int main(int , char **)
{
std::string str = "Abba";
//Remove all non alpha values from string
str.erase(std::remove_if(str.begin(), str.end(), [](char const c){return !std::isalpha(c);}), str.end());
if (isPalindrome(str) == false)
{
std::cout << str << " is no palindrome" << std::endl;
}
else
{
std::cout << str << " is a palindrome" << std::endl;
}
return 0;
}
The erasion of non alpha values in the string is from this question.