1

I want to get an int and a float in the same line using scanf() which I know how to do, but I also want to be able to quit by entering one input ("-1") how can I do this?

while (input != -1){
  
    printf("Enter: ");
    scanf("%d %f", &input1, &input2);

    if(input1 == -1){
      input = -1;
}
Steve Summit
  • 45,437
  • 7
  • 70
  • 103
plauwh
  • 21
  • 3
  • 2
    You can’t. What stops you from calling `scanf()` twice? – Dúthomhas Sep 19 '22 at 18:20
  • What is the problem with the code you show? Please take some time to refresh [the help pages](http://stackoverflow.com/help), take the SO [tour], read [ask], as well as [this question checklist](https://codeblog.jonskeet.uk/2012/11/24/stack-overflow-question-checklist/). – Some programmer dude Sep 19 '22 at 18:20
  • You're asking for `scanf()` to read two input values, but only provide one. You need to handle input differently. Read the line in, then parse the data afterwards. If the line is just `"-1"`, then you can exit like normal, otherwise, treat it as a line of two inputs. – Jeff Mercado Sep 19 '22 at 18:20
  • Or read the line with `fgets()` and apply `sscanf()` instead. – Weather Vane Sep 19 '22 at 18:24
  • 1
    `scanf` is best for simple, really simple input. It breaks down pretty badly for more complicated things — and "read two things" is already complicated enough to be a questionable idea. And something like "read two things, but if the first one is -1, don't wait for the second one" is right out. If you want to keep using `scanf`, I'd say you'll need to adjust your goals to do something simpler. If you do still want to do this slightly-complicated thing, your best bet is to [use something other than scanf](https://stackoverflow.com/questions/58403537). – Steve Summit Sep 19 '22 at 18:28
  • 2
    `scanf` doesn't know about lines because it treats the space characters and newline characters as whitespace characters, and makes no distinction between them. The user may think that lines are significant, but the user is mistaken. So to make the user and `scanf` play well together, you need a different method to the end the loop. One approach is to tell the user to type `q` to quit. **Then check the return value from `scanf`.** `scanf` will return 2 when the user enters two numbers on a line, and 0 when the user types `q` on a line. – user3386109 Sep 19 '22 at 18:32
  • @Dúthomhas: Re “You can’t”: You can. – Eric Postpischil Sep 19 '22 at 18:48
  • 2
    IMO, if you want to process lines, the first step is to read the line (using `fgets()` or POSIX `getline()`, for example), and then parse the content that was read. The parsing can be done using `sscanf()` or other tools. Note that `scanf()` et al do not care about newlines except to the extent they separate other values — unless you work extremely hard. In particular, both `%d` and `%f` (and all other numeric formats; also `%s`) skip white space before processing the data for their conversion, and the definition of white space includes newlines. – Jonathan Leffler Sep 19 '22 at 19:04

2 Answers2

0

This shows a crude method to do it by asking scanf to scan for spaces and tabs between the two inputs. If the user enters only one input and presses Enter/Return, there will be a newline character in the stream, and scanf will not accept it for the space and tab request. It will return 1 to indicate only one input was assigned.

For demonstration, sscanf is used in place of scanf.

In %*[ \t], the * tells scanf not to store any results from this conversion, and [ \t] says to match one or more space or tab characters. If there is a new-line character in the buffer after the “-1”, this match will fail, and scanf will stop.

Note this is fragile; if the user enters a space or tab after “-1”, it will match the %*[ \t], and then scanf will continue with the %d.

Also note that using scanf in this way is largely used for facilitating early learning of programming. Later on, more refined input processing is done with other techniques.

#include <stdio.h>


static void Test(const char *Input)
{
    printf("Sample input is %s", Input);

    int input1;
    float input2;
    int result = sscanf(Input, "%d%*[ \t]%f", &input1, &input2);
    switch (result)
    {
        case EOF:
            printf("Error, input failure occurred.\n");
            break;

        case 0:
            printf("Error, no inputs read.\n");
            break;

        case 1:
            if (input1 == -1)
            {
                printf("User entered -1.\n");

#if 0
    /*  When doing this with input from a stream, instead of a string, you may
        wish to include this code to consume the rest of the line the user
        entered.
    */
                //  Consume rest of line.
                int c;
                do
                    c = getchar();
                while (c != EOF && c != '\n');
#endif
            }
            else
                printf("Error, only one input but is not -1.");
            break;

        case 2:
            printf("Two inputs:  %d and %f.\n", input1, input2);
            break;
    }
}


#define NumberOf(a) (sizeof (a) / sizeof *(a))


int main(void)
{
    const char *SampleLines[] =
    {
        "2 3.5\n",
        "-1\n",
    };

    for (size_t i = 0; i < NumberOf(SampleLines); ++i)
        Test(SampleLines[i]);
}
Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • 4
    My own opinion is that once we're uttering obtuse locutions like `%*[ \t]`, we're well past "facilitating early learning of programming" and falling deeply into outright hackery, but YMMV. :-) – Steve Summit Sep 19 '22 at 18:58
  • 1
    If the user types `1` followed by a space and then a newline, the `scanf()` will read the blank for the `%*[ \t]` non-assigning conversion, and then continue to wait for the numeric input for `%f`. If you want to read lines, read lines and then parse the line. – Jonathan Leffler Sep 19 '22 at 19:08
  • @JonathanLeffler: I presume you mean “types `-1`”. The answer already says the `scanf` will continue in this case and cautions against treating `scanf` as a refined way of processing input. – Eric Postpischil Sep 19 '22 at 19:13
  • 1
    Also, this is kind of cheating, since the EOL is managed before `*scanf()` gets it. OP’s conditions were that input processing stop if the first _valid_ input is `-1`, only using `scanf()`. – Dúthomhas Sep 19 '22 at 19:25
0

Given the following conditions:

  • each line contains exactly one or two inputs
  • the first input is always a valid integer
  • using only one scanf() family function

then you cannot do it in C. There must be a logical break to decide how to continue processing the line somewhere, and a single scanf() can’t do it alone.

Option 1 • Break input into lines and then process with scanf

Eric Postpischil’s answer shows a way to do it if you preprocess each line as a separate input string — by transforming EOL into EOF (or, more accurately, end of string).

You can break input into lines any way you feel comfortable. The simplest and least problematic way would be to use a library function like fgets() or readline().

Option 2 • Invoke scanf multiple times

It is as simple as saying:

while (1)
{
  if (scanf( "%d", &input1 ) != 1) break;
  if (input1 == -1) break;

  if (scanf( "%f", &input2 ) != 1) complain_or_something();

  do_stuff_with_inputs( input1, input2 );
}

The brevity trap

C’s syntax encourages programmers to try to write things with brevity, but this should not be your goal. Rather, your goal should be readability and correctness — even if this means code that requires two or three lines to express itself.

If you find that a specific chunk of code would definitely benefit from some higher-level abstractions, write a helper function:

bool read_int_and_float( int * n, float * f )
{
  if (scanf( "%d", n ) != 1) return false;  // EOF or error       -->  false
  if (*n == -1) return false;               // first input == -1  -->  false
  if (scanf( "%f", f ) != 1) return false;  // EOF or error       -->  false
  return true;                              // both inputs good   -->  true
}

That function cannot be misread, and as it only does one task (obtaining a specifically-formatted line of input), managing your headspace becomes a billion times easier.

So now you can write brief, readable code where it counts:

while (read_int_and_float( &input1, &input2 ))
{
  do_stuff_with_inputs( input1, intput2 );
}

You will find that correct and readable code lends itself to rather succinct structures anyway, but don’t shoot yourself in the foot by limiting yourself with a desire to be too pretty.

Dúthomhas
  • 8,200
  • 2
  • 17
  • 39