-3

I am trying to make a code that does run=length encoding based on the value entered by the user. Only accepts lower-case letters. Using two vectors. The problem is it doesn't with all examples: Example: zzzzzzzz output: (terminate called after throwing an instance of 'std::out_of_range' what(): basic_string::at: __n (which is 8) >= this->size() (which is 8) exit status -1)

How can it be solved?

    #include <iostream>
    #include <string>
    #include <vector>

    using namespace std;

     bool is_small_letter(char c){
      return c >= 'a' && c <= 'z';
    }

    void runLength(string str, vector<char>& charachters, vector<int>& length) {
      int count;
      int i;

      for (i = 0; i < str.size(); i++) {
        if (!is_small_letter(str.at(i))) {
          throw runtime_error("invalid input");
        }

        **count = 1;
        if (i == str.size() -1) {
          // do nothing
        } else {
          while (str.at(i) == str.at(i + 1)) {
            count++, i++;
          }**  //changed following one answer advice.
        }
        charachters.push_back(str.at(i));
        length.push_back(count);
      }
    }

    int main() {
      string str;
      vector<char>charachters;
      vector<int>length;

      cout << "Enter the data to be compressed: ";
      cin >> str;
      try {
        runLength(str, charachters, length);

        cout << "The compressed data is: ";
        for (int i = 0; i <= length.size() - 1; i++){
          cout << length.at(i) << charachters.at(i);
        }

      } catch (runtime_error &excpt) {
         cout << endl << "error: " << excpt.what();
      }

      return 0;
    }
Jongware
  • 22,200
  • 8
  • 54
  • 100
J. Doe
  • 13
  • 4
  • 2
    Why are you doing `i < str.size() - 1` in your for loop? – NathanOliver Oct 01 '18 at 19:28
  • when I do i < str.size it goes out of range. What are your suggestions? – J. Doe Oct 01 '18 at 19:31
  • Okay, but only running the loop to `str.size() - 1` is going to skip the last element in the string. You'll have to add extra processing to your function to check that last character and handle it appropriately. That or rework the loop to make running to `i < str.size()` not be an error. – NathanOliver Oct 01 '18 at 19:33
  • Can you please explain more how I can do that? – J. Doe Oct 01 '18 at 19:34
  • _"when I do i < str.size it goes out of range."_ Where does it go out of range? It sounds like that's your real bug and your change to `size() - 1` is just hiding that bug. – Drew Dormann Oct 01 '18 at 19:38
  • `for (i = 0; i < str.size(); i++)` and `while (i < str.size() - 1 && str.at(i) == str.at(i + 1))` Btw: `is_small_letter()` does already exist. It is called `islower()` from ``. – Swordfish Oct 01 '18 at 19:41
  • it says: exit status -1 still after modifying the code – J. Doe Oct 01 '18 at 19:43
  • Your loop is confusing. You have a `for` loop that increments `i` automatically, and then within that loop you have a `while` loop that increments `i` conditionally. That is a recipe for something to go wrong. – PaulMcKenzie Oct 01 '18 at 19:48
  • cant you try to use std::map instead to keep a count of each letter? – Yucel_K Oct 01 '18 at 19:54
  • The only thing is I am a beginner and we still haven't learned to do std::map – J. Doe Oct 01 '18 at 19:57
  • @Papipone Rolled back your edit cause you changed the semantics of the code. – Swordfish Oct 01 '18 at 20:15
  • @Swordfish Thanks, I will be more careful. – Papipone Oct 01 '18 at 20:20
  • *The only thing is I am a beginner and we still haven't learned to do std::map* [No time like the present to learn.](https://en.cppreference.com/w/cpp/container/map) – user4581301 Oct 01 '18 at 20:20
  • 1
    @J.Doe `while (i < str.size() - 1 && str.at(i) == str.at(i + 1))` and get rid of the `if() ... else`. – Swordfish Oct 01 '18 at 20:25
  • @Swordfish it says: terminate called after throwing an instance of 'std::out_of_range' what(): basic_string::at: __n (which is 6) >= this->size() (which is 6) exit status -1 – J. Doe Oct 01 '18 at 20:48
  • For which input? – Swordfish Oct 01 '18 at 20:48
  • @Swordfish aaaee – J. Doe Oct 01 '18 at 20:54
  • I copied the code from your question, removed the `**`, replaced the `while` with my suggestion and removed the `if() ... else`, compiled, run, input "aaaee". Can't reproduce the error. You are doing something different. – Swordfish Oct 01 '18 at 20:58
  • @Swordfish I think I was doing something wrong, it works now though. Thanks. – J. Doe Oct 01 '18 at 21:12

