0

Trying to solve this Problem. I have written the code which looks like this:

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

using namespace std;

int i,j,n,t;

int main()
{
cin>>t;

while(t--)
{
    vector <string> dr,rd;
    string a,b;
    cin>>n;
    cin.ignore();

    for(i=0;i<n;i++)
    {
        a.clear(),b.clear();
        getline(cin,a);
        j=a.find(" on ");
        b=a.substr(j,a.size()-1);
        a.resize(j);
        dr.push_back(a);
        rd.push_back(b);
    }

    for(i=0,j=rd.size()-1;i<rd.size();i++,j--)
    {
        cout<<dr[i]<<rd[j]<<endl;
    }
    cout<<endl;
}

return 0;
}

It works perfectly, at least on the given example when I run it using file I/O(fstream) but when i use console I/O as is the code given it crashes after reading the second number for test case.

Like this:
2
4
Begin on Road A
Right on Road B
Right on Road C
Left on Road D (reads and prints what should be printed until here)
6 (reads this and goes to new line)
(crashes) Begin on Old Madras Road
Left on Domlur Flyover
Left on 100 Feet Road
Right on Sarjapur Road
Right on Hosur Road
Right on Ganapathi Temple Road

Where is my mistake?

Mike Kinghan
  • 55,740
  • 12
  • 153
  • 182
classius
  • 115
  • 1
  • 6

1 Answers1

1

Welcome to SO.

If you say that your program "crashes", you should also say how it crashes and if possible quote the exact output that shows how it is crashing. Many people who might be able to help will not bother if they have to work out what the problem really is as well as solve it.

If your question is about a coding problem, like yours, it is also important to tag your question with the name of the programming language you are coding in - in your case C++. If you don't, then most C++ programmers who could help will simply never notice your question. (I have now tagged your question C++.)

And if you are programming in a compiled language, like C++, it is also helpful to say what compiler you are using, and what version of that compiler, - e.g. "GCC 4.7.2", "MS VC++ 2012" - because then programmers can try to reproduce the problem using the same one. It is quite common for coding problems to be in some way dependent on the compiler or compiler version being used.

I have built your program with GCC 4.7.2 and clang 3.2 on Linux and it executes the test case that you have given with no problems when I enter the input lines from the console.

This leaves me to speculate what you mean when you say it "crashes", and this is what I speculate:

I think you may mean that after you enter "6", the program terminates with an error diagnostic something like:

terminate called after throwing an instance of 'std::out_of_range'
  what():  basic_string::substr
Aborted

If I am wrong about that, feel free to stop reading now.

If I am right about that, then the diagnostic is telling you that in your call a.substr(j,a.size()-1), the index j is out of range.

If j is out of range at that point, then it must have got out of range as a result of the preceding line j=a.find(" on "). And that can only mean that a.find(" on ") failed to find " on " at any place in a.

Look up the documentation of std::string::find, e.g. here and read about the returned value:

If no matches were found, the function returns string::npos.

which is (size_t)-1, and is assuredly out of range.

Next, if a.find(" on ") is failing, that can only mean that the preceding line getline(cin,a) is failing at that point to read a line from cin that contains " on ". And we know that is true! Because you say that the program crashes right after it has read "6" and printed the required newline.

So that getline(cin,a) must be reading a line from the console after the "6" but before "Begin on Old Madras Road". An empty line. And that is what it would do if you happened to press [Enter] just a bit too long when entering the "6", so that the keyboard buffer contains "6\n\n", or maybe "6\n\n\n", instead of just "6\n". Your call to cin.ignore(), after reading the integer, will consume only one following character, because you are accepting the default arguments of:

std::istream& std::istream::ignore(std::streamsize n = 1, int delim = EOF)  

As I say, your program works fine for me. But I can make it crash in the way I described by pressing [Enter] either too long when I enter the test case line-count - either the first time or the second time - or by pressing [Enter] again after I have done so. Either way, I am inputting an empty line to getline(cin,a).

If I am right so far, then a serious error in your code is that you are not checking that std::string::find() is successful on an input string and just assuming that it is. Even if I am not right, that is serious error.

You can stop your program from crashing when the user's finger stays too long on [Enter] by ensuring that cin ignores all the newlines it can read after cin>>n. You must look at the next character, if any, without extracting it; check if it is a newline, and if it is then extract it and repeat. Replace cin.ignore() with:

for (   ;cin.peek() == '\n'; cin.ignore()){}

However this will not prevent your program from crashing in the same way if the user happens to mistakenly enter a route-line that does not contain " on ": try it with "Left On Domlur Flyover".

To fix this bug you must check that j=a.find(" on ") is successful and handle the situation when it is not. Here is complete fix for what I think is the problem, built with GCC 4.7.2 and clang 3.2:

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

using namespace std;

int main()
{

int t;

cin>>t;

while(t--)
{
    vector <string> dr,rd;
    string a,b;
    size_t i,j,n;
    cin>>n;
    for (   ;cin.peek() == '\n'; cin.ignore()){}

    for(i=0;i<n;i++)
    {
        a.clear(),b.clear();
        getline(cin,a);
        j=a.find(" on ");
        if (j == string::npos) {
            cout << "Invalid direction. Try again" << endl;
            --i;
            continue;
        }
        b=a.substr(j,a.size()-1);
        a.resize(j);
        dr.push_back(a);
        rd.push_back(b);
    }

    for(i=0,j=rd.size()-1;i<rd.size();i++,j--)
    {
        cout<<dr[i]<<rd[j]<<endl;
    }
    cout<<endl;
}

return 0;
}

Notice I have also corrected a couple of minor defects:-

  • No need for any global variables. It is good practice to avoid globals if possible and to declare variables in the smallest possible scope.

  • I have changed the type of i,j,n from int to size_t. Variables that used to hold the return value of some_class::some_method() or the return value of some_function(), or are compared with that return value, are best declared with the same type as that return value to avoid bugs and compiler warnings: string::find() and vector::size() return size_t, which is not the same as int.

P.S. Investigate what goes wrong, either with your original program or mine, if the user mistakenly enters one of the integer inputs as, say, "y" or "6y" instead of "6". How could you fix that?

Mike Kinghan
  • 55,740
  • 12
  • 153
  • 182