How to properly remove a character from a char array (with or without converting to a string)?

24,488

Solution 1

For a char array you have to use std::remove instead of erase, and insert the null-terminator manually:

auto newEnd = std::remove(std::begin(s), std::end(s), letterToRemove);
*newEnd = '\0';

Solution 2

If you have a generic array, that may or may not have decayed to a pointer, and you have the number of elements in the array but no other terminator (i.e. it's not a C-style zero-terminated string), then here's a solution:

First I want to you think abut how the array looks in memory. For example if we have an array of characters

char array[X] = { 'A', 'B', 'C', 'D', ... };

This array will look like this in memory

+---+---+---+---+-----+
| A | B | C | D | ... |
+---+---+---+---+-----+

If you wanted to remove the letter 'B' from that array you find the position of the letter, then you copy the next letter ('C') into its place, and the next next letter ('D') into the next letter place, etc.

You can of course do this using a single loop:

size_t index_to_remove = 1;  // The index of 'B'
for (size_t i = index_to_remove = 1; i < element_count - 1; ++i)
    array[i] = array[i + 1];

The above loop is simple, easy to figure out, and easy to step through in a debugger. It is also not very effective, especially if the array is large.

All the above copying can actually be done using a single call to the memmove function:

memmove(&array[index_to_remove],
        &array[index_to_remove + 1],
        element_count - index_to_remove - 1);

The above call will basically do the same as our loop above but in a more efficient and optimized way.

Using either of the above methods, the loop or the memmove call, will leave the array like this:

+---+---+---+-----+
| A | C | D | ... |
+---+---+---+-----+

Important note: Remember to change element_count after you "removed" the character, so it reflects the new size.

Share:
24,488
Admin
Author by

Admin

Updated on July 05, 2022

Comments

  • Admin
    Admin almost 2 years

    Sorry in advance for any possible duplicate question. I have been Googling a solution for my compiler error, for a week now, tried different workarounds from various answers around here, yet I keep getting some errors.

    I am currently learning C++, trying to build a program that does basic stuff like vowel/consonant count, letter removal etc. Everything works fine until I get to the custom letter removal part. Basically, it's near impossible to do that using character functions (according to my knowledge), while converting to a string seems to spawn other kinds of errors.

    Here is the code fragment where I keep getting errors:

    if (strcmp (service, key4) ==0)
    {
    string str(s);
    
    cout<<endl<<"Please insert the letter you would like removed from your "<<phrasal<<":"<<endl;
    
    cin>>letterToRemove;
    
    s.erase(remove(s.begin(), s.end(),letterToRemove), s.end());
    
    cout<<endl<<s<< "\n"<<endl;
    }
    

    and here are initialized variable I used:

    int main()
    
    {
    char s[1000], phrasal[10], service[50], key1[] = "1", key2[] = "2", key3[] = "3", key4[] = "4", key5[] = "5", key6[] = "6", key0[] = "0", *p, letterToRemove;
    
    int vowel=0, vowel2=0, consonant=0, consonant2=0, b, i, j, k, phrase=0, minusOne, letter, idxToDel;
    
    void pop_back();
    
    char *s_bin;
    

    As you can see, the original 's' is a char array. In the first code sample I have tried converting it into a string array (string str(s)), but that results in the following compiling errors:

    • error: request for member 'erase' in 's', which is of non-class type 'char[1000]'
    • error: request for member 'begin' in 's', which is of non-class type 'char[1000]'
    • error: request for member 'end' in 's', which is of non-class type 'char[1000]'
    • error: request for member 'end' in 's', which is of non-class type 'char[1000]'

    The other workaround I've tried was this:

    if(strcmp(service, key4)==0)
    
    {std::string s(s);
    
    cout<<endl<<"Please insert the letter you would like removed from your "<<phrasal<<":"<<endl;
    
    cin>>letterToRemove;
    
    s.erase(remove(s.begin(), s.end(),letterToRemove), s.end());
    
    cout<<endl<<s<< "\n"<<endl;
    }
    

    Now here's the funny part, I get no errors whatsoever for this one, but the debug crashes as soon as I select the custom letter removal feature. Here's what it says:

    "terminate called after throwing an instance of 'std::length_error' what(): basic_string::_S_create          This application has requested the Runtime to terminate it in an unusual way. Please contact the application's support team for more information."

    Any help would be much appreciated, stuck at this one for a week now!

    P.S. Is it okay if I or a moderator deletes this question after it's been answered? I'm quite sure I'm not the first one who asks this, but, as before-hand mentioned, I keep getting these errors, even after following answers from similar questions.

  • Admin
    Admin over 7 years
    Thank you. Both yours and @baddger964 's answers seems to have solved this problem. However, what exactly happens to the char s[ ] array after I have converted it using string str(s) ? My code consists of some loops that will make it go back to certain points, where the operator is required to insert a word/phrase based on the cin.getline format (to be as detailed as possible, you have to insert a sentence which equals 'cin.getline(service, 50)' ; depending on your input, the standard will have to apply some char functions to the s array). Thank you for your time.
  • Admin
    Admin over 7 years
    Thank you, this has proven useful!
  • Admin
    Admin over 7 years
    Yep, that's how it works indeed, but for my code you do not have an already known position for the element you want removed; the operator inputs a number/letter and the program would have to search for that element in the string array... which brings it to another questioning: what algorithm should I apply after the strchr function, that will basically do what you pinpointed in your example above? Thanks for the highly detailed answer, though, I appreciate it.
  • Some programmer dude
    Some programmer dude over 7 years
    @Teo If you are using strchr, or any other C string function on this array, then it is a string and you should really be using std::string.
  • Some programmer dude
    Some programmer dude over 7 years
    @Teo As for your question in the comment, you can actually subtract two (related) pointers to get the difference. If you subtract s from the (non-null) pointer you get from strchr you get an offset that can be used as an index. Or you use the result from strchr directly in the call to memmove.
  • emlai
    emlai over 7 years
    "what exactly happens to the char s[ ] array after I have converted it using string str(s) ?" — Nothing. The characters are copied into the std::string object, and the array is left untouched.
  • Admin
    Admin over 7 years
    Thanks @tuple_cat, that's great!