[c][explained] Problems with scanf

Поделиться
HTML-код
  • Опубликовано: 13 окт 2020
  • I do my best to credit the sources and people that helped me with the content. For this one, I did get help from people on Reddit and Discord. :D
    Some of the articles that I read to make this:
    / fun-with-scanf-in-c
    stackoverflow.com/a/22902085
    I do know about the places where some of the lines are not consistent with the opacity, but meh, didn't do a retake, I apologize.

Комментарии • 118

  • @theteachr
    @theteachr  3 года назад +182

    %s does not consume the whitespace (
    ). It stops at one, and appends the null terminator. That explains why a space in a string doesn't get read into the buffer supplied to scanf. So, it's actually %d that discards the leading whitespace.
    Thanks to the redditor who pointed this out.

    • @maharun
      @maharun 2 года назад

      does float and double date types also discards the leading whitespace? please tell me......
      and where is the reddit you are talking about?

    • @csbnikhil
      @csbnikhil 9 месяцев назад

      ⁠@@maharunYes. They also discard the white space.
      The subreddit is C_Programming.

  • @Ramash440
    @Ramash440 2 года назад +97

    Wish I knew this years ago. When doing uni assignments which involved command line I/O this stuff was such a headache. Everybody in my class found a different way to reinvent the wheel and deal with this behavior.

  • @joaogabriels.f.5143
    @joaogabriels.f.5143 2 года назад +53

    You can use [^
    ] to consume the '
    ', like this: scanf("%[^
    ]s", my_string);
    it reads: read everything that it's not '
    '.
    the trick is that italso matches '
    ' and consume it. So you can interpret it as "read eveything until new line, excluding the new line"
    this is great to read more than one word in a single sentence

    • @shadamethyst1258
      @shadamethyst1258 2 года назад +3

      I usually just `fgets` into a temporary buffer and then parse the value of that buffer. I don't like scanf's spurious behavior

    • @abhishek.rathore
      @abhishek.rathore 2 года назад

      @@shadamethyst1258 or just use a getline

    • @privileged6453
      @privileged6453 2 года назад

      @@abhishek.rathore getline is not a C standard library function, it’s a POSIX extension

    • @lembranaodesistaissonaoimp5762
      @lembranaodesistaissonaoimp5762 2 года назад +1

      Regex?

    • @joaogabriels.f.5143
      @joaogabriels.f.5143 2 года назад +1

      @@lembranaodesistaissonaoimp5762 meio que isso mesmo, o scanf aceita certas funcionalidades de regex.

  • @diegoenriqueoviedollanes453
    @diegoenriqueoviedollanes453 2 года назад +110

    A good option also is to select what would be the maximum number of characters that scanf will read.
    char name[10];
    scanf("%9s",name);
    So if the user writes more characters than 10, only the first 10 would be taken. Avoiding any buffer overflow.

    • @ramok1303
      @ramok1303 2 года назад

      Thanks

    • @NoNameAtAll2
      @NoNameAtAll2 2 года назад +9

      maybe only read 10-1 characters, and keep last one for guaranteed /0

    • @tomaszstanislawski457
      @tomaszstanislawski457 2 года назад +14

      Actually it **WILL** overflow the buffer for 10-letter input. It will place byte `0` at `name[10]` that does not exist overflowing a buffer. To fix it use "%9s" specifier

    • @diegoenriqueoviedollanes453
      @diegoenriqueoviedollanes453 2 года назад +3

      @@tomaszstanislawski457 I haven't programmed in c for a long time, I appreciate the correction if I wrote something wrong. Happy hacking!

    • @dipanjanghosal1662
      @dipanjanghosal1662 2 года назад +2

      @@diegoenriqueoviedollanes453 I'd advice you to edit your original comment to %9s because many people don't delve deeper into the replies.

  • @mitchellrose6865
    @mitchellrose6865 3 года назад +37

    Dude thank you so much. Every other solution I found online was crap. This is well explained and your editing is great. Very well made.

  • @alexanderbarhoum5934
    @alexanderbarhoum5934 6 месяцев назад

    it's been 3 years and your video is still saving lives. thank you very much! ❤

  • @claverbarreto5588
    @claverbarreto5588 3 года назад +22

    thanks for this, keep doing it, i found the video super helpful and it taught great things, the illustrations are amazing thank you.

  • @ovutai363
    @ovutai363 3 года назад +1

    You're so cool, I love your "C explained" series

  • @ramok1303
    @ramok1303 2 года назад

    Thank you for making this video you help me so much to understand problems with code you've made a really good job

  • @diegoenriqueoviedollanes453
    @diegoenriqueoviedollanes453 2 года назад +1

    I always used the space in the scanf, just because it works, thanks for explain that, now everything makes sense.

  • @dharanishrahuls5705
    @dharanishrahuls5705 3 года назад +3

    Thank you so much. You were very clear in the explanation. Keep it up. U will reach the sky for sure.

  • @desmondle4073
    @desmondle4073 5 месяцев назад

    This is an amazingly insightful video. Thankyou smmmm

  • @xabe1
    @xabe1 2 года назад

    Thanks for tge video! I programming on C a lot of time and I never faced this problem, I sure that this knowledge will help me in the future.

  • @marshallbcodes
    @marshallbcodes 9 месяцев назад

    I was banging my head against the wall trying to figure this out. Thank you for the great explanation!

  • @Reydriel
    @Reydriel 2 года назад

    I've been looking for info on why this happens in scanf for ages, thanks man

  • @diegoenriqueoviedollanes453
    @diegoenriqueoviedollanes453 2 года назад

    Really nice quality, keep that good work

  • @diegoenriqueoviedollanes453
    @diegoenriqueoviedollanes453 2 года назад

    Thanks for that useful way of asking for input, my programs were full of fflush, not anymore

  • @chimbwesa
    @chimbwesa 3 года назад +3

    how am i just finding this!? thank you commandah

  • @SloeElvis
    @SloeElvis 2 года назад +16

    This video is beautiful! May i ask what you used to animate it?

    • @theteachr
      @theteachr  2 года назад +9

      Thanks! It’s Keynote.

  • @dawaxd
    @dawaxd 2 года назад +1

    very good explanation! helped me a lot

  • @DityoManob
    @DityoManob 7 месяцев назад

    Best explanation for the scanf issue.

  • @halbgefressen9768
    @halbgefressen9768 Год назад +1

    A good solution is using fgets in combination with another parsing method (strtol etc.)

  • @reussunased5108
    @reussunased5108 2 года назад +1

    Me who only uses getline when reading input from user:
    *What is going on here ?*

  • @krzysiu3991
    @krzysiu3991 2 года назад

    thank you so much, saved my time

  • @matheusaugustodasilvasanto3171
    @matheusaugustodasilvasanto3171 2 года назад

    Excellent video, thank you for the explanation.
    What font is being used here?

  • @obaodelana
    @obaodelana 3 года назад +1

    Great explanation!

  • @rishiktiwari
    @rishiktiwari 2 года назад +3

    I am pretty sure that this is the same problem as Java's scanner. Just write two scanner .nextLine() inputs one after the other and it skips the 2nd one when executed.

    • @theteachr
      @theteachr  2 года назад +2

      Yep, it suffers from the same thing.

  • @gianzindozap
    @gianzindozap 2 года назад

    You could also use %*c to clean the buffer

  • @sahil1307
    @sahil1307 2 года назад

    Can you please tell me from where you learnt this
    Any book, website, playlist

  • @hwstar9416
    @hwstar9416 5 месяцев назад

    2:01 actually the
    is still in stdin, however when you read the %d it consumes the
    looking for an integer

    • @theteachr
      @theteachr  5 месяцев назад

      Addressed in the pinned comment with a little more detail.

  • @mohansivadhas
    @mohansivadhas 2 года назад

    Thanks Dude!!! u r great!

  • @ishikani
    @ishikani 2 года назад +5

    One lovely trick that I have against this is literally a single macro which is
    #define CINFLUSH scanf("%*[^
    ]");scanf("%*c");
    Works every time, all the time.

  • @bobmichael8735
    @bobmichael8735 Год назад

    Great video! What software did you use for editing this video?

  • @ajtan2607
    @ajtan2607 2 года назад +2

    This is from my little "test" library (simplified):
    ```
    void get_str(char *s, int n) {
    if (s != NULL && n > 0) {
    int len = 0, maxlen = n - 1;
    int c = getchar();
    while (c != '
    ' && c != EOF) {
    if (len < maxlen) {
    s[len] = c;
    len++;
    }
    c = getchar();
    }
    s[len] = '\0';
    }
    }
    ```
    `s` is the storage and `n` is the storage capacity. The loop extracts all characters from `stdin` until a delimiter newline or `EOF` is found. The condition inside the loop makes sure that only the first `maxlen` characters (excluding the delimiter) are written to `s`. `maxlen` is `n - 1` because at least one element must be reserved for the null character.
    If you give the right arguments, you can type as much as you want without having to worry about buffer overflow. The function will get rid of the excess characters, including the delimiter. I think this is a nice little "training wheel" for beginners.

    • @shadamethyst1258
      @shadamethyst1258 2 года назад

      This is exactly what fgets does :)

    • @ajtan2607
      @ajtan2607 2 года назад

      @@shadamethyst1258 Not exactly. My function gets rid of the the other characters not stored in the array, including the delimiter newline. `fgets()` does not do this. Please read the description again carefully.

  • @edwardmacnab354
    @edwardmacnab354 2 года назад

    very enlightening

  • @tamjidhasanfahim6726
    @tamjidhasanfahim6726 Год назад

    Thanks it helps a lot

  • @ivanjermakov
    @ivanjermakov 2 года назад +4

    C is such a mess from a developer experience perspective (understandable, considering when it was introduced). I work as a software engineer for several years and have some C/C++ background, but I still watch the video open-eyed. Very good content and nice visuals.

    • @CrippleX89
      @CrippleX89 2 года назад

      Yeah C is really starting to show its age now. It’s been a good run but more modern languages are probably a better choice for most tasks. And with Rust being just as quick, capable, suitable as a systems programming language and becoming more and more mature, C and C++ probably will have to fight for its right to exist.

    • @bva0
      @bva0 2 года назад

      @@CrippleX89 Maybe C, but the new C++ standards have everything you'd want in a good language. Smart pointers, threading, softer typing, modular packages, among others...

    • @VeryAwesomeSheep
      @VeryAwesomeSheep 2 года назад

      @@bva0 C also supports threading

    • @c0smo709
      @c0smo709 2 года назад +2

      get good

    • @erek
      @erek 2 года назад +2

      @@bva0 maybe but all of the backwards compatibility in C++ makes the language very complicated and a horrible mess to deal with

  • @code-to-learn
    @code-to-learn Год назад

    Nice presentation

  • @vikidprinciples
    @vikidprinciples Год назад

    Man I wasted so much time because of this problem! Thanks for the explanation

  • @Quzhisnadja128
    @Quzhisnadja128 2 года назад

    Thank you sir

  • @sameerkulkarni8176
    @sameerkulkarni8176 3 года назад +1

    That's awesome.....👌👌

  • @tadiwrr
    @tadiwrr 2 года назад

    thank you

  • @maharun
    @maharun 2 года назад +3

    you made a mistake... %s doesn't consume new line.. it leaves the
    in the stdin buffer..
    try taking a string
    then take a char1 input
    then take char2 input
    when you will run it
    1. you enter the string
    2. it SKIPS char1 input
    3. goes to char2 input
    so %s never consumes new line

    • @theteachr
      @theteachr  2 года назад +3

      I address this in my comment.

    • @windowsxseven
      @windowsxseven 2 года назад

      @@theteachr "I address this in my comment" 🤓 screw off

  • @chaitanyasampara1169
    @chaitanyasampara1169 2 года назад +1

    Now I like C even more

  • @kiitekogra4012
    @kiitekogra4012 Год назад

    Thanks😊

  • @carloscapitinealexandremut8898
    @carloscapitinealexandremut8898 3 года назад +1

    Thanks for the explanation, but i coud't get the problem solved. is there any other way to get out of this?

    • @theteachr
      @theteachr  3 года назад +3

      Having `while (getchar() != '
      ');` before scanning a sequence of characters should work just fine. Join the Discord and post the query for a more elaborate explanation specifically tailored to your use case. discord.gg/ANUEGSZPvn

  • @GrandNecro
    @GrandNecro 2 года назад +1

    what software did you use to edit the video?

    • @U20E0
      @U20E0 2 года назад +2

      Apparently it was made using keynote

  • @glewfw7989
    @glewfw7989 2 года назад

    im amazed this dude called noob to everyone that uses scanf lol

  • @Voltra_
    @Voltra_ 2 года назад

    This random youtuber succeeded where teachers have failed, thx. Although I just use C++ instead

  • @dhawan177
    @dhawan177 Год назад

    Thank you so much i am new to coding and this was soo annoying to see even when i write the code correctly it will just stop. Thak you

  • @lakshanchamod1208
    @lakshanchamod1208 8 месяцев назад

    thnks

  • @cyrileo
    @cyrileo 2 года назад

    +10 social creit score ⬆

  • @avastorneretal
    @avastorneretal 2 года назад

    std::cout

  • @sadhmansarker9133
    @sadhmansarker9133 11 месяцев назад

    can smn explain the while (getchar() != '
    '; part i dont get it

  • @nihildicit4217
    @nihildicit4217 2 года назад

    char str[101];
    scanf("%100[^
    ]", str);
    while(getchar() != '
    ');
    This solves some of the problems but not all of them.

  • @oanminhkhoi6584
    @oanminhkhoi6584 4 месяца назад

    But why adding that weird space before %c would make it work ?

    • @theteachr
      @theteachr  4 месяца назад

      A space in the format string for `scanf` means any amount of whitespace (newlines, spaces, tabs, ...). `scanf("%c %d", &gender, &age)` would read the input `m 45` and assign `m` to `gender` and `45` to `age`.

    • @oanminhkhoi6584
      @oanminhkhoi6584 4 месяца назад

      ​@@theteachr Appreciate the reply. Thanks alot

  • @demnyan
    @demnyan 2 года назад

    cin cout one love 🦊💖

    • @theteachr
      @theteachr  2 года назад

      If you do a cin on an int, but the user entered something that could not be parsed as one, the variable would be set to 0. I had no idea how to differentiate between a valid input of 0 and an invalid input, as in both the cases the variable would have the same value.
      It sets some global error flag (I think). IMO, it should have been something that the programmer couldn’t skip.

    • @demnyan
      @demnyan 2 года назад

      @@theteachr for int you can use atoi( ) and there will be no errors )

  • @yooyo3d
    @yooyo3d 2 года назад

    scanf("%[^
    ]
    ", name);
    Read everything til new line into name then consume new line.

    • @techeadache
      @techeadache 9 месяцев назад

      I tried this! The function scanf(_s) never returns. It's an infinity loop for string input.
      Although, it works for anyting that is NOT a string. For example,
      scanf_s("%c
      ", &gender, 1u); // WORKS AND RETURNS

  • @eldiablo9541
    @eldiablo9541 2 года назад

    You would think that after all this time something as simple as this would have been "fixed". I wonder why they haven't changed that

  • @floskater99
    @floskater99 2 года назад

    Pretty sure Scanner.nextLine() and Scanner.nextInt() in Java have exactly the same problem xD

  • @leoleo10xd
    @leoleo10xd Год назад

    does this problem have a name?

    • @theteachr
      @theteachr  Год назад

      I wouldn’t think so. It’s expected behavior after all. The programmer is expected to know of the subtlety and that’s what is the problem.

  • @alejandroalzatesanchez
    @alejandroalzatesanchez 2 года назад

    and we have problems with like python/java/lua/etc this is worse

  • @katsumikougen8351
    @katsumikougen8351 2 года назад

    Bisqwit brought me here.

    • @theteachr
      @theteachr  2 года назад +1

      This sent me to Bisqwit.

  • @23trekkie
    @23trekkie 2 года назад

    Name: Genie
    Age: six
    Age: Age: Age: Age: Age: Age: Age: Age:....

  • @CurrentlyObsessively
    @CurrentlyObsessively 3 года назад +2

    These fixes are not effective. You should test input at each input. You can even write a small input check function to consolidate your code.
    I recommend Hacking the Art of Exploitation by Jon Erickson. The chapter on programming is strictly C programming and has numerous examples of input and error checks.

    • @theteachr
      @theteachr  3 года назад +1

      Thanks for the input and the recommendation!

  • @samuelwaller4924
    @samuelwaller4924 2 года назад +2

    Your first problem was only having two genders lol

    • @theteachr
      @theteachr  2 года назад +2

      I want more reactions than just being able to ❤️ the comments.

  • @johnjcb4690
    @johnjcb4690 3 года назад

    VERY NICE explaining and work , great. But It is not working in Eclipse. First Scanf processes, the second to last not, And it would only print PRintfs. All correct
    include
    int main()
    {
    char name[20];
    float greutate;
    int ziua;
    int luna;
    int an;
    //scanf("specificatori de format", adresele_din_memorie);//
    printf("Name of sir:
    :");
    fflush(stdout);
    scanf(" s", nume);
    printf("Cat este de greu domnul:[kg]
    ");
    fflush(stdout);
    scanf(" %f", &greutate);
    printf("Dati ziua:
    ");
    fflush(stdout);
    scanf(" %d", &ziua);
    printf("Dati luna:
    ");
    // windows
    fflush(stdout);
    scanf(" %d", &luna);
    // afisare ziua si luna
    an = 2020;
    printf("Astazi este %d/%d/%d!", ziua, luna, an);
    return 0;

    • @theteachr
      @theteachr  3 года назад

      scanf("%s", nume);
      I think the missing % is causing you an issue.

  • @ivanborsuk1110
    @ivanborsuk1110 2 года назад

    so unnecessary

    • @U20E0
      @U20E0 2 года назад +1

      What exactly?

  • @blushy9215
    @blushy9215 2 года назад

    its pronounced char as in charcoal not care........ 👎👎👎 boo

  • @leoleo10xd
    @leoleo10xd Год назад

    " %[^
    ]s " worked for me.
    printf("
    Name: ");
    scanf(" %[^
    ]s ", vec[i].name);