2 Answers2

1

unfortunately mbonness'es answer will only delay your demise.

This happens because it didn't consider the case of what would happen if at least the last 2 characters in a string were same. for example what would happen if player entered abcdd ? since the while loop was iterating the i, it would go out of the scope within the while loop. hence the crash would happen.

However, there is also another issue you haven't thought about yet. what happens if you have multiple characters in a string that are not next to each other? for example ababab? the current code will not handle such senario.

This is why unfortunately you have to loop the entire str vector twice. and store each character one at a time. and whenever you start next iteration you have to check if the character is already in the characters vector. if so you ignore the scan and move on to the next.

let's look at the function body;

  void runLength(string str, vector<char>& charachters, vector<int>& length) {
      int count;
      int i;

      for (i = 0; i < str.size(); i++) {

so what's happening in this for loop? we keep running as long as i is less than str.size(). note that although str.size() is not a zero-based index it will work fine. why? because we are not using <= operators. we are using < operator.

next, we going to need a bool variable to check if the current str.at(i) already added to the vector<char>charachters.

bool charAlreadyExist = false;

let's do our second inner loop to check if the characters contain the current element of the str.

 for(int l = 0; l < charachters.size(); l++)
    {
        if(charachters.at(l) == str.at(i))
        {
           charAlreadyExist = true;
           break;
        }
    }

the next loop only starts if charAlreadyExist is false

 if (charAlreadyExist == false)
        {
          for(int j = 0; j < str.size(); j++)
          {
              if (str.at(i) == str.at(j))
              {
                count++;
              }
          }

Notice that in the above statement we no longer increment i. this was the reason why mmbonnes'es answer would fail

the rest of the function code would be similar to what you did.

Here is the full function code. but try to implement it with out looking at it first though. it will help you understand the code better

void runLength(string str, vector& charachters, vector& >!length)
{
    int count;
    int i;
    for (i = 0; i < str.size(); i++)
    {
        count = 0;
        bool charAlreadyExist = false;
        for(int l = 0; l < charachters.size(); l++)
       {
            if(charachters.at(l) == str.at(i))
            {
               charAlreadyExist = true;
               break;
            }
        }
        if (charAlreadyExist == false)
        {
          for(int j = 0; j < str.size(); j++)
          {
              if (str.at(i) == str.at(j))
              {
                count++;
              }
          }
          charachters.push_back(str.at(i));
          length.push_back(count);
          if (!is_small_letter(str.at(i)))
          {
              throw runtime_error("invalid input");
          }
        }
    }
 }

so now when you type in abababab, it will print the correct amount of a's and b's even though they are not next to each other.

next thing for you to do is what to do if user enters "abab ababab". i will leave that to you to figure out . hint.. cin<< will not work for getting multple words in a line from users.

Yucel_K
  • 688
  • 9
  • 17
  • I don't know if spoilerizing the complete code is such a good idea. I kinda get why you did it, but an answer should be in your face and any lazy Cargo Cultist is still just going to cut and paste it. – user4581301 Oct 01 '18 at 21:49
  • i hope not. it would be a good waste of 20 min of how to figure out to use spoilers T_T – Yucel_K Oct 01 '18 at 21:53
-3

I think the problem is your for loop is not interating over the whole input string. Try this:

#include <iostream>
#include <string>
#include <vector>

using namespace std;

 bool is_small_letter(char c){
  return c >= 'a' && c <= 'z';
}

void runLength(string str, vector<char>& charachters, vector<int>& length) {
  int count;
  int i;

  for (i = 0; i < str.size(); i++) {
    if (!is_small_letter(str.at(i))) {
      throw runtime_error("invalid input");
    }

    count = 1;
    if (i == str.size() -1) {
      // do nothing
    } else {
      while (i < str.size() - 1 && str.at(i) == str.at(i + 1)) {
        count++, i++;
      }
    }
    charachters.push_back(str.at(i));
    length.push_back(count);
  }
}

int main() {
  string str;
  vector<char>charachters;
  vector<int>length;

  cout << "Enter the data to be compressed: ";
  cin >> str;
  try {
    runLength(str, charachters, length);

    cout << "The compressed data is: ";
    for (int i = 0; i <= length.size() - 1; i++){
      cout << length.at(i) << charachters.at(i);
    }

  } catch (runtime_error &excpt) {
     cout << endl << "error: " << excpt.what();
  }

  return 0;
}
mbonness
  • 1,612
  • 1
  • 18
  • 20