Using fgets to store a double into a struct - c

I'm a bit new to C programming and I keep running into this error. Previously I was using fscanf with %lf because age was a double but since I switched to fgets, I do not know how to get it to accept a double. I am attempting to read into a struct. Here's my relevant code:
double age;
...
while(fgets(Employees[i].age, 10, input) != 0)
gives me this error:
error: incompatible type for argument 1 of fgets
expected 'char *' but argument is of type 'double'

fgets tries reads in a line of text, which isn't ideal for your situation, as you're only interested in the double values.
So instead of using fgets, you'll want to use fscanf with a double format string:
while(fscanf(input, "%lf", &Employees[i].age) == 1)

You may also want to read an entire line of text using fgets() and then try to parse it into values using sscanf().
sscanf() works just like fscanf() and scanf() except that its first parameter is a character string pointer.
Reading an entire line at a time may make it easier to do certain things, such as reporting errors by line number or if a sscanf() fails, trying to convert the line using different format strings.
An example for fun:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main()
{
char buffer[1024];
const size_t buffer_size = sizeof(buffer);
int line_number = 0;
while( fgets(buffer, buffer_size, stdin) ) {
size_t len = strlen(buffer);
double value;
line_number++;
if( buffer[len-1] == '\n' ) {
buffer[len-1] = '\0';
len--;
}
if( sscanf(buffer, " %lf", &value) != 1 ) {
fprintf(stderr, "stdin:%d: Unable to read value: \"%s\" makes no sense.\n", line_number, buffer);
exit(EXIT_FAILURE);
}
printf("Successfully read value %lf\n", value);
}
return EXIT_SUCCESS;
}

Related

Pattern Recognition for File input in C

