Get rid of warning in sscanf - c

I have a little problem with my code. At the moment I just have a line (char* string with \0 at the end) and I want the line to be checked on special characters. Therefore I used the following code:
char lineJunk;
if(sscanf(lineContent, "%*[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+=\0/{}:]%c", &lineJunk)){
return 0;
}
Now my compiler will spit out the following warning:
Multiple markers at this line
- no closing ‘]’ for ‘%[’ format [-Wformat=]
- embedded ‘\0’ in format [-Wformat-contains-nul]
- too many arguments for format [-Wformat-extra-args]
These warnings only appear when I have \0 in my sscanf. Yet otherwise the code won't work, because the Line I am checking on has \0 at its end. When I use \\0 instead of \0 the warnings disappear, but the code doesn't work anymore. Just \ doesn't work either.
Somebody know a solution?

You are not using sscanf() correctly, to explain the warnings
No closing ] means your format string has no closing ] which is required since you are passing a format with [.
The closeing ] in your format string is "not" there really, because you have an embeded '\0' in the format string, so the actual format string is
"%*[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+="
This is because you have an explicit '\0' in the format string, which is causing the previous warning too.
There is one in the format string which is added by the compiler at the end of it, to mark the end and so it becomes a legitimate c string, in the sense that you can pass it to strlen() and other functions that expect the nul terminator to be present.
By embeding it in the format string, you are marking the end of the string at the position where you inserted, that's why the format string is the one I say in point 1.
You are discarding the matched value by using the * modifier, you need to remove it to make the passed parameter useful, because as is you are discarding the matched value and hence no parameter is required.
You can't match the '\0' with sscanf() if you want that you need to traverse the string one byte at a time until you find a '\0', and in that case the length should be known beforehand.

There is no need for '\0' in the format of sscanf(char *src, char *format, ...). sscanf() will stop scanning when it reaches the '\0' in src. So sscanf() will never provide '\0' for scanning.
As mention by #iharob, the '\0' in the format is trouble as sscanf() see that as the end of the format. That is what the compiler is warning about.
// Eliminate `\0` from the format.
#define SKIP "%*[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+=/{}:]"
if(sscanf(lineContent, SKIP "%c" , &lineJunk) == 1) {
return 0;
}
Should A-Z be consecutive as with typical encoding of ASCII, a short-cut would be: #define SKIP "%*[A-Za-z0-9+=/{}:]"
--
Note better to check sscanf() results withe what code wants: 1 rather than non-zero. Under select situations sscanf() will return EOF

Related

reading the remainder of a string with sscanf

