Thread: Getline vs Get Problems, and Input Buffer Confusion

  1. #1
    Registered User
    Join Date
    Sep 2010
    Posts
    2

    Question Getline vs Get Problems, and Input Buffer Confusion

    Hi all,

    I recently started learning C++, and have been using the following book:

    C++ Without Fear: A Beginner's Guide That Makes You Feel Smart

    I'm also using Microsoft Visual C++ Express 2010, if it matters.

    It's very approachable, and so far I've gotten a lot out of it. I'm currently doing an exercise with strings and it cautions against buffer overflow, but simply suggests making inordinately large char arrays to catch potential problems. This seems like a terrible solution, so I started digging for a better answer.

    The best I've found is using the cin.get() function along with cin.sync(). Here's the code I'm using:

    Code:
    #define STRLENMAX 4
    #include <iostream>
    #include <string.h>
    #include <math.h>
    #include <stdlib.h>
    using namespace std;
    
    int get_number();
    
    int main() {
      int x;
      for (;;) {
        cout << "Enter a number (press ENTER to exit): ";
        x = get_number();
        if (x == 0)
          break;
        cout << "The square root of x is: " << sqrt(double(x)) << endl;
      }
    
      return 0;
    }
    
    int get_number() {
      char s[STRLENMAX];
    
      cin.get(s, STRLENMAX - 1);
      cin.sync();
      if (strlen(s) == 0)
        return 0;
      return atoi(s);
    }
    This seems to work fine, but there are a few issues I need some clarification on:

    1. If I change the cin.get to a cin.getline function, the program no longer works as expected. The example I saw used the getline function just fine, but it doesn't work here - instead it appears to not be flushing the input buffer as expected when entering a large number. Can anyone explain why this is?

      For example, using the .get function, if I type a 3 digit number, the square root of the first 2 digits is returned appropriately, the remaining input is dropped, and I can continue using the program just fine. Using the .getline function, if I type a 3 digit number, the square root of the first 2 digits is returned appropriately, but the program ends (as if I had hit enter with no input). Based on the descriptions here and here, the reverse should be happening, since getline should be dropping the \n delimiter.
    2. I'm defining the STRLENMAX constant as 4, so I would expect I should be able to process a 3 digit number (since I read STRLENMAX - 1 (3), plus the null byte). From my testing, I can only process 2 digit numbers. Why is this?
    3. This may all be unneccessary, but I was trying to find the 'ideal' way to read input from the console, and this was the best I could come up with. Is there a better, easier, more efficient way?


    Thanks in advance!

  2. #2
    Novice
    Join Date
    Jul 2009
    Posts
    568
    1. See here about failure state.
    2. See get() and getline(). Both functions already read n-1 characters. So they are reading ((STRLENMAX -1) - 1) for you. Everything appears to work as documented.
    3. Someone will be along shortly.

  3. #3
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Ideal is never easy. I made a function that can (hopefully) deal with input in a safe and easy manner. The code is here if you wish to study it (not beginner friendly):
    Code:
    #ifndef INPUT_20081018_H
    #define INPUT_20081018_H
    
    #include <boost/format.hpp>
    #include <iostream>
    #include <string>
    #include <sstream>
    
    namespace Stuff
    {
        namespace Input
        {
            template<typename StrT> void StreamOut(const StrT& Output) { std::wcout << Output; }
            void StreamOut(const char* Output) { std::cout << Output; }
            void StreamOut(const std::string& Output) { std::cout << Output; }
            template<typename T> void StreamOut(const boost::basic_format<T>& Output) { StreamOut(Output.str()); }
    
            template<typename StrT, typename T> bool StreamIn(StrT& Stream, T& Buffer)
            {
                std::wstring Answer;
                std::getline(Stream, Answer);
                std::wstringstream StrStream(Answer);
                void* SuccessPtr = (StrStream >> Buffer);
                return SuccessPtr != nullptr;
            }
    
            template<typename T> bool StreamIn(T& Buffer) { return StreamIn(std::wcin, Buffer); }
    
            // Read function overloads
            // Read function with no validator func
            template<typename DataT, typename StrT1>
            void ReadNoVal(DataT& Out, const StrT1& Question)
            {
                Read(Out, Question, L"Invalid input!\n", L"Invalid input!\n", [](const DataT&){ return true; });
            }
    
            template<typename DataT, typename StrT1, typename StrT2>
            void ReadNoVal(DataT& Out, const StrT1& Question, const StrT2& ErrorType)
            {
                Read(Out, Question, ErrorType, L"Invalid input!\n", [](const DataT&){ return true; });
            }
    
            template<typename DataT, typename StrT1, typename StrT2, typename StrT3>
            void ReadNoVal(DataT& Out, const StrT1& Question, const StrT2& ErrorType, const StrT3& ErrorValidation)
            {
                Read(Out, Question, ErrorType, ErrorType, [](const DataT&){ return true; });
            }
    
            // Read functions with validator function
            template<typename DataT, typename StrT1, typename Validator>
            void Read(DataT& Out, const StrT1& Question, Validator ValidatorFnc)
            {
                Read(Out, Question, L"Invalid input!\n", L"Invalid input!\n", ValidatorFnc);
            }
    
            template<typename DataT, typename StrT1, typename StrT2, typename Validator>
            void Read(DataT& Out, const StrT1& Question, const StrT2& ErrorType, Validator ValidatorFnc)
            {
                Read(Out, Question, ErrorType, L"Invalid input!\n", ValidatorFnc);
            }
    
            template<typename DataT, typename StrT1, typename StrT2, typename StrT3, typename Validator>
            void Read(DataT& Out, const StrT1& Question, const StrT2& ErrorType, const StrT3& ErrorValidation, Validator ValidatorFnc)
            {
                for (;;)
                {
                    StreamOut(Question);
                    if (!StreamIn(Out))
                    {
                        StreamOut(ErrorType);
                        continue;
                    }
                    if (ValidatorFnc(Out))
                        break;    
                    StreamOut(ErrorValidation);
                }
            }
        }
    }
    
    #endif // INPUT_20081018_H
    But the point is, you should use std::getline and std::string.
    Also, did the book really tell you to use #defines for constants and cin.get with char buffers? If so, you need a new book. I recommend Accelerated C++; but there may be others in the C++ book thread.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  4. #4
    Registered User
    Join Date
    Sep 2010
    Posts
    2
    Thank you msh - I clearly wasn't paying enough attention to the resources I posted regarding get and getline; you're correct that I was actually doing a -2 instead of -1. Odd that this book uses that convention for those functions, since it's clearly wrong...

    Elysia, thanks for the code! You're correct - it's not beginner friendly, but I understand a bit of what's going on and it gives me something to work towards (if not necessarily developing it, just fully understanding it)!

    The book did in fact note #define should be used for constants, but it was using the cin.getline function for input (now - previously it was just using cin with the >> stream operator). I used the cin.get function because of the problem I noted, which I still don't understand (although msh did provide a link). Still trying to figure out what's going on there...

    I've switched to C++ Primer Plus, Fifth Edition - it's a MUCH longer tome, but hopefully I'll get some better experience from it. I'll take a look at the Accelerated C++ text as well, thanks for the suggestion!

    Still surprised there's no easy / friendly way to read and clear text from the input buffer - seems like it would be a common enough problem to warrant addressing...

    Thanks again to you both!

  5. #5
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    The thing is that the stream operators are really suited for parsing and not so much for user input. Therefore it becomes a little complicated.
    But basically, the solution is to read an entire line, then convert it to appropriate type. This nets you an easy solution (though, of course, not perfect).
    My little code snippet is meant to be generic code to handle all sorts of types and ANSI/Unicode, and to keep prompting if invalid input is entered.
    So it's a little much, but if you look closely, the "actual" code is only just a few lines.

    Also, constants are better done with "const". Say, const int instead of define. Define should be avoided, if possible. It can cause subtle and difficult to debug errors.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Bad output -- possible input buffer problems
    By jake123 in forum C Programming
    Replies: 8
    Last Post: 02-18-2008, 03:36 PM
  2. For loop problems, input please.
    By xIcyx in forum C Programming
    Replies: 2
    Last Post: 04-22-2007, 03:54 AM
  3. Need help with structures
    By yoursport in forum C++ Programming
    Replies: 8
    Last Post: 04-20-2005, 11:59 AM
  4. text input buffer clearing
    By red_Marvin in forum C++ Programming
    Replies: 4
    Last Post: 03-20-2003, 03:17 PM
  5. Input Problems and C# (CSharp) Tutorials
    By Grayson_Peddie in forum C# Programming
    Replies: 4
    Last Post: 02-27-2003, 10:45 PM