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);
}
Related
Somewhat unexperienced with C here!
I'm using CLion to write a program and keep getting this warning message whenever I use fscanf to store a value from an input file into a variable:
Clang-Tidy: 'fscanf' used to convert a string to an integer value, but
function will not report conversion errors; consider using 'strtol'
instead
I don't understand this error as I thought fscanf was the function I should be using to read input files? Can someone explain (at a noob level) what's wrong with the way I'm using it?
Here's a sample of my code:
FILE *initial_configuration_box_1_pointer;
initial_configuration_box_1_pointer = fopen("initial_configuration_box_1.xyz", "r");
fscanf(initial_configuration_box_1_pointer, "%d\n", &N1); // Warning here.
fscanf(initial_configuration_box_1_pointer, "pid\tx\ty\tz\n"); // No warning here.
for (i = 1; i <= n1; i++)
{
fscanf(initial_configuration_box_1_pointer, "p\t%lf\t%lf\t%lf\n", &rx1[i - 1], &ry1[i - 1], &rz1[i - 1]); // Warning here.
}
fclose(initial_configuration_box_1_pointer);
I'm aware similar questions have been asked, but I couldn't understand any of the (few) answers they got...
There are a lot of good reasons for a beginner to avoid scanf completely. (Read http://sekrit.de/webdocs/c/beginners-guide-away-from-scanf.html). If you're going to use it interactively, never end a format string with whitespace, since doing so will just confuse the user. And always check the value returned by scanf to see if it actually matched any input. A common error is for the input to fail to match the expected data, and the scanf loop becomes an infinite loop repeatedly checking the same invalid input. Perhaps the warning is suggesting the second point above: since scanf does not validate the input for you, you have to do it explicitly by checking how many conversion specifiers scanf was able to match. Try something like:
#include <stdio.h>
#include <stdlib.h>
int
main(int argc, char ** argv)
{
int n1;
const char *path = argc > 1 ? argv[1] : "initial_configuration_box_1.xyz";
FILE *ifp;
if( (ifp = fopen(path, "r")) == NULL ){
perror(path);
return EXIT_FAILURE;
}
if( 1 != fscanf(ifp, "%d", &n1) || n1 <= 0 ){
fprintf(stderr, "Invalid input\n");
return EXIT_FAILURE;
}
fscanf(ifp, " pid x y z");
double rx1[n1];
double ry1[n1];
double rz1[n1];
for( int i = 0; i < n1; i++ ){
if( 3 != fscanf(ifp, " p %lf %lf %lf", rx1 + i, ry1 + i, rz1 + i) ){
fprintf(stderr, "Invalid input near line %d\n", i);
return EXIT_FAILURE;
}
}
if( fclose(ifp) ){
perror(path);
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Note that whitespace in a scanf format string does not match exactly, so using \n or \t or is all the same. Usually, people just use a single space to make it easier to read. Also, whitespace between certain conversion specifiers (notable %d and %lf) is irrelevant, and only included for readability.
I'm trying to read numbers from the file in a 2D-Array, i have to skip the first row and first column, rest all have to save in an array, i've tried using sscanf, fscanf and even strtok() but failed miserably. So please help me to solve this issue.
Thanx in advance,
Link to the file
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char* argv[]){
FILE *f=fopen("Monthly_Rainfall_Himachal.txt","r");
float data[12][12];
int i,j;
char newLine[1000];
fgets(newLine,1000,f);
char* item,waste;
i=0;
while(1)//read file line by line
{
fscanf(f, "%s %f %f %f %f %f %f %f %f %f %f %f %f ", waste, &data[i][0], &data[i][1], &data[i][2], &data[i][3], &data[i][4], &data[i][5], &data[i][6], &data[i][7], &data[i][8], &data[i][9], &data[i][10], &data[i][11]);
i++;
if(feof(f))break;
}
fclose(f);
for(i=0 ;i<12 ;i++){
for(j=0 ;j<12 ;j++){
printf("%.1f\t",data[i][j]);
}
printf("\n");
}
return 0;
}
Problems:
You don't check if the fopen was successful in opening the file or not and blindly assume it did.
Check its return value:
if(f == NULL)
{
fputs("fopen failed! Exiting...\n", stderr);
return EXIT_FAILURE;
}
Instead of reading and storing the first line, you can just read and discard it using scanf:
scanf("%*[^\r\n]"); /* Discard everything until a \r or \n */
scanf("%*c"); /* Discard the \r or \n as well */
/* You might wanna use the following instead of `scanf("%*c")`
if there would be more than one \r or \n
int c;
while((c = getchar()) != '\n' && c != '\r' && c != EOF);
But note that the next fscanf first uses a `%s` which
discards leading whitespace characters already. So, the
`scanf("%*c");` or the while `getchar` loop is optional
*/
You have an unused character pointer item and a character variable waste. Both of these are unnecessary. So, remove them.
In the very long fscanf line, you first try to scan in a string into a character variable which invokes Undefined Behavior and things go haywire. You also need to check its return value to see if it was successful.
Replace that fscanf line with the following:
if(fscanf(f, "%*s") == EOF)
{
fputs("End Of File! Exiting...\n", stderr);
return EXIT_SUCCESS;
}
for(j = 0; j < 12; j++)
{
if(fscanf(f, "%f", &data[i][j]) != 1)
{
fputs("End Of File or bad input! Exiting...\n", stderr);
return EXIT_SUCCESS;
}
}
You assume the input is of a maximum of 12 lines, but if it contains more than 12 lines, your code will invoke Undefined Behavior due to an array overrun.
Check the value of i along with the feof to make sure it does not go beyond 11:
if(i >= 12 || feof(f))
Note: I did not test any of the above code. Please correct me if I've made a mistake. Thanks!
I have a problem with sscanf function. To be more specific I want read a float from a txt file (I want only one float for line) but sscanf doesn't recognize these situations:
3.4t
or
4.t6
or
4.5 6.5
or
(this is an empty line)
I use this code:
#define LUNRIGA 200
char riga[LUNRIGA+1];
while (fgets(riga,LUNRIGA,f) != NULL) {
r = sscanf(riga,"%f",&numeri[i]);
if (r == 1) { /* riga valida */
printf("OK");
}else{
printf("Error");
return 1;
}
}
sscanf is notoriously picky about the input. You will have better luck using strtod or strtol - they can read a value even if it's followed by junk. Change your code as follows:
#define LUNRIGA 200
char riga[LUNRIGA+1];
char* tempPtr;
while (fgets(riga,LUNRIGA,f) != NULL) {
numeri[i] = strtof( riga, &tempPtr );
if (tempPtr > riga) { /* riga valida */
printf("OK");
}
else {
printf("Error");
return 1;
}
}
Note by the way that you don't seem to increment i in your loop - you might want to see if that's actually what you want, or if you would like to increment it every time you get a valid number (assuming you don't just want the last value, but all of them...)
As a little demo of the behavior of strtod, I wrote a few lines of code:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
char* s1="123.45t";
char* p1;
char* s2 = " notanumber";
double d1, d2;
d1 = strtod(s1, &p1);
printf("the number is %.2lf; the pointer is now %p; string is at %p\n", d1, s1, p1);
d2 = strtod(s2, &p1);
printf("the number is %.2lf; the pointer is now %p; string is at %p\n", d2, s2, p1);
}
The output of this is:
The number is 123.45; the pointer is now 0x400668; string is at 0x40066e
The number is 0.00; the pointer is now 0x400670; string is at 0x400670
As you can see, when reading garbage the pointer returned points to the start of the string - indicating "failed". When it is successful, the pointer is pointing to "where I stopped reading", which is "after successfully converting a bit of string to double.
If you want to be sure there's no junk on the line, you can use the %n directive to find out where conversion stops and decide what to do about what comes afterwards on the line:
char riga[LUNRIGA];
float numeri[NUM_VALUES];
int offset;
int i;
for (i = 0; i < NUM_VALUES && fgets(riga, sizeof(riga), f) != NULL; i++)
{
int r = sscanf(riga, "%f%n", &numeri[i], &offset);
if (r == 1 && riga[offset] == '\n')
printf("OK (got %f from <<%s>>\n", numeri[i], riga);
else
{
printf("Error processing <<%s>>\n", riga);
return 1;
}
}
You can do some extra processing before the sscanf(), for example to check that you read a newline (so there isn't the residue of an extra long line still to be read) and to remove the newline (but then you need to change the condition).
Note the use of sizeof(riga), and the check for not overflowing numeri by imposing a for loop on the code, and the presence of a newline at the end of each message printed.
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;
}
I am currently trying to scan a single line in from a file but having a snag at strings.
This is the example line my professor told me to work on.
enum status{MEM,PREP,TRAV}
union type { double int day, char* title, float cost}
13953 P 12 26 2011 1 5 2012 2 A 3.30 249.00 A 2.0 148.00 MEM Cuba Christmas 3 0 2 Sierra Del Rosario, Cuba
I'm fine with everything accept at the point (MEM Cuba Christmas) when I'm scanning it in from a FILE. I read the first part of the data just using fscanf(), but MEM is a enumerated type with a union type that dictates the following input. My problem is with the syntax of the scanning. I tried using getline starting at MEM but I hit snags with the tokenizing since the city / country can have spaces. Not sure what other scans to use I was looking at sscanf() but wasn't sure if it works with files.
UPDATED:
int main(void);
{
int m, length = 100;
char *word, file_name[100];
FILE *file_point
printf("Please enter file name with .txt extension:");
scanf("%s", file_name);
file_point = fopen(file_name,"r");
while (fscanf(file_point, "%d", &m) != EOF)
{
temp.dest_code = m;
fscanf(file_point, " %c %d %d %d %d %d %d %d",
&temp.area_code,
&temp.Smonth, &temp.Sday, &temp.Syear,
&temp.Emonth, &temp.Eday, &temp.Eyear,
&temp.leg_num);
for (n=0; n < temp.leg_num; n++)
{
fscanf(file_point," %c %f %f",
&temp.tleg[n].travel_type,
&temp.tleg[n].travel_time,
&temp.tleg[n].cost);
}
fscanf(file_point," %d %d %d ",
&temp.adult,
&temp.child,
&temp.infant);
temp_name = (char *)malloc(length + 1);
getline (&temp_name, &length, file_point);
word = strtok(temp_name, ",");
temp.dest_name=(char *)malloc(strlen(word)+1);
strcpy(temp.dest_name, word);
word = strtok(NULL, ",");
temp.dest_country=(char *)malloc(strlen(word)+1);
strcpy(temp.dest_country,word2);
printf("name:%s country:%s\n", temp.dest_name, temp.dest_country);
printf("adult:%d , child:%d , infant:%d \n", temp.adult, temp.child, temp.infant);
}
}
This was the code I was using as a base that I came up with but not sure how to handle the enumerated and union. I was thinking of doing something like:
getline(&status, &length, file_point);
but how do I convert string to integer or float?
If I understand your problem properly (I'm not sure I do), then you face the problem of seeing 'MEM' (or 'PREP' or 'TRAV') as a string in the input, and you have to understand how to handle the following data. The enum suggests that you might want to convert the string MEM to the value of MEM in the enumeration.
It is hard to fully automate such a conversion. It would be simplest simply to recognize the strings and decide what to do based on the string:
if (strcmp(found_string, "MEM") == 0)
...do the MEM stuff...
else if (strcmp(found_string, "PREP") == 0)
...do the PREP stuff...
else if (strcmp(found_string, "TRAV") == 0)
...do the TRAV stuff...
else
...report unknown type code...
However, you can create a structure to handle the conversion from string to enumeration value.
struct StateConv
{
const char *string;
enum state number;
};
static struct StateConv converter[] =
{
{ "MEM", MEM },
{ "PREP", PREP },
{ "TRAV", TRAV },
};
enum { NUM_STATECONV = sizeof(converter) / sizeof(converter[0]) };
enum state state_conversion(const char *string)
{
for (int i = 0; i < NUM_STATECONV; i++)
{
if (strcmp(string, converter[i].string) == 0)
return(converter[i].number);
}
fprintf(stderr, "Failed to find conversion for %s\n", string);
exit(1);
}
You need a better error handling strategy than 'exit on error'.
Your scanning code will need to read the word, and then call state_conversion(). Then depending on what you get back, you can read the remaining (following) data in the correct way for the state you were given.
No, you can't do that in the way you are trying. MEM in your file is a string type, you need to parse it like you parse a string and then set the value of your enum according to that string.
For example, when you want to parse your status type (MEM,PREP,TRAV):
char typeBuffer[6];
fscanf(file_point,"%5s",typeBuffer);
Then manually compare the content of typeBuffer:
status stat;
if (strcmp(typeBuffer, "MEM") == 0){
stat = MEM;
}
The conversion between string type and enum cannot be implicit.