I was trying to get input from a file in C using scanf. The data in the file is as follows:
223234 <Justin> <Riverside>
This is the following regex I tried:
FILE* fid;
int id;
char name[100], city[100];
char dontcare1[40], dontcare3[40];
char dontcare2,dontcare4[40],dontcare5;
fid = fopen("test.txt", "r");
fscanf(fid,"%d%[^<]%c%[^<]%c%[>]%c ",&id,&dontcare1[0],
&dontcare2,&dontcare3[0],&dontcare4[0],
&city[0],&dontcare5);
I was wondering if there is a better way to do this, how would I account for whitespaces in the file without creating extra variables, this doesn't seem to pick up the city name enclosed in the brackets.
In *scanf() you can expect literal characters and one space can match many separators.
My example is simplified with sscanf() in order to avoid dealing with a file, but it works the same with fscanf().
The trick here is to use %n in order to obtain the number of read characters till that point; this way, we ensure the last > literal has actually been read
(we cannot know that with the result of *scanf())
/**
gcc -std=c99 -o prog_c prog_c.c \
-pedantic -Wall -Wextra -Wconversion \
-Wc++-compat -Wwrite-strings -Wold-style-definition -Wvla \
-g -O0 -UNDEBUG -fsanitize=address,undefined
**/
#include <stdio.h>
int
main(void)
{
const char *line="223234 <Justin> <Riverside>";
int id;
char name[100], city[100];
int n_read=-1;
sscanf(line, "%d <%[^>]> <%[^>]>%n",
&id, name, city, &n_read);
if(n_read!=-1) // read till the end then updated
{
printf("id=%d\n", id);
printf("name=%s\n", name);
printf("city=%s\n", city);
}
return 0;
}
When trying to open the file, it's useful to ensure that the file was actually opened successfully.
FILE *fid;
fid = fopen("path/to/file", "r");
if (fid == NULL){
printf("Unable to open file. \n");
return -1;
}
Actually addressing your problem, I'd probably just use string.h's strtok function, then use a space as a delimiter.
Also, I wouldn't use scanf, but rather fgets... The reasons for this can be found in various other SO articles. The following is an untested solution.
char line[100], line_parse[100]; // Buffer(s) to store lines upto 100
char *ret; // token used for strtok
// Read an integer and store read status in success.
while (fgets(line, sizeof(line), fPtrIn) != NULL)
{
// Copy the line for parsing, as strtok changes original string
strcpy(line_parse, line);
// Separate the line into tokens
ret = strtok(line_parse, " ");
while (ret != NULL)
{/*do something with current field*/
ret = strtok(NULL, " "); // Move onto next field
}
Please be aware that strtok is not thread-safe. In multi-threaded code, you should therefore not use this function. Unfortunately, the ISO C standard itself does not provide a thread-safe version of the function. But many platforms provide such a function as an extension: On POSIX-compliant platforms (such as Linux), you can use the function strtok_r. On Microsoft Windows, you can use the function strtok_s. Both of these functions are thread-safe.
You can actually do this quite simply by reading the line into an array (buffer) and then parsing what you need from the line with sscanf(). Don't use scanf() directly as that opens you up to a whole array of pitfalls related to what characters remain unread in your input stream. Instead, do all input by reading a line at a time and then use sscanf() to parse the values from the buffer, just as you would with scanf(), but by using fgets() to read, you consume an entire line at a time, and what remains in your input stream does not depend on the success or failure of your conversions.
For example, you could do:
#include <stdio.h>
#define MAXC 1024
#define NCMAX 100
int main (void) {
char buf[MAXC],
name[NCMAX],
city[NCMAX];
unsigned n;
if (!fgets (buf, MAXC, stdin))
return 1;
if (sscanf (buf, "%u <%99[^>]> <%99[^>]>", &n, name, city) != 3) {
fputs ("error: invalid format", stderr);
return 1;
}
printf ("no. : %u\nname : %s\ncity : %s\n", n, name, city);
}
The sscanf() format string is key. "%u <%99[^>]> <%99[^>]>" reads the number as an unsigned value, <%99[^>]> consumes the '<' and then the character class %99[^>] uses the field-width modifier of 99 to protect your array bounds and the class [^>] will read all characters not including > (it does the same for the city next). The conversion is Validated by Checking the Return to insure three valid conversions took place. If not, the error is handled.
Example Use/Output
With your input in the file dat/no_name_place.txt, the file is simply redirected on stdin and read by the program resulting in:
$ ./bin/no_name_city < dat/no_name_place.txt
no. : 223234
name : Justin
city : Riverside
If you have to use scanf(), the other answers seem to cover every aspect. This is an alternative, getting input character by character using fgetc() and strcpy().
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAX_SIZE 100
int main(void)
{
int id = 0, c = 0;
char buff1[MAX_SIZE], buff2[MAX_SIZE];
size_t i = 0U;
FILE *fptr = NULL;
if (!(fptr = fopen("test.txt", "r")))
{
perror("error opening file");
return -1;
}
while ((c = fgetc(fptr)) != EOF)
{
if (isdigit(c)) /* maybe check INT_MAX here if you are planning to scan big numbers */
{
id = (id * 10) + (c - '0');
}
if (i != 0 && c == ' ')
{
buff2[i] = '\0';
strcpy(buff1, buff2);
i = 0U;
}
if (isalpha(c))
{
if (i < MAX_SIZE - 1)
{
buff2[i++] = c;
}
else
{
fputs("Buff full", stderr);
return -1;
}
}
}
buff2[i] = '\0';
return 0;
}

strcmp and fscanf with a While Loop through a file

I'm having trouble with a specific line of code that is giving me the errors of
error: invalid conversion from ‘int’ to ‘const char*’
error: initializing argument 1 of ‘int strcmp(const char*, const char*)’
Does anyone happen to know why? This is the line of code in question.
while (strcmp(fscanf(fr, "%s", words), "DONE") != 0)
Essentially, my code scans through a file (performing certain operations) until it reaches the key word of "DONE" (without the quotes), at which it exits the file. I'm a beginner C programmer so forgive any inaccuracies/inefficiencies in code.
The full code is below.
#include <stdio.h>
#include <string.h>
FILE *fr;
struct player {
char name[50];
float DOC;
};
struct player players[50];
int main() {
fr = fopen ("playerinfo.txt", "r");
if (ftell(fr) == 0) {
fclose(fr);
printf("PLAYER FILE IS EMPTY");
return 0;
}
char words[50];
while (strcmp(fscanf(fr, "%s", words),"DONE") != 0) {
float pts;
fscanf(fr, "%f", pts);
float asts;
fscanf(fr, "%f", asts);
float mins;
fscanf(fr, "%f", mins);
struct player *aPlayer;
float theDOC = (pts + asts) / mins;
strcpy(aPlayer->name, words);
aPlayer->DOC = theDOC;
}
fclose(fr);
return 0;
}
In your code,
strcmp(fscanf(fr, "%s", words),"DONE")
does not do what you think it does. fscanf() does not return a pointer to the scanned string, rather, it returns a count (int type). Your compiler warned you. Read the man page before you proceed.
This improper usage causes the warning.
That said, you must check for the success of scanf() family of functions, otherwise, you have a very high possibility of ending up with using indeterminate values (think of the content of words, if scanning fails).
So, you break the operations into two parts.
use fgets() / fscanf() to intake the input (newline trimming, if needed). Check for success of the call.
compare the input buffer with the required string (strcmp()).
That said, I really don't see much point of the whole loop, as you'll be creating a new local variable aPlayer every time you enter the loop. I hope you know what you're doing.
Disregarding above case, a generic flow should look like
input = "Not Done";
while ('input' is not "Done")
scan values;
check for succss;
store into variables;
scan next 'input'
The problem is in your strcmp() function. Indeed, when you do:
strcmp(fscanf(fr, "%s", words),"DONE")
you compare the return of fscanf (which is an int) to the const char * "DONE". This is impossible. You need to compare directly words with "DONE".
You should do something like:
int test;
test = fscanf(fr, "%s", words);
while ((test != EOF) && (strcmp(words,"DONE") != 0)) {
float pts;
fscanf(fr, "%f", pts);
float asts;
fscanf(fr, "%f", asts);
float mins;
fscanf(fr, "%f", mins);
struct player *aPlayer;
float theDOC = (pts + asts) / mins;
strcpy(aPlayer->name, words);
aPlayer->DOC = theDOC;
test = fscanf(fr, "%s", words);
}

C How to ignore empty lines in user input?

here is my current code:
int num = 0;
char c = '#';
scanf("%d",&num);
do{
for (int i=0;i<num;i++){
printf("%c",c);
}
printf("\n");
}
while (scanf("%d", &num) == 1);
How would I have it so that if the user doesn't enter anything, that the program won't spit out a newline?
Any help is appreciated, thank you!
This code should work for what you want to do :
#include <stdio.h>
int main()
{
int num = 0;
char c = '#';
char readLine[50];
while ((fgets(readLine, sizeof readLine, stdin) != NULL) && sscanf(readLine, "%d", &num) == 1)
{
for (int i=0;i<num;i++){
printf("%c",c);
}
printf("\n");
fflush(stdout);
}
return 0;
}
The behaviour of this code is the following : fgets will read anything you enter in the standard stream (stdin), and put it in the readLine array. The program will then try to read the number which is in your readLine variable and put it in your num variable with the sscanf function. If a number is read, the program will execute the behaviour you did present in your question (writing a # character "num" times), and go back to the beginning of the loop. If anything else than a number has been read, the loop is stopped.
In general, avoid scanf. It's very easy to leave yourself with unprocessed cruft on the input stream. Instead, read the whole line and then use sscanf (or something else) to process it. This guarantees that you won't get stuck with a partially read line, those are hard to debug.
I prefer getline to fgets to read lines. fgets requires you to guess how long the input might be, and input might get truncated. getline will allocate the memory to read the line for you avoiding buffer overflow or truncation problems.
NOTE: getline is it's not a C standard function, but a POSIX one and fairly recent (2008), though it was a GNU extension well before that. Some older compilers may not have it.
#include <stdio.h>
#include <stdlib.h>
int main()
{
char c = '#';
char *line = NULL;
size_t linelen = 0;
/* First read the whole line */
while( getline(&line, &linelen, stdin) > 0 ) {
/* Then figure out what's in it */
long num = 0;
if( sscanf(line, "%ld", &num) > 0 ) {
for( int i = 0; i < num; i++ ) {
printf("%c", c);
}
printf("\n");
}
}
free(line);
return 0;
}
if( sscanf(line, "%ld", &num) > 0 ) { will ignore any line that does not match any part of the pattern, such as a blank line or a line full of words, by checking how many things matched. Yet it will still handle 0 as a valid input.
$ ./test
foo
bar
foo123
12
############
1
#
0
2
##
I also moved num inside the loop to guarantee it's reinitialized each iteration, and on the general principle of putting your variables in minimum scopes to avoid interference. And I upgraded it to a long int better able to handle the unpredictably large numbers users might type in.
Here is how I have done input parsing over the years using the fgets() and sscanf() functions. I don't write c++ much, and if I can I keep code within old style ansi C then I do.
The fgets and sscanf functions from the stdio.h library are universal and are always available on any platform.
For a character array used to read in anything, I generally set LINE_SIZE to 256 or 512 even if I know typically the line to be read is 80 characters or less. With any computer today having over 1GB of RAM, not worth worrying about allocating an extra 500 or so bytes. Obviously, if you have no idea how long the input line is then you either have to:
guess at what LINE_SIZE should be set to and not worry about it
or verify a newline character is present in line[] prior to a null character after calling fgets().
# include <stdio.h>
# define LINE_SIZE 256
int main ( int argc, char *argv[] )
{
FILE *fp;
char line[LINE_SIZE];
int nn;
int value;
fp = fopen( "somefile", "r" );
fgets( line, LINE_SIZE, fp );
/*
this way to read from standard input (i.e. the keyboard)
using fgets with stdin prevents compiler warning when using
deprecated gets function
fgets( line, LINE_SIZE, stdin );
*/
if ( line[0] != '\n' )
{
/* definitely not a blank line */
nn = sscanf( line, "%d", &num );
if ( nn == 1 )
{
/* some number placed into num variable that met the
%d conversion for the sscanf function
*/
}
}
return 0;

sscanf reading in wrong values

Fairly new to C and am trying to parse input from a file. I have no problems getting the operation and address fields but I am getting the value "32767" for the size field.
Here is the code causing issues:
#include <stdio.h>
#include <string.h>
void read_file(char *filename)
{
// operation field, address field, size field
FILE *file = fopen(filename, "r");
char buff[25];
char operation;
long address;
int size;
char *cur_trace = fgets(buff, 25, file);
while (cur_trace) {
// read indivdual fields of trace
// if cur_trace[0] == I then ignore line
if (cur_trace[0] != 'I') {
sscanf(cur_trace, " %c %lx[^,]%*c%u", &operation, &address, &size);
printf("operation: %c\n address: %lx\n size: %u\n", operation, address, size);
}
cur_trace = fgets(buff, 25, file);
}
}
int main(int argc, char const *argv[])
{
read_file("tester.txt");
return 0;
}
and here is the input text I am reading. All lines beginning with 'I' are being ignored.
I 0400d7d4,8
M 0421c7f0,4
L 04f6b868,8
S 7ff0005c8,8
The brackets is not a generic part of the format string, it's part of a specific scanf format code to read strings. It can't just be placed anywhere as a sort of pattern, or used for any other format.
And by the way, reading hexadecimal values will stop at the first non-hexadecimal character, so you don't need it anyway. Just doing e.g.
sscanf(cur_trace, " %c %lx,%u", &operation, &address, &size);
should be enough (if the types of the variables are correct).
The problem is that your format string is not parsing the 3rd argument &size, because of the format string.
The 32767 value is just uninitialized junk.
You need to check that sscanf returns 3 so that all arguments are accounted for.

Using scanf to accept user input

gcc 4.4.2
I was reading an article about scanf. I personally have never checked the return code of a scanf.
#include <stdio.h>
int main(void)
{
char buf[64];
if(1 == scanf("%63s", buf))
{
printf("Hello %s\n", buf);
}
else
{
fprintf(stderr, "Input error.\n");
}
return 0;
}
I am just wondering what other techniques experienced programmers do when they use scanf when they want to get user input? Or do they use another function or write their own?
Thanks for any suggestions,
EDIT =========
#include <stdio.h>
int main(void)
{
char input_buf[64] = {0};
char data[64] = {0};
printf("Enter something: ");
while( fgets(input_buf, sizeof(input_buf), stdin) == NULL )
{
/* parse the input entered */
sscanf(input_buf, "%s", data);
}
printf("Input [ %s ]\n", data);
return 0;
}
I think most programmers agree that scanf is bad, and most agree to use fgets and sscanf. However, I can use fgets to readin the input. However, if I don't know what the user will enter how do I know what to parse. For example, like if the user was to enter their address which would contain numbers and characters and in any order?
Don't use scanf directly. It's surprisingly hard to use. It's better to read an entire line of input and to then parse it (possibly with sscanf).
Read this entry (and the entries it references) from the comp.lang.c FAQ:
http://c-faq.com/stdio/scanfprobs.html
Edit:
Okay, to address your additional question from your own edit: If you allow unstructured input, then you're going to have to attempt to parse the string in multiple ways until you find one that works. If you can't find a valid match, then you should reject the input and prompt the user again, probably explaining what format you want the input to be in.
For anything more complicated, you'd probably be better off using a regular expression library or even using dedicated lexer/parser toolkits (e.g. flex and bison).
I don't use scanf() for interactive user input; I read everything as text using fgets(), then parse the input as necessary, using strtol() and strtod() to convert text to numeric values.
One example of where scanf() falls down is when the user enters a bad numeric value, but the initial part of it is valid, something like the following:
if (scanf("%d", &num) == 1)
{
// process num
}
else
{
// handle error
}
If the user types in "12e4", scanf() will successfully convert and assign the "12" to num, leaving "e4" in the input stream to foul up a future read. The entire input should be treated as bogus, but scanf() can't catch that kind of error. OTOH, if I do something like:
if (fgets(buffer, sizeof buffer, stdin))
{
int val;
char *chk;
val = (int) strtol(buffer, &chk, 10);
if (!isspace(*chk) && *chk != 0)
{
// non-numeric character in input; reject it completely
}
else
{
// process val
}
}
I can catch the error in the input and reject it before using any part of it. This also does a better job of not leaving garbage in the input stream.
scanf() is a great tool if you can guarantee your input is always well-formed.
scanf() has problems, in that if a user is expected to type an integer, and types a string instead, often the program bombs. This can be overcome by reading all input as a string (use getchar()), and then converting the string to the correct data type.
/* example one, to read a word at a time */
#include <stdio.h>
#include <ctype.h>
#define MAXBUFFERSIZE 80
void cleartoendofline( void ); /* ANSI function prototype */
void cleartoendofline( void )
{
char ch;
ch = getchar();
while( ch != '\n' )
ch = getchar();
}
main()
{
char ch; /* handles user input */
char buffer[MAXBUFFERSIZE]; /* sufficient to handle one line */
int char_count; /* number of characters read for this line */
int exit_flag = 0;
int valid_choice;
while( exit_flag == 0 ) {
printf("Enter a line of text (<80 chars)\n");
ch = getchar();
char_count = 0;
while( (ch != '\n') && (char_count < MAXBUFFERSIZE)) {
buffer[char_count++] = ch;
ch = getchar();
}
buffer[char_count] = 0x00; /* null terminate buffer */
printf("\nThe line you entered was:\n");
printf("%s\n", buffer);
valid_choice = 0;
while( valid_choice == 0 ) {
printf("Continue (Y/N)?\n");
scanf(" %c", &ch );
ch = toupper( ch );
if((ch == 'Y') || (ch == 'N') )
valid_choice = 1;
else
printf("\007Error: Invalid choice\n");
cleartoendofline();
}
if( ch == 'N' ) exit_flag = 1;
}
}
I make a loop call fgets until the end of the line is read, and then call sscanf to parse the data. It's a good idea to check whether sscanf reaches the end of the input line.
I rarely use scanf. Most of the times, I use fgets() to read data as a string. Then, depending upon the need, I may use sscanf(), or other functions such as strto* family of functions, str*chr(), etc., to get data from the string.
If I use scanf() or fgets() + sscanf(), I always check the return values of the functions to make sure they did what I wanted them to do. I also don't use strtok() to tokenize strings, because I think the interface of strtok() is broken.

Resources