0

While importing data from a file, I bumped into an import error while using fscanf(input, "%d", &myint);. Sometimes it took me a while to find out that I should've used fscanf(input, "%d\n", &myint); instead, which fixed the problem. I know that there are other ways to import a line, but I have a doubt about this specifically.

My doubt arises from the fact that in the programming book I use (in the paragraph about the fscanf function) this information is missing, and, indeed, it imports integers without '\n', which personally caused me an error.

Here are the first three lines of my input file:

3
2 3
6 6

And here are the instructions I've used:

fscanf(input, "%d\n", &playersnum);
fscanf(input, "%d %d", (dicethrows+(i*2)), (dicethrows+(i*2)+1))

where dicethrows is a pointer to an int array. As you can see, in the last instruction (which is actually in a for loop with counter i) I didn't need to use '\n', but without it in the first line still caused me reading issues...

My question is, when should I add a '\n' in my fscanf and why?

EDIT: Adding some code (sorry for not translating the variables' names, I think it doesn't really matter as the code is quite short):

int main(void) {
    int numerogiocatori;
    int numerolanci=0;
    int* posizionigiocatori;
    int* lancidadi;
    //impor data
    FILE* input=fopen("info.txt", "r");
    if(input!=NULL) {
        //first thing: first line (number of players)
        fscanf(input, "%d\n", &numerogiocatori);
        posizionigiocatori=malloc(numerogiocatori);
            for(int i=0; i<numerogiocatori; posizionigiocatori[i]=1, i++); //all players start from position 1

        //second thing: count lines after the first one (=number of throws)
        do {
            int ch=fgetc(input);

            if((char)ch=='\n' || ch==EOF) {
                numerolanci++;

                if(ch==EOF)
                    break;
            }
        } while(true);
        lancidadi=malloc(sizeof(int)*2*numerolanci);

        //third thing: import throws
        fseek(input, 0, SEEK_SET);
        fscanf(input, "%*d\n");

        for(int i=0; fscanf(input, "%d %d", (lancidadi+(i*2)), (lancidadi+(i*2)+1)) != EOF; i++);

        fclose(input);
    }
   ...

Before posting I tried to check a second time and the values I get are actually different.

Luca
  • 70
  • 7
  • 1
    Have a look at https://stackoverflow.com/questions/15740024/why-does-scanf-ask-twice-for-input-when-theres-a-newline-at-the-end-of-the-form I mention it because I doubt "I should've used `fscanf(input, "%d\n", &myint);`" – Yunnosch Jun 26 '22 at 16:28
  • Yea before posting I gave a look at the suggested questions and answers but I couldn't find anything specific... That's exactly the behavior I've encountered while making the program... I can't explain it so I came here – Luca Jun 26 '22 at 16:30
  • 2
    Please provide a [mre]. – Yunnosch Jun 26 '22 at 16:50
  • 2
    As a general rule (and I think it may be an absolute rule) you *never* want `\n`, or any whitespace, at the end of a scanf/fscanf format string. If you had some problem, and if adding `\n` at the end of the format string seemed to fix it, it means you didn't really understand the problem, and adding `\n` was not the proper fix for it. – Steve Summit Jun 26 '22 at 17:08
  • The other general rule is that this is par for the course when using scanf/fscanf. These functions are barely useful for simple problems, but you will just about always have strange, baffling problems when you try to use them for anything complicated. (And, I'm sorry to say, in my book, trying to read two numbers on one line, using `"%d %d`, can indeed be an example of "something complicated".) – Steve Summit Jun 26 '22 at 17:11
  • I am convinced that your question is a duplicate. You have even confirmed with "That's exactly the behavior I've encountered while making the program". If you want to explain how this question is different you will have to provide a [mre]. – Yunnosch Jun 26 '22 at 18:31
  • @Yunnosch, what I meant was *my* problem, which I didn't see in the question *I had already given* a look at and that you think is the "original one". But you couldn't wait to find an excuse to close the question, could you? I'll provide some "minimal reproducible example" – Luca Jun 26 '22 at 21:12
  • The shown code is not a [mre] and you better either translate it, or explain it, ideally both. Please also add an explanation of desired behaviour in contrat to the behaviour you get. Also, because I suspect that your goal (whatever it is) is best achieved without the `"\n"`, please provide a [mre] of "it imports integers without '\n', which personally caused me an error". – Yunnosch Jun 26 '22 at 21:32
  • 1
    "How could it be complicated?" You've just seen how: You tried something straightforward, and it should have worked, but it didn't, and the reason basically is, scanf sucks. fscanf sucks a little bit less, but it sucks, too. You can get these functions to work, eventually, but it can be timeconsuming and frustrating, and ultimately not worth the effort. – Steve Summit Jun 26 '22 at 21:44
  • @SteveSummit Read, the thing is that I get unexpected behaviors. Sometimes I just get a segmentation fault, sometimes it works but the counter gives me a different value (20 lines without '\n' and 19 with) – Luca Jun 26 '22 at 21:45
  • I mean, through ftell() I see that with '\n' the position after the first fscanf is 3. Without the '\n' the position is 1 (with unexpected behavior; means that the newline will be counted later (maybe?)). There must be something... – Luca Jun 26 '22 at 21:52
  • As I said earlier, you *definitely* do not want the `\n` in the format strings. – Steve Summit Jun 26 '22 at 21:54
  • Although, with that said, I tried your code, and it worked fine for me with or without the `\n`' – Steve Summit Jun 26 '22 at 21:54
  • In this case, I don't see that the trailing `\n` would actually cause problems. (Which is not to say it's not wrong — I do maintain that it's wrong.) – Steve Summit Jun 26 '22 at 21:55
  • 1
    There are several other things fishy about your code: (1) You don't really need to rewind the file; (2) testing `fscanf`'s return value in the test condition of the `for` loop is somewhat confusing; (3) you should be testing `fscanf`'s return value to make sure it *is* 2, not that it's not `EOF`, and (4) you're not using `numerogiocatori` for anything. But (1), (2), and (4) are not causing your problem. (I'm not sure about (3).) – Steve Summit Jun 26 '22 at 21:59
  • Yup... if I delete the '\n' from there and change the if statement from this `if((char)ch=='\n' || ch==EOF)` to this `if((char)ch=='\n')` with an `else if(ch==EOF) break;`, then the number remains the same (19, the right one) and the problem is fixed. Keeping into consideration the EOF as well seemed reasonable to me because *"if I've got three lines I'll have only two '\n' because the last one is an EOF"*. Also looking at the `ftell` return value, I guess that without that '\n' the loop to count the number of lines *also counts* the first one, which is not discarded. – Luca Jun 26 '22 at 22:00
  • I rewinded the file to make it easier (instead of dynamically reallocating every time as I did the other times) and to try a different approach, hoping that it'd turn useful (I've got plenty of problems in the last 10 days with importing files in C), and I've discovered something new... – Luca Jun 26 '22 at 22:02
  • I overlooked the way you're counting lines, then rewinding. That's definitely a big part of your issue. – Steve Summit Jun 26 '22 at 22:03
  • 1
    One side issue is that you can't be sure whether the file will end in `\n`, or `EOF`, or both. If if ends in `\n` followed bu `EOF` (which I consider the usual case, although it's less usual than it used to be), your count might be too high by 1. And then, with `\n` in the first `scanf`, your count might be too low by 1. So those two errors might cancel each other out. – Steve Summit Jun 26 '22 at 22:05
  • 1
    One of the many problems with `scanf` and `fscanf` is that they are *not line based*. So your attempt to count lines in a program that uses `fscanf` is, I'm afraid, doomed to failure. – Steve Summit Jun 26 '22 at 22:07
  • So you'd suggest me to use fgets and then sscanf? – Luca Jun 26 '22 at 22:08
  • I mean, if you tell me that scanf and ffscanf su** this much... – Luca Jun 26 '22 at 22:09
  • 1
    In very simple programs, `scanf` and `fscanf` can be simple solutions. But in anything complicated, as I said before, they tend to just be way more trouble than they're worth. We've already spent way too much time on this, and we still don't really understand what's going on. So, yes, my recommendation would be to read *lines* of text using `fgets`, then convert the numbers on those lines by calling `atoi`, or `sscanf`. See [What can I use for input conversion instead of scanf?](https://stackoverflow.com/questions/58403537). – Steve Summit Jun 26 '22 at 22:09
  • To some extent, my advice to you here might seem irresponsible. Its a bit of a cop-out: if we worked hard enough, we could probably get the `fscanf` solution to work, so are we just being lazy by punting and switching to `fgets`? In this case I say: no, because I believe that learning about *scanf and working around its many problems is a dead-end pursuit that just isn't worth it. – Steve Summit Jun 26 '22 at 22:11
  • You're right... I might have found an explanation. Let's say the first line is an integer and the other 2 are the ones I want to import later. If the '\n' of the first line is not "ignored" (as in the case of a fscanf without '\n'), it'll be counted by the next loop which I've used to count lines, and since I also count the EOF, then I get a number which is higher by one. If I don't use the '\n' in my fscanf (as you suggested), the position in the file, at the next fscanf, will be first line *before* the newline. So I just need to not count the EOF in my loop (because online I didn't see it)-> – Luca Jun 26 '22 at 22:20
  • ->and count the first newline itself, that I thought would be ignored in the first place. This was the problem... I'll use fgets anyway, it seems to be better. And I'll avoid rewinding as well. Thank you so much for your time! – Luca Jun 26 '22 at 22:22
  • 2
    Another tip: in the loop that actually reads the throws, have it stop when `fscanf` returns other than 2, *or* `i` reaches numerolanci, whichever comes first. If you have a bug somewhere, and you mis-count, and you allocate space for, say, 3 throws, you don't want the loop to read 4 or more throws, because you'll overflow your posizionigiocatori array. – Steve Summit Jun 26 '22 at 22:37
  • 2
    `scanf` has its detractors, and its *ardent* detractors. I don't think any experienced C programmer would deny that `scanf` is difficult to use safely or correctly. Much more so than introductory courses tend to lead C newbies to believe. But that doesn't mean there is no purpose to which it is suited, and I would not agree that it is unsuited for the particular purposes of this program. – John Bollinger Jun 26 '22 at 22:39

0 Answers0