I'm trying to read a string which consists of a set of numbers followed by a string, wrapped with some other basic text.
In other words, the format of the line is something like this:
Stuff<5,10,-5,8,"Test string here.">
Naively, I tried:
sscanf(str,"Stuff<%d,%d,%d,%d,\"%s\">",&i1,&i2,&i3,&i4,str2);
But after some research I discovered %s is supposed to stop parsing when it gets to a whitespace character. I found this question, but none of the answers addresses the problem I have: the string could contain any character in it, including newline characters and properly escaped quotes. The latter is not a problem, if I can just get sscanf to put everything after the first quote in the pre-allocated buffer I provide, I can strip the end off myself.
But how do I do this? I can't use %[] because it requires something in it to terminate the string, and the only thing I want to terminate it is the null terminator. So I thought, "Hey, I'll just use the null terminator!" But %[\0] made the compiler grumpy:
warning: no closing ‘]’ for ‘%[’ format
warning: embedded ‘\0’ in format
warning: no closing ‘]’ for ‘%[’ format
warning: embedded ‘\0’ in format
Using something like %*c won't work either, because I don't know exactly how many characters need to be taken. I tried passing strlen(str) since it will be less than that, but sscanf returns 4 and nothing is put into str2, suggesting that perhaps because the length was too long it gave up and didn't bother.
Update: I guess I could do something like:
sscanf(str,"Stuff<%d,%d,%d,%d,\"%n",&i1,&i2,&i3,&i4,&n);
str2 = str+n;
Your update seems to be a good answer. I was going to suggest strchr to find the location of the first quote char, after using sscanf to get i1 thru i4. Side note, you should always check the return value from sscanf to make sure that the conversions worked. This is even more important with your suggested answer, since n will be left uninitialized if the first four conversions aren't successful.
Scan for '\"', then for everything not '\"', then '\"' again.
Be sure to check sscanf() result and limit how long the test string may be.
char test_string[100];
int n = 0;
if (sscanf(str, "Stuff<%d,%d,%d,%d, \"%99[^\"]\"> %n",
&i1, &i2, &i3, &i4, test_string, &n) == 5 && str[n] == '\0') Good();
Your attempt using "...%[\0]...", from sscanf() point-of-view, is "...%[".
Everything in the format from "\0" on is ignored.
Using the int n = 0, appending " %n" to the format string, appending &n to the parameters and checking str[n] == '\0' is a neat trick with sscanf() to insure the entire line parsed correctly. Note: "%n" does not add to sscanf() result.
This is not the only way to achieve what you want to achieve, but probably the neatest way to do it: You'll need to use the scansets. I won't tell you the solution directly with this answer, I'll explain how to use scansets as far as I know them, and you'll hopefully be able to do it yourself.
Scansets %[...] are like %s when it comes to assignment, they interpret values as characters and store them into character arrays. %s is whitespace-terminated, %[...] is the flexible version of that.
There are two ways of using the scanset, first one being without a preceding caret ^, second one being with a preceding caret ^.
When you use scanset without the preceding caret ^, the characters you put inside the brackets will be the only ones that will be read, stored and then left behind. As soon as scanf encounters a non-matching character, that %[...] will be over. For example:
// input: asdasdasdwasdasd
char s[100] = { 0 };
scanf( "%[das]", s );
printf( "%s", s );
// output: asdasdasd
When you use scanset with the preceding caret ^, the search is inversed. It reads, stores and leaves behind every character until it reaches any one of the characters that you've put down after the preceding caret ^. Example:
// input: abcdefgh^kekQ
char s[100] = { 0 };
scanf( "%[^Q^]", s );
printf( "%s", s );
// output: abcdefgh
Beware, remaining characters is still to be read inside the stream, the file pointer won't get beyond the character which caused termination. I.e. for the first one, getchar( ); would give a 'w', and for the second one it would give a '^'.
I hope this will be enough. If you still cannot find your way out, ask away, I can give you a solution.

How to write and read (including spaces) from text file

I'm using fscanf and fprintf.
I tried to delimit the strings on each line by \t and to read it like so:
fscanf(fp,"%d\t%s\t%s",&t->num,&t->string1,&t->string2);
The file contents:
1[TAB]string1[TAB]some string[NEWLINE]
It does not read properly. If I printf("%d %s %s",t->num,t->string1,t->string2) I get:
1 string1 some
Also I get this compile warning:
warning: format specifies type 'char *' but the argument has type 'char (*)[15]' [-Wformat]
How can I fix this without using binary r/w?
I'm guessing the space in "some string" is the problem. fscanf() reading a string using %s stops at the first whitespace character. To include spaces, use something like:
fscanf(fp, "%d\t%[^\n\t]\t%[^\n\t]", &t->num, &t->string1, &t->string2);
See also a reference page for fscanf() and/or another StackOverflow thread on reading tab-delimited items in C.
[EDIT in response to your edit: You seem to also have a problem with the arguments you're passing into fscanf(). You will need to post the declarations of t->string1 to be sure, but it looks like string1 is an array of characters, and therefore you should remove the & from the fscanf() call...]
The %s conversion specification stops reading at the first white space, and tabs and blanks both count as white space.
If you want to read a string of non-tabs, you can use a 'scan set' conversion specifier:
if (fscanf(fp, "%d\t%[^\t\n]\t%[^\t\n]", &t->num, t->string1, t->string2) != 3)
...oops - format error in input data...
(I'd lay odds that omitting the & from the string arguments is correct.) The question was edited; I win. Dropping the & is necessary to avoid the compiler warning!
This still doesn't quite do what you expect. If there are blanks at the start of the second field, they'll be eaten by the \t in the format string. Any white space in the format string eats any white space (including newlines) in the input. The %[^\t] conversion specification won't get started until there's a character that isn't white space in the input. I'm also assuming you want your input limited by newlines. You can leave out the \n characters if you prefer.
Note that I checked that the fscanf() interpreted 3 fields. It is important to error check your inputs.
If you really want control, you should probably read whole lines with fgets() and then use sscanf() to parse the data.
About fgets() and sscanf(); can you expand about how it will give more control?
Suppose the input data is written
1234
a string with spaces
another string
spread out over multiple lines like that. With raw fscanf(), this will be acceptable input even though it is spread over 9 lines of input. With fgets(), you can read a single line, and then analyze it with sscanf(), and you'll know that the first line was not in the correct format. You can then decide what to do about it.
Also, since mafso called me on it in his comment, we should ensure that there are no buffer overflows by limiting the size of the strings that the scan sets match.
if (fscanf(fp, "%d\t%14[^\t\n]\t%14[^\t\n]", &t->num, t->string1, t->string2) != 3)
...oops - format error in input data...
I'm using the error message about char (*)[15] to deduce that 14 is the correct number to use. Note that unlike printf(), you can't specify the sizes via * notation (in the scanf()-family, * supresses assignment), so you have to create the format with the correct sizes. Further, the size you specify is the number of characters before the terminating null byte, so if the array is of size 15, the size you specify in the format string is 14, as shown.

Difference between %255[^\0]s and %255c

In C, when using sscanf, for the format parameter, is there a difference between using:
%255[^\0]s
And:
%255c
Is one faster? Will one of the above ever give a different outcome?
The question is certainly not what the OP intended.
The OP requests the sscanf(buf, format, dest) difference between formats
"%255[^\0]s" // a seemingly format specifier %255[^\0] and the letter 's'
"%255c"
Certainly the OP wanted the sscanf(buf, format, dest) difference between formats
"%255[^\0]" // a seemingly format specifier %255[^\0]
"%255c"
OR
"%255s" // format specifier %255s
"%255c"
The "%255[^\0]" is not the format one may think. This is the same as format "%255[^". sscanf() does not know there is something past the explicit null character '\0'. Since the format specifier begins with a [ but does not end with a matching ], it is an invalid specifier. "If a conversion specification is invalid, the behavior is undefined."
This also applies to the original "%255[^\0]s":behavior is undefined.
Following are the salient issues between "%255s" and "%255c"
"%255c" does not consume leading white spaces. "%255s" does consume unlimited leading white space, scanning them, but not saving them to dest.
"%255c" does scan white spaces and saves them to dest. "%255s", after it found a non-white space, will stop scanning should it encounter a white space.
Both will scan up to 255 characters and place the scanned characters into dest.
"%255c" does not append a \0 so dest should cope with 255 char.
"%255s", if it scans at least 1 char, will append a \0, so dest should cope with 256 char.
Neither will scan a \0 as the scanning buf stops on \0 in sscanf(). "%255c" would scan a \0 in fscanf(). This is unusual as fscanf() is not used much when files have \0 in them.
Should any speed difference occur, certainly it is implementation dependent.
Can't say for sure about speed, but there is a difference in outcome.
First, %255c will (assuming there's at least 255 characters in the string you're scanning) read 255 characters, regardless of what they are. %255[^\0]s, on the other hand, will read up to 255 non-whitespace characters.
Second, because strings are already terminated by \0, the [^\0] part of the regex is redundant, as sscanf will never consider null-terminators as part of the string.

C programming language (scanf)

I have read strings with spaces in them using the following scanf() statement.
scanf("%[^\n]", &stringVariableName);
What is the meaning of the control string [^\n]?
Is is okay way to read strings with white space like this?
This mean "read anything until you find a '\n'"
This is OK, but would be better to do this "read anything until you find a '\n', or read more characters than my buffer support"
char stringVariableName[256] = {}
if (scanf("%255[^\n]", stringVariableName) == 1)
...
Edit: removed & from the argument, and check the result of scanf.
The format specifier "%[^\n]" instructs scanf() to read up to but not including the newline character. From the linked reference page:
matches a non-empty sequence of character from set of characters.
If the first character of the set is ^, then all characters not
in the set are matched. If the set begins with ] or ^] then the ]
character is also included into the set.
If the string is on a single line, fgets() is an alternative but the newline must be removed as fgets() writes it to the output buffer. fgets() also forces the programmer to specify the maximum number of characters that can be read into the buffer, making it less likely for a buffer overrun to occur:
char buffer[1024];
if (fgets(buffer, 1024, stdin))
{
/* Remove newline. */
char* nl = strrchr(buffer, '\n');
if (nl) *nl = '\0';
}
It is possible to specify the maximum number of characters to read via scanf():
scanf("%1023[^\n]", buffer);
but it is impossible to forget to do it for fgets() as the compiler will complain. Though, of course, the programmer could specify the wrong size but at least they are forced to consider it.
Technically, this can't be well defined.
Matches a nonempty sequence of characters from a set of expected
characters (the scanset).
If no l length modifier is present, the corresponding argument shall
be a pointer to the initial element of a character array large enough
to accept the sequence and a terminating null character, which will be
added automatically.
Supposing the declaration of stringVariableName looks like char stringVariableName[x];, then &stringVariableName is a char (*)[x];, not a char *. The type is wrong. The behaviour is undefined. It might work by coincidence, but anything that relies on coincidence doesn't work by my definition.
The only way to form a char * using &stringVariableName is if stringVariableName is a char! This implies that the character array is only large enough to accept a terminating null character. In the event where the user enters one or more characters before pressing enter, scanf would be writing beyond the end of the character array and invoking undefined behaviour. In the event where the user merely presses enter, the %[...] directive will fail and not even a '\0' will be written to your character array.
Now, with that all said and done, I'll assume you meant this: scanf("%[^\n]", stringVariableName); (note the omitted ampersand)
You really should be checking the return value!!
A %[ directive causes scanf to retrieve a sequence of characters consisting of those specified between the [ square brackets ]. A ^ at the beginning of the set indicates that the desired set contains all characters except for those between the brackets. Hence, %[^\n] tells scanf to read as many non-'\n' characters as it can, and store them into the array pointed to by the corresponding char *.
The '\n' will be left unread. This could cause problems. An empty field will result in a match failure. In this situation, it's possible that no data will be copied into your array (not even a terminating '\0' character). For this reason (and others), you really need to check the return value!
Which manual contains information about the return values of scanf? The scanf manual.
Other people have explained what %[^\n] means.
This is not an okay way to read strings. It is just as dangerous as the notoriously unsafe gets, and for the same reason: it has no idea how big the buffer at stringVariableName is.
The best way to read one full line from a file is getline, but not all C libraries have it. If you don't, you should use fgets, which knows how big the buffer is, and be aware that you might not get a complete line (if the line is too long for the buffer).
Reading from the man pages for scanf()...
[ Matches a non-empty sequence of characters from the
specified set of accepted characters; the next pointer must be a
pointer to char, and there must be enough room for all the characters
in the string, plus a terminating null byte. The usual skip of
leading white space is suppressed. The string is to be made up of
characters in (or not in) a particular set; the set is defined by the
characters between the open bracket [ character and a close bracket ]
character. The set excludes those characters if the first character
after the open bracket is a circumflex (^). To include a close
bracket in the set, make it the first character after the open bracket
or the circumflex; any other position will end the set. The hyphen
character - is also special; when placed between two other
characters, it adds all intervening characters to the set. To
include a hyphen, make it the last character before the final close
bracket. For instance, [^]0-9-] means the set "everything except
close bracket, zero through nine, and hyphen". The string ends with
the appearance of a character not in the (or, with a
circumflex, in) set or when the field width runs out.
In a nutshell, the [^\n] means that read everything from the string that is not a \n and store that in the matching pointer in the argument list.

How To Read in Strings that only Contain Alphabet letters with fscanf?

I have been struggling to figure out the fscanf formatting. I just want to read in a file of words delimited by spaces. And I want to discard any strings that contain non-alphabetic characters.
char temp_text[100];
while(fscanf(fcorpus, "%101[a-zA-Z]s", temp_text) == 1) {
printf("%s\n", temp_text);
}
I've tried the above code both with and without the 's'. I read in another stackoverflow thread that the s when used like that will be interpreted as a literal 's' and not as a string. Either way - when I include the s and when I do not include the s - I can only get the first word from the file I am reading through to print out.
The %[ scan specifier does not skip leading spaces. Either add a space before it or at the end in place of your s. Also you have your 100 and 101 backwards and thus a serious buffer overflow bug.
The s isn't needed.
Here are a few things to try:
Print out the return value from fscanf, and make sure it is 1.
Make sure that the fscanf is consuming the whitespace by using fgetc to get the next character and printing it out.

Resources