1

I am learning the language C by myself and with the help of internet. I came across an exercise, and I was able to read in everything with integers and double, but allowing the user to type in a full sentence and store it in a variable has given me hard time. Can someone explain how I can get a sentence from the user, and store it in a variable. I have tried many things, such as [%^\n] with scanf, and also fget but I am having some trouble. For some reason, it is not working.

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>

int main() {
    int i = 4;
    double d = 4.0;
    char s[] = "Orange ";


    // Declare second integer, double, and String variables.
    int secondInt;
    double justDouble;
    char variable[500];

    // Read and save an integer, double, and String to your variables.
    scanf("%d", &secondInt);
    scanf("%lf", &justDouble);
    scanf("%[^ \n]", variable);


    // Print the sum of both integer variables on a new line.
    printf("%i\n ", i + secondInt);

    // Print the sum of the double variables on a new line.
    printf("%.1lf\n ", d + justDouble);

    // Concatenate and print the String variables on a new line
    printf("%s ", s);
    printf("%s ", variable);
    // The 's' variable above should be printed first.

    return 0;    
}
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • @xing, it didn't work. – Samjohns081998 Feb 17 '21 at 17:02
  • 1
    @xing method works on my machine. Are you sure you're recompiling your program? – Dock Feb 17 '21 at 17:05
  • 2
    `scanf` is -- *barely* -- good enough for simple, simple input. But it's no good for complicated input, and (in my opinion) trying to do anything complicated with `scanf` is purely a waste of time. I recommend you use `%s` to read strings that don't contain whitespace, and when the time comes to read strings that do contain whitespace, abandon `scanf` forever and use [superior input techniques](https://stackoverflow.com/questions/58403537), such as `fgets`. – Steve Summit Feb 17 '21 at 17:08
  • But then the other issue is that if you use `fgets` to read strings, you have to use it to read your numbers, too, because you can't mix `fgets` and `scanf` in one program. – Steve Summit Feb 17 '21 at 17:11
  • 1
    @Samjohns081998 If xing's solution didn't work for you, it may be that you left out the leading space in `" %499[^\n]"`. (You may be saying, "But that's ridiculous, and so easy to overlook! How come the leading space is so significant in this situation?" And you'd be right. But this proves: `scanf` is not a very good function. In fact I don't recommend trying to use `%[...]` at all: by the time you're using it, you're past the point you should have abandoned `scanf` in favor of something better overall.) – Steve Summit Feb 17 '21 at 17:13
  • @SteveSummit, what do you mean my leading space? would you recommend me write the whole program in fgets, since it better than scanf? – Samjohns081998 Feb 17 '21 at 17:16
  • 2
    @Samjohns081998 he means the leading space in `" %499[^\n]"` – Dock Feb 17 '21 at 17:17
  • 1
    @Samjohns081998 xing suggested `" %499[^\n]"`. I suspect you may have used `"%499[^\n]"`. See the difference? – Steve Summit Feb 17 '21 at 17:17
  • 2
    @Samjohns081998 And, yes: As soon as you are ready to, and if it won't get you in too much trouble with your instructor, I recommend that you abandon `scanf`, and write a program like this using `fgets` only. See [this question](https://stackoverflow.com/questions/58403537) for help. It will be a bit scary at first, and it will be a certain amount of work, but it will be work you can build on, as opposed to working with `scanf`, which is a complete dead end. – Steve Summit Feb 17 '21 at 17:19
  • @SteveSummit , I didn't have the leading space. – Samjohns081998 Feb 17 '21 at 17:20
  • @SteveSummit, it worked. Thank you so much guys. I was stuck on this for a few hours. I didn't find anything on internet that explained the leading space and number after % – Samjohns081998 Feb 17 '21 at 17:22
  • @Dock, Thank you for explaining the leading space. – Samjohns081998 Feb 17 '21 at 17:22
  • @xing, thanks for explaining the number after %. – Samjohns081998 Feb 17 '21 at 17:23
  • 1
    @Samjohns081998 So what happened was this: You were getting tired of how long it took you to walk home every day, and xing said, "Here, use my car, it goes real fast!" So you tried it, and indeed it went real fast, except it had no brakes, and you crashed halfway home. And it turns out the car *did* have brakes, except that to apply them you had to reach over and open the glove compartment and push a little button inside. So obvious! (I'm not trying to blame xing here, this is just one of the 17 different things that's horribly wrong with `scanf`.) – Steve Summit Feb 17 '21 at 17:23
  • If you are going to use `scanf` then you could spend **at least an hour** studying the [man page](https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/scanf-scanf-l-wscanf-wscanf-l?view=msvc-160) and associated pages such as [Format specification](https://learn.microsoft.com/en-us/cpp/c-runtime-library/format-specification-fields-scanf-and-wscanf-functions?view=msvc-160), and writing short test programs. There is plenty of material about `scanf` both in man pages and on Stackoverflow. – Weather Vane Feb 17 '21 at 17:25
  • @SteveSummit, wow that is a good story. There is a space between in the results. `16` ` 8.0` ` Orange is something.` I think xing mentioned it, but i don't understand how to take it out. – Samjohns081998 Feb 17 '21 at 17:25
  • 1
    Some explanation: the `scanf` conversion stops at the first character it cannot convert, which is typically (but not necessarily) a space or a newline, and that character remains in the input buffer. It will be read by the *next* `scanf()`. Format specifiers `%d` and `%s` and `%f` automatically filter such leading whitespace characters, but `%c` and `%[]` and `%n` do not. You can instruct `scanf` to do so by adding a space just before the `%`. – Weather Vane Feb 17 '21 at 17:29
  • 1
    @Samjohns081998 [overlaps Weather Vane's comment] The "space" is probably the newline (the Enter key) you pressed after typing `8.0`. Now, there was the same extra newline after the `16` you typed, too, so why didn't you need an extra space when you wrote `scanf("%lf", &justDouble)`? Because `%d` and `%lf` always skip leading whitespace, so you don't need the leading space, but `%[...]` is different. (There's a good reason, but it's still a difference, and an error-prone one. Have I said I hate `scanf`?) – Steve Summit Feb 17 '21 at 17:29
  • @SteveSummit thank You for your help. I really appreciated it. – Samjohns081998 Feb 17 '21 at 17:36
  • @WeatherVane, thank You for explaining. I understood much with your help. – Samjohns081998 Feb 17 '21 at 17:36
  • It does take some time to understand how `scanf` works / handles whitespace, and why it causes problems when mixed with other input functions. – Weather Vane Feb 17 '21 at 17:40
  • @SteveSummit, I spend some time learning fget and why scanf isn't good. Here is how did the code now. `fgets(secondInt, 5, stdin); newInt = atoi(secondInt); fgets(justDouble, 5, stdin); newDouble = atof(justDouble); fgets(variable, 500, stdin);` – Samjohns081998 Feb 17 '21 at 21:14
  • @SteveSummit, I basically got strings first, and then used fget to read them in, and then converted them. It seems more work, but it is much safer that you would agree. – Samjohns081998 Feb 17 '21 at 21:15
  • 1
    Be cautious about using `fgets(secondInt, 5, stdin)` — that works OK as long as the integer is typed in with not more than 3 digits and a newline. As soon as the user gets 'inventive' (e.g. typing 1000 where you expected at most 999), you're leaving debris in the input stream that will complicate the next operation. You should use something like `char buffer[4096]; if (fgets(buffer, sizeof(buffer), stdin) != 0) { …parse the buffer for the requisite data… } else { …handle EOF… }`. If you like, you can increase the buffer size. As shown, you should also check that each `fgets()` operation works. – Jonathan Leffler Feb 17 '21 at 23:55
  • 1
    @Samjohns081998 That's the right general approach. Two more tips: (1) In general, there's no point in making your input buffers just barely big enough like that. I would make your `secondInt` variable considerably bigger, at least 30, maybe 100, maybe 512, or as Jonathan Leffler suggested even bigger. (2) `atoi` and `atof` are fine for now (they're still what I often use), but they don't handle errors well, so `strtol` and `strtod` are generally recommended these days. – Steve Summit Feb 18 '21 at 12:22
  • @SteveSummit, ok I will rememeber to use strtol, and strtod next time. Can you explain what the `" %499[^\n]"` in this scanf? – Samjohns081998 Feb 18 '21 at 15:30
  • @Samjohns081998 I thought that `" %499[^\n]"` had been explained in comments by xing, Weather Vane, and me. Which aspect of it are you still wondering about? (I may not be able to answer: as I said, I don't think it's important to use or understand `%[...]` at all. It's sort of like a turbocharger on a tricycle, but incompatible with a real one, meaning that learning how to use it teaches you nothing about driving an actual, turbocharged car.) – Steve Summit Feb 18 '21 at 17:21

2 Answers2

1

This should do the trick by using fgets() in general

#include <stdlib.h>
#include <stdio.h>

int main()
{
  // variable to store the message
  char msg[100];

  // prompting the user to enter the message
  printf("Pls enter a msg: ");

  // using fgets() to retrieve a whole sentence from the user
  fgets(msg, 100, stdin);

  // printing the message to stdout
  printf("%s", msg);
}

You can learn more about fgets here: https://www.tutorialspoint.com/c_standard_library/c_function_fgets.htm

Let me know if anything is not clear so I can improve my answer

1

To work with scanf, you need to make sure that everything entered gets read. In your example, you first expect an integer, and then a double. What does the user type to 'finish' entering the integer? Probably a <RET> (or a blank) - and now you need to scanf these too! Or they will 'clog' the input stream.
For example, your second scanf could be scanf(" %lf"... - note the blank before the % sign, it will read (and discard) any number of whitespace (which is <RET>, <TAB>, <space>).

scanf is very powerful, but needs a lot of detail understanding to be used correctly. Most people don't get it, and therefore claim "it's old and bad and shouldn't be used".
In professional software, it is generally avoided; not because it's not capable, but because the chance is too high that is used wrong, or that it is encountered by a developer that changes it and messes it up.

Aganju
  • 6,295
  • 1
  • 12
  • 23
  • The reason `scanf` is not used is not merely that it's not capable and is too error prone: Even if you *can* get it to work, it's going to be two or three times more work than doing it some other way. That other way, whatever it is, is going to be easier *and* work better. So what's the point of using `scanf`? – Steve Summit Feb 18 '21 at 18:16