0

I'm working on a program that is supposed to take instruction from the user and do some stuff with it. It uses classes and objects to know what to do. However, some commands only require the user to input 2 things, and others 3, 4, etc. For example:

request> balance 2

This call is to retrieve the balance of customer 2. However this call:

request> deposit 2 30

This is supposed to deposit 30 dollars in to customer 2's acct.

How can I get the program to understand that the user can either be inputting 2 variables separated by a space, or more?

I have thought maybe taking the whole user input as a string and then parsing that may be easier...

char temp[257];
scanf("%s", temp);

However if you debug and output this so called string, it only outputs the last item the user entered.. so if a user entered

request> balance 3

It only grabs the 3 and stores it to temp...

I think you guys get the picture, I need to be able to feed the terminal multiple strings, ints, etc. and have it store each separate one into its own variable. Any help would be greatly appreciated!

Some dummy code for reference:

#include <iostream>
#include <stdio.h>
#include <cstdlib.h>

including namespace std;

int main()
{
    char temp[257];
    int x, y, z;

    printf("request: ");
    scanf("%s", temp);     // I need this to be able to scan more than one 
                           // thing, and know where to store them
    scanf("%s %d %d", temp, x, y);
    // because if I do this ^^, then it will just hang if I only need x and 
    // not y.

    // Do some random stuff w/ variables now

    return 0;

}
6
  • Is this a dup of stackoverflow.com/questions/35157995/parsing-using-scanf-in-c Commented Feb 9, 2018 at 2:51
  • reading over now, maybe, but mine is presented with more appeasement to the eye :D Commented Feb 9, 2018 at 2:53
  • 5
    Your first problem is that you're using the wrong language. This is not C++, but C. In C, if you want to read an entire line of text, as input, the function for that is called fgets(). You will read the entire line of text into a suitably-sized char array (checking for overflow, of course), and once the entire line of text is read, parse the read line for a valid command. Attempting to swallow input, one token at a time using scanf(), is just a recipe for frustration. As you've already discovered. It can be made to work, but is surprisingly hard and full of minefields. Just use fgets. Commented Feb 9, 2018 at 2:54
  • Im reading the fgets() syntax as wanting to read from a stream, how would I turn the user input into a stream or use it as the stream? Much appreciated. Commented Feb 9, 2018 at 3:05
  • Generally you need to read the line, tokenize it, then classify the first token. From there you should know how many other tokens you need for a valid command and what each of them represent, assuming they are positional. Commented Feb 9, 2018 at 4:31

2 Answers 2

1

While you have a solid answer, it is still a bit ambiguous whether you wanted a C++ or C solution to begin with. For the sake of completeness, you can use fgets (your stream is stdin) and then call sscanf to parse a varying number of items entered by the user, using a switch on the return to handle the different cases.

A short example allowing 1-input to show balance and 2-inputs to update balance could be done as follows. The program exits on the first matching failure (so you can just type 'q' to quit). input failures are ignored.

#include <stdio.h>

enum { NMMAX = 16, BUFSZ = 256 };  /* if you need constants, define them */

typedef struct {    /* simple struct for user name and balance (integer) */
    char name[NMMAX];
    long balance;
} account_t;

int main (void) {

    account_t accounts[] = {{"John", 12305},
                            {"Paul", 388541},
                            {"George", -38112},
                            {"Ringo", 1}};
    int naccts = sizeof accounts / sizeof *accounts;

    for (;;) {     /* loop continually until matching failure or user cancels */
        char buf[BUFSZ] = "";
        int acct = 0,
            rtn = 0;
        double bal = 0;
        fputs ("request> ", stdout);        /* display prompt */
        if (!fgets (buf, BUFSZ, stdin)) {   /* read input, check EOF */
            fprintf (stderr, "error: user canceled input (ctrl+d)\n");
            return 1;
        }
        /* separate inputs into acct and bal, rtn holds number of inputs */
        rtn = sscanf (buf, " %d %lf", &acct, &bal);
        if (rtn && acct > naccts) {  /* check if requested account valid */
            fprintf (stderr, "error: invalid account.\n");
            continue;
        }
        switch (rtn) {  /* switch on sscanf return */
            case 0  :   /* use matching falure to exit */
                goto finished;
                break;
            case 2  :   /* 2 inputs - update balance */
                accounts[acct].balance += (long)(bal * 100);
            case 1  :   /* 1 input - show balance */
                printf ("%-15s : $%.2f\n", accounts[acct].name, 
                        accounts[acct].balance / 100.0);
                break;
        }
    }
    finished:;

    return 0;
}

(note: case 2 : fall-through is intentional.)

Example Use/Output

$ ./bin/acctbal
request> 0
John            : $123.05
request> 0 -1.85
John            : $121.20
request> 1
Paul            : $3885.41
request> 1 .59
Paul            : $3886.00
request> 5
error: invalid account.
request>
request> q

Good luck with whichever language you end up writing it in.

Sign up to request clarification or add additional context in comments.

2 Comments

Never count the old lowly goto out. Perfect for near jumps and only way to break 2-loop (or scopes) at once (and not exit). This one ended up a cleverer way to approach this problem than I untended. By happy accident we will say...
It was just an element of fun....yes you handled it nicely. I don't count it out. :)
0

Your example code looks a lot like C except for the using namespace std; which you also shouldn't be doing but that is another topic. Here is the quick and dirty way to process user input (line all at once) using C++ constructs.

#include <iostream>
#include <sstream>

int main()
{
    std::string line;              ///< hold user input as string
    std::getline(std::cin, line);  ///< get the whole line at once
    std::stringstream ss(line);    ///< stream for processing line arguments

    /*std::string args;
    while (ss >> args)
    {
        // decide what to do with each input argument here
        std::cout << "args= "<< args << std::endl;
    }*/
    // if you "know" your input arguments you don't need a loop
    std::string command;
    int val1(-1);
    int val2(-1);
    ss >> command >> val1 >> val2;

    std::cout << "command = " << command << " val1="
    << val1 << " val2=" << val2 << std::endl;

    return 0;
}

8 Comments

I appreciate the feedback. I will fix the syntax for C. I may end up using this, however is there a way to do this without including a new library? This is a class assignment and am unsure if we can use libraries we have not gone over.
@lookingforlemons If you are talking about std::stringstream yes you can do without it, however std::string is C++ answer to char* and it's kind of fundamental to the whole OOP thing.
i'm actually referring to sstream... as I agree its fundamental, our professor insists we understand how to use *char when it comes to strings for now
so if in the while loop, the string read 'deposit 3 40', how could i say that string1 = 'deposit', int1 = 3, and int2 = 40?
If you are using C++11 or later, then string to integer int foo = std::stoi(args) otherwise you can use std::stringstream but since you haven't learned that you can use an old friend from C int i; sscanf(args.c_str(), "%d", &i);
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.