1

I saw on this website that this: fscanf(fin, "%[^\n]", &p); could be used for reading from my input file(fin) into that char type pointer(*p) all the characters until the first enter hit. At some inputs it works properly, but at others it doesn't.

This is the input I should process and I cannot:

(((zahar 100 ou 3) 5 unt 100 nuca 200) 4 (lapte 200 cacao 50 zahar 100)3)20

This is my whole code:

#include <string.h>
#include <stdio.h>
FILE *fin, *fout;
int main()
{
    fin =  fopen("reteta.in", "r");
    fout = fopen("reteta.out", "w");
    char *p;
    p = new char[1001]();
    fscanf(fin, "%[^\n]", &p);
    fprintf(fout, "%s", &p);
    return 0;
}
Deduplicator
  • 44,692
  • 7
  • 66
  • 118
Mihnea Gafton
  • 61
  • 1
  • 8
  • 2
    Match infinitely many characters but not `\n`, and put them into the `char[]` pointed to by the coresponding argument. (Which is thus of type `char*`, **not `char**`.** – Deduplicator Oct 27 '14 at 22:05
  • 2
    Googling `fscanf` leads me to http://msdn.microsoft.com/en-us/library/kwwtf9ch.aspx with three mouse clicks. – Ken White Oct 27 '14 at 22:06
  • Note that `new char[1001]();` is not C -- it is C++ code. – Jonathan Leffler Oct 27 '14 at 22:18
  • 1
    What's with the trailing `()` ? – cnicutar Oct 27 '14 at 22:20
  • How should it go in c? Sorry for answering slow, i am literaly googling half of the functions i read in here. – Mihnea Gafton Oct 27 '14 at 22:23
  • 1
    You should be passing `p` and not `&p` to `fscanf()`. You should almost certainly include a newline in the output to `fout`. You should check that the input succeeded before printing it. The `FILE *` variables should be local to `main()` and not global variables. Arguably, you should close the files before exiting (though the runtime system will close them automatically, but it is generally regarded as better practice to close what you open -- it really matters in long-running programs, though it won't harm this one). – Jonathan Leffler Oct 27 '14 at 22:24
  • @cnicutar: Value initialization. The buffer will be zeroed initially. – Ben Voigt Oct 27 '14 at 22:24
  • In C, you'd simply write: `char p[1001];` (and in C++ too; a 1 KiB buffer isn't going to break on any system you're running a compiler on). It has the additional merit of not leaking memory, whereas `malloc()` -- which is more or less the C counterpart to C++'s `new` -- requires data to be freed manually (though again, if the program is about to exit, it won't matter much). – Jonathan Leffler Oct 27 '14 at 22:26
  • @BenVoigt Thanks, in hindsight that should have been obvious :-? – cnicutar Oct 27 '14 at 22:28

2 Answers2

4

The %[ notation introduces something called a "scanset" which is somewhat like a regular expression (but not as powerful).

In your specific example it means the format specifier instructs scanf to keep scanning characters that match anything except an \n (encountering a non-matching character will terminate the scan).

From the C11 standard:

The conversion specifier includes all subsequent characters in the format string, up to and including the matching right bracket (]). The characters between the brackets (the scanlist) compose the scanset, unless the character after the left bracket is a circumflex (^), in which case the scanset contains all characters that do not appear in the scanlist between the circumflex and the right bracket.


Even comparing it to a regular expression is stretching it: the standard simply says it: "Matches a nonempty sequence of characters from a set of expected characters".


The real problem with your code, spotted by Jonathan Leffler is this:

fscanf(fin, "%[^\n]", &p);
                      ^

Drop the & since you want to pass p, not "the address of p".

cnicutar
  • 178,505
  • 25
  • 365
  • 392
  • Well this is what it was meant for, to read everything from scanf until the \n character. By what i see you are saying the same thing, or maybe i do not understand. And if it makes this why is it not working for that input? – Mihnea Gafton Oct 27 '14 at 22:15
  • @MihneaGafton You are not explaining in what way it is not working. What exactly is happening and how is it deviating from your expectations ? – cnicutar Oct 27 '14 at 22:19
  • Sorry for not explaining properly, when i try to compile that code with that input it gives me infinit-loop like error on console. – Mihnea Gafton Oct 27 '14 at 22:21
2

% introduces a format-specifier, [ means it's a scan-set and opens it, ^ at the first position in the scan set reverses from "match all in" to "match all not in", \n is the new-line character, and ] closes the scan-set.

Thus, it means: Match indefinitely many characters which are not \n and put them into the char[] pointed to by the argument.

That argument thus must have type char*.
&p is exactly one address-of too much, and the buffer pointed to by it (new char[1001]) is infinite orders of magnitude too small.

Restrict the length of the match (by putting 1000 between the scanset and the %).
Alternatively, and far better, use fgets.


Other points:

  1. Your buffer should be stack-allocated, as it is small enough:

    char buf[1001];
    
  2. Alternatively, at least use a smart-pointer so you don't forget to delete [] it:

    std::unique_ptr<char> p = new char[1001];
    
  3. Why do you value-initialize it? That's superfluous. Use new char[1001] instead of new char[1001]().
  4. Don't forget to check the return-value of fscanf, it should be the number of assignments on success.
  5. C++ (and C99) has an implicit return 0; at the end of main.
  6. You really should close all files you opened (fopen->fclose).
Deduplicator
  • 44,692
  • 7
  • 66
  • 118
  • "Restrict the length of the match (by putting 1000 between the scanset and the %)." this means to write like `fscanf(fin, "%1000[^\n]", &p);`, i tried and it displays me a infinite-loop like console error, similarly to the one in the first case. – Mihnea Gafton Oct 27 '14 at 22:23
  • @jonathan: I don't think "infinitely" was a typo, although "indefinitely" is somewhat more accurate, it doesn't emphasize the unbounded nature. – Ben Voigt Oct 27 '14 at 22:30
  • 1
    @MihneaGafton: ?? Anyway, did you read the part about the additional `&` there being a bug? – Deduplicator Oct 27 '14 at 22:30
  • @BenVoigt: I agree that the change could be contentious -- in fact, since you queried it, it is contentious. It depends on how literally one takes 'infinitely'. There aren't enough fundamental particles in the universe to store an infinite number of characters, so the string cannot hold an infinite number of characters. It's only safe if the 'infinite' number of characters is 1000 or less. If Deduplicator objects, he can reinstate 'infinitely'. – Jonathan Leffler Oct 27 '14 at 22:33
  • @Deduplicator I did, noe it works, thanks. As soon as i figure out how to mark this question as solved i willl do it. – Mihnea Gafton Oct 27 '14 at 22:35
  • @JonathanLeffler: I'm pretty sure he was making the point that *it isn't safe*, because "infinite" is not less than 1000. – Ben Voigt Oct 27 '14 at 22:36