edit for clarity:
fscanf is working the way it is written here...it's getting the data into the array that's broken.
another edit for clarity:
The only point of failure is the memcpy line on the second iteration through the loop where fileindex is 0 and i is 1. the memcpy line works fine when i is 0.
I'm converting a program that used to read data from a binary file directly into an array using fread into reading from ascii text. Here's the setup:
#define MAX_FLIGHT_ENTRIES 27000
#define MAX_PLANES_IN_A_FLIGHT 10
typedef struct {
double local_x;
double local_y;
double local_z;
float pitch;
float roll;
float heading;
float gearpos;
float flappos;
float speedbrakepos;
float canopypos;
float afterburnerOn;
float kias;
float time; // record timestamp
} FLIGHT_ENTRY_TYPE;
static FLIGHT_ENTRY_TYPE FlightEntries [MAX_PLANES_IN_A_FLIGHT][MAX_FLIGHT_ENTRIES];
The way this used to work is, in a loop, the array would be filled via:
fread (&FlightEntries[fileIndex][i], sizeof (FLIGHT_ENTRY_TYPE), 1, pFile);
And I believe this would actually instantiate each entry in the array by putting the data directly into memory. Now I'm reading from a text file and I've tried everything I can to get the values into the array, but it only ever writes the first entry FlightEntries[0][0]. Any attempt to read or write to FlightEntries[0][1] crashes. Here's my current best attempt.
for ( i = 0; i < MAX_FLIGHT_ENTRIES; i++)
{
// If the file end is found before it should be, set values to defaults
// and save the file
if (feof(pFile))
{
FlightInfo[fileIndex].endFrameIndex = i - 1;
break;
}
else
{
float plocalx, plocaly, plocalz;
float ppitch, proll, pheading, pgearpos, pflappos, pbrakepos, pcanopypos, pafterburnon, pkias, ptime;
int fresult;
fresult = fscanf(pFile, "%f %f %f %f %f %f %f %f %f %f %f %f %f\n",
&plocalx,
&plocaly,
&plocalz,
&ppitch,
&proll,
&pheading,
&pgearpos,
&pflappos,
&pbrakepos,
&pcanopypos,
&pafterburnon,
&pkias,
&ptime);
FLIGHT_ENTRY_TYPE newEntry;
newEntry.local_x = (double)plocalx;
newEntry.local_y = (double)plocaly;
newEntry.local_z = (double)plocalz;
newEntry.pitch = ppitch;
newEntry.roll = proll;
newEntry.heading = pheading;
newEntry.gearpos = pgearpos;
newEntry.flappos = pflappos;
newEntry.speedbrakepos = pbrakepos;
newEntry.canopypos = pcanopypos;
newEntry.afterburnerOn = pafterburnon;
newEntry.kias = pkias;
newEntry.time = ptime;
memcpy (&FlightEntries[fileIndex][i], &newEntry, sizeof FLIGHT_ENTRY_TYPE);
}
}
I don't think the array entries are getting allocated properly. I've tried accessing the individual structure members directly via FlightEntries[fileIndex][i].local_x = (double)plocalx; and I've also tried using memcpy to do the same thing for each member...am I doing my pointers wrong or something? I have no idea where to go with this. Every time I get past one stumbling block, something else comes up and I think it's all related to the array as opposed to reading from file. Do I have to do something to allocate space?
The big question is:
What does fread do with binary data in FlightEntries[0][1] that memcpy isn't doing? And is my memcpy line correct? Do I need to do some kind of malloc?
...used to read data from a binary file directly into an array using fread....
EDIT
Now I'm reading from a text file and I've tried everything I can to get the values into the array.
Unless the contents of your file match the current definition of the FLIGHT_ENTRY_TYPE struct, and perhaps these definitions:
#define MAX_FLIGHT_ENTRIES 27000
#define MAX_PLANES_IN_A_FLIGHT 10
There will be problems with your read attempt, no matter what you try. (The file contents, and the struct definition MUST be in alignment to do what you are trying to do. Because you are using a text file, this should be easily verifiable.)
Also, feof(pFile) is rarely a good choice for reading a file
Consider changing it to something like this:(pseudo code)
FLIGHT_ENTRY_TYPE newEntry;
int len = sizeof(FLIGHT_ENTRY_TYPE)*2;// *2 to account for comma delimiters, etc. Change as needed
char **stringArray = {0};
//see edits in answer below for defintion of allocArray()
stringArray = allocMemory(stringArray, MAX_PLANES_IN_A_FLIGHT, len);
FILE *fp = fopen(".\\filename.bin", "rb");
if(fp && stringArray)
{
while(fgets(stringArray[i], len, fp)
{
fresult = sscanf(stringArray[i], "%f %f %f %f %f %f %f %f %f %f %f %f %f\n",
&plocalx,
&plocaly,
&plocalz,
&ppitch,
&proll,
&pheading,
&pgearpos,
&pflappos,
&pbrakepos,
&pcanopypos,
&pafterburnon,
&pkias,
&ptime);
if(fresult > 0)
{ ...assign values to newEntry struct... }
}
fclose(fp);
freeMemory(stringArray,MAX_PLANES_IN_A_FLIGHT);
}
The first three members of your struct are double values, and should therefore be read into double values. If you decide to make that change:
float plocalx, plocaly, plocalz;
TO:
double plocalx, plocaly, plocalz;
Be sure to also Change:
fresult = fscanf(pFile, "%f %f %f %f %f %f %f %f %f %f %f %f %f\n",
To:
fresult = fscanf(pFile, "%lf %lf %lf %f %f %f %f %f %f %f %f %f %f\n",
You should also check the return value of sscanf()
The return value: Converts input from the specified source string into a series of values according to the specifiers in formatString. If an input failure occurs before the first conversion, the function returns EOF (-1); otherwise, the function returns the number of input items assigned. If copying takes place between objects that overlap, the behavior is undefined.
EDIT 2
If you do need to create an array of strings (for each line of the file), these functions will help:
char ** allocMemory(char ** a, int numStrings, int maxStrLen)
{
int i;
a = calloc(sizeof(char*)*(numStrings+1), sizeof(char*));
for(i=0;i<numStrings; i++)
{
a[i] = calloc(sizeof(char)*maxStrLen + 1, sizeof(char));
}
return a;
}
void freeMemory(char ** a, int numStrings)
{
int i;
for(i=0;i<numStrings; i++)
if(a[i]) free(a[i]);
free(a);
}
//usage:
#define MAX_FLIGHT_ENTRIES 27000
#define MAX_PLANES_IN_A_FLIGHT 10
int main(void)
{
char **stringArray = {0};
stringArray = allocMemory(stringArray, MAX_PLANES_IN_A_FLIGHT, sizeof FLIGHT_ENTRY_TYPE);
...//read data into string array as shown in code above
return 0;
}
The concept here is that each line of the file is assumed to represent the data to populate one struct instance of the FLIGHT_ENTRY_TYPE struct. Stated differently, one line in the file is read for each struct in the array of structs.
Extenuating unlisted circumstances.
Turns out, since I couldn't debug, I had placed a log file snippet at the end of the loop iteration. I believe that instantiating the logfile was corrupting the array and preventing the array from being written to. Possibly a heap issue, but I'm not sure. I've learned quite a bit from this exercise about dealign with memory pointers as well as better ways to read and write data from file. So Thank you all for the help.
Related
Say Given a text file that looks like this:
a,b,c
x,y,z
where a is a char *, b contains a float and c contains a double.
For an example, the input file can look like this:
apple,$12.34,test130.8
x,y,z
I want to use fscanf() to read a, b, c and assign each one of them to a corresponding variable.
"apple" will be assigned to A of the same data type; "12.34"(not "$12.34") will be assigned to B with a float data type; so on.
My attempt was as follows:
fp = the file pointer
char A[50];
float B;
double C;
fscanf(fp, "%[^,],%[^,],%[^,]\n", A, B, C);
But I realized that %[^,]can only specify type char *; ergo, I'm not allowed to assign type char * to a float or double variable.
Is there a way to parse %[^,] to make it only specifies type float?
if I only use this:
fscanf(fp, "%s,%f,%lf\n", A, B, C);
It will be thrown off by the "$" in "12.34", and it will give me 0.000000.
Using sscanf() (instead of fscanf()) for ease of testing:
#include <stdio.h>
#include <stdlib.h>
int main() {
char *s = "apple,$12.34,test130.8\npear,$23.45,abc";
for(int offset = 0, n;; offset += n) {
char *symbol;
float price;
char *note;
if(sscanf(s + offset, " %m[^,],$%f,%m[^\n]%n", &symbol, &price, ¬e, &n) != 3) {
break;
}
printf("symbol: %s, price: %f, note: %s\n", symbol, price, note);
free(note);
free(symbol);
}
}
and the matching output (note how it demonstrate the evils of using floating points for money):
symbol: apple, price: 12.340000, note: test130.8
symbol: pear, price: 23.450001, note: abc
I used %m to have scanf() allocate the strings. If I knew the maximum size of the strings I would reuse a fixed size strings instead of dynamically allocating and freeing those.
When using fscanf() instead of break you could use feof() to see if we are done, or if the input is invalid. If it's invalid you may want to resync to the next \n with fsccnf(..., "%c", ch). For the above s[offset] == '\0' will tell if you are the end but see below.
You may find it's much easier to get a line with fgets(), then use sscanf() similar to above to extract each item. If fails you can report the line and just read the next one. fgets() will return NULL if you have no more data and it leads to cleaner code when you separate I/O and parsing.
There's already an answer from #AllanWind (using dynamic allocation for strings that my old library doesn't do.) Here's an alternative solution (that is much the same.)
First, the input file used for testing:
apple,$12.34,test130.8
banana,$20.67,testing201.45
Then the code using fscanf() with a complicated format string:
int main( void ) {
FILE *fp = fopen( "test.txt", "r" );
if( fp == NULL) {
fprintf( stderr, "fopen() failed\n" );
return -1;
}
char txt[50], word[12];
double dval1, dval2;
while( fscanf( fp, " %49[^,],%*c%lf,%11[^0123456789]%lf", txt, &dval1, word, &dval2 ) == 4 )
printf( "'%s' / %.2lf / '%s' / %.2lf\n", txt, dval1, word, dval2 );
fclose( fp );
return 0;
}
Finally, the output
'apple' / 12.34 / 'test' / 130.80
'banana' / 20.67 / 'testing' / 201.45
I have the following code which reads from a given input file into and then into struct I have made.
OFFFile ReadOFFFile(OFFFile fileData, FILE* srcFile)
{
int nvert, nfaces;
fscanf(srcFile, "%s\n");
fscanf(srcFile, "%d %d %s\n", &nvert, &nfaces);
fileData.nvert = nvert;
fileData.nfaces = nfaces;
fileData.vertices = (int *) malloc(fileData.nvert * sizeof(float));
fileData.triFaces = (int *) malloc(fileData.nfaces * sizeof(int));
// Print to check correct size was allocated
printf("%d\n", (fileData.nvert * sizeof(float)));
printf("%d\n", (fileData.nfaces * sizeof(int)));
int i;
float ftemp1, ftemp2, ftemp3;
int itemp1, itemp2, itemp3;
fscanf(srcFile, "%f", &ftemp1);
printf("%lf", ftemp1);
fscanf(srcFile, "%f", &ftemp2);
// fscanf(srcFile, " %lf", &ftemp3);
/* for (i = 0; i < nvert; ++i)
{
fscanf(srcFile, "%f %f %f\n", &ftemp1, &ftemp2, &ftemp3);
fileData.vertices[i].x = ftemp1;
fileData.vertices[i].y = ftemp2;
fileData.vertices[i].z = ftemp3;
}
*/
return(fileData);
}
The problem I am having is with the whole last section that is currently in quotes (The 2 fscanf lines above it are me attempting to test). If I have just one float being read it works fine, but when I add the second or third the whole function fails to even run, although it still compiles. I believe it to be caused by the negative sign in the input, but I don't know how I can fix it.
The data is in the form
OFF
4000 7000 0
0.8267261981964111 -1.8508968353271484 0.6781123280525208
0.7865174412727356 -1.8490413427352905 0.7289819121360779
With the floats continuing on for 4000 lines (hence for loop). These are the structs I have made
typedef struct
{
float x;
float y;
float z;
} Point3D;
typedef struct
{
int face1;
int face2;
int face3;
} triFace;
typedef struct
{
int nvert;
int nfaces;
Point3D *vertices;
triFace *triFaces;
} OFFFile;
Text dump of another file with a lot less lines, also does not work in the for loop. Only using this for testing. https://justpaste.it/9ohcc
Your main problem is the first line in the readOFFFile function:
fscanf(srcFile, "%s\n");
This tries to read a string (presumably the string OFF on the first line of the file), but you don't give fscanf any place to store the string, so it crashes. (As an aside, your compiler really should have warned you about this problem. If it didn't, it's old-fashioned, and there are lots of easy mistakes that it's probably not going to warn you about, and learning C is going to be much harder than it ought to be. Or perhaps you just need to find an option flag or checkbox to enable more warnings.)
You can tell fscanf to read and discard something, without storing it anywhere, using the * modifier. Here's a modified version of your program, that works for me.
void ReadOFFFile(OFFFile *fileData, FILE* srcFile)
{
fscanf(srcFile, "%*s");
if(fscanf(srcFile, "%d %d %*s", &fileData->nvert, &fileData->nfaces) != 2) {
exit(1);
}
fileData->vertices = malloc(fileData->nvert * sizeof(Point3D));
fileData->triFaces = malloc(fileData->nfaces * sizeof(triFace));
int i;
for (i = 0; i < fileData->nvert; ++i)
{
if(fscanf(srcFile, "%f %f %f", &fileData->vertices[i].x,
&fileData->vertices[i].y,
&fileData->vertices[i].z) != 3) {
exit(1);
}
}
}
I have made a few other changes. The other fscanf call, that reads three values but only stores two, also needs a * modifier. I check the return value of fscanf to catch errors (via a crude exit) if the input is not as expected. I got rid of the \n characters in the fscanf calls, since they're not necessary, and potentially misleading. I got rid of some unnecessary temporary variables, and I had the readOFFFile function accept a pointer to an OFFFile structure to fill in, rather than passing and returning it.
Here is the main program I tested it with:
int main()
{
OFFFile fd;
FILE *fp = fopen("dat", "r");
ReadOFFFile(&fd, fp);
for (int i = 0; i < fd.nvert; ++i)
printf("%f %f %f\n", fd.vertices[i].x, fd.vertices[i].y, fd.vertices[i].z);
}
This is still an incomplete program: there are several more places where you need to check for errors (opening the file, calling malloc, etc.), and when you do detect an error, you need to at least print a useful error message before exiting or whatever.
One more thing. As I mentioned, those \n characters you had in the fscanf format strings were unnecessary and misleading. To illustrate what I mean, once you get the program working, have it try to read a data file like this:
OFF 2 0
0 0.8267261981964111
-1.8508968353271484 0.6781123280525208
0.7865174412727356 -1.8490413427352905 0.7289819121360779
Totally malformed, but the program reads it without complaint! This is one reason (one of several dozen reasons, actually) why the scanf family of functions is basically useless for most things. These functions claim to "scan formatted data", but their definition of "formatted" is quite loose, in that they actually read free-form input, generally without any regard for line boundaries.
For some advice on graduating beyond scanf and using better, more reliable methods for reading input, see this question. See also this section and this section in some online C programming course notes.
The line:
fscanf(srcFile, "%s\n");
is invoking undefined behavior. The compiler should warn you about that. Once you've invoked UB, there's no point in speculating further about what is happening.
It's not clear to me what you intended that line to do, but if you use %s in a scanf, you need to give it a valid place to write data. You should always check the value returned by scanf to ensure that you have actually read some data, and you should never use "%s" without a width modifier. Perhaps you want something like:
char buf[256];
if( fscanf(srcFile, "%255s ", buf) == 1 ){
/* Do something with the string in buf */
}
From your comment, it seems that you are intending to use that scanf to skip a line. I strongly recommend using a while(fgetc) loop instead of scanf to do that. If you do want to use scanf, you could try something like fscanf(srcFile, "%*s\n"), but beware that it will stop at the first whitespace, and not necessarily consume an entire line. You could also do fscanf(srcFile, "%*[^\n]%*c"); to consume the line, but you are really better off using a fgetc in a while loop.
Addressing title question:
"How do I read multiple floats from one line of a file"
...with suggestions for a non-scanf() approach.
Assuming the file is opened, (and a file pointer) fp is obtained ) , the first two lines are already handled, and values into ints, say the lines value is converted to int lines;
And given your struct definition (modified to use double to accommodate type compatibility in code below):
typedef struct
{
double x;
double y;
double z;
} Point3D;
In a function somewhere here is one way to parse the contents of each data line into the 3 respective struct values using fgets(), strtok() and strtod():
char delim[] = " \n";
char *tok = NULL;
char newLine[100] = {0};
Point3D *point = calloc(lines, sizeof(*point));
if(point)
{
int i = 0;
while(fgets(newLine, sizeof newLine, fp))
{
tok = strtok(newLine, delim);
if(tok)
{
if(parseDbl(tok, &point[i].x))
{
tok = strtok(NULL, delim);
if(tok)
{
if(parseDbl(tok, &point[i].y))
{
tok = strtok(NULL, delim);
if(tok)
{
if(!parseDbl(tok, &point[i].z))
{
;//handle error
}else ;//continue
}else ;//handle error
}else ;//handle error
}else ;//handle error
}else ;//handle error
}else ;//handle error
i++;//increment for next read
}//end of while
}else ;//handle error
Where parseDbl is defined as:
bool parseDbl(const char *str, double *val)
{
char *temp = NULL;
bool rc = true;
errno = 0;
*val = strtod(str, &temp);
if (temp == str)
rc = false;
return rc;
}
Hey I need to read the data from file and print it(i have to use fscanf). My code only works for first value when I add more it's not working.
Here is my code
char name[5]; //this value is fixed size
char isWorking;
double latitude; //the length may vary
double longitude; //the length may vary
while(fscanf(fp2,"%s %c %lf %lf",name,isWorking,latitude,longitude) == 4) {
printf("\n%s %c %lf %lf",id,type,latitude,longitude);
i++;
}
my file looks like that
Bob1 Y 122.232323 -9.323232
Bob2 N 9.0001 -9.001
It doesn't print anything except when I remove other values and try to read only name
I am reading a line at the time from a text file that has this format: 2.34 4.68 5.98. I want to store these float values in an array so that I can make use of each argument later on but once I use sscanf and try to print the argument I get 0. Any idea what’s the problem is. Thank you.
char* line[2000];
char* State[2000];
sscanf(line[12],"%f", &State[12]).
printf("\n state 1: %2.3f", &State[0]);// this suppose to print 2.34 but it prints 0
You can scan them all at once:
float state[3];
if(sscanf(line[12], "%f %f %f", &state[0],
&state[1], &state[2]) != 3) {
...
} else
printf("\n state1: %f, state2: %f, state3:%f",
state[0], state[1], state[2]);
}
At the moment you're scanning into one place (State[12]) and expecting the value to be stored somewhere else (State[0]).
A few more mistakes you're making:
Storing into a char * array when you should be storing into a float array
Using pointers with printf when it expects to take in actual values
Using . to end a statement...? Not sure if this is a typo
sscanf(line[12],"%f", &State[12]).
OK. You win a prize for having a new (at least to me) way of misunderstanding how arrays work in c.
You almost certainly don't want the [12] in either case here.
The name of an array (either line or State) can be automatically converted to a char* which points at the beginning of the array. So
sscanf(line,"%f", ...);
scans the contents of line. How ever line[12] is a the single character at the position (*line)+12.
You need a float to put the result in or you need to change your format specifier from %f to %s. You should never but the results of a %f into a char*.
You are puting the number in 12th case of your table ; that's strange.
Is it not easier to use fscanf directly ?
FILE *fp = fopen(fileName);
float tabNumbers[3] = {0};
if (fp) {
for (int i = 0; i < 3; ++i)
fscanf(fp, "%f", &tabNumbers[i]);
fclose(fp);
}
this is my struct
typedef struct {
char mmsi[10];
char name[20];
double latitude;
double longitude;
int course;
double speed;
}Vessel;
this is my function which doesnt want to work
void searchByLatLong(double latitude, double longitude){
FILE * file;
struct dirent *drnt;
DIR * dir = opendir("./text");
char *path = (char *)malloc(19);
Vessel *vessel = (Vessel *)malloc(sizeof(Vessel));
while((drnt = readdir(dir)) != NULL) {
if(strcmp(drnt->d_name,".") && strcmp(drnt->d_name,"..")) {
strcpy(path,"text/");
strcat(path,drnt->d_name);
file=fopen(path, "r");
fscanf(file," %s %[a-zA-Z0-9 ]19s %lf %lf %d %lf", &vessel->mmsi,&vessel->name,&vessel->latitude,&vessel->longitude,&vessel->course,&vessel->speed);
// if (mmsi+".txt" == drnt->d_name){
printf("%s\n%s\n%lf\n%lf\n%d\n%lf\n\n",vessel->mmsi,vessel->name,vessel->latitude,vessel->longitude,vessel->course,vessel->speed);
//}
fclose(file);
}
seekdir(dir, telldir(dir));
// if(this->mmsi == mmsi){
// printVessel();
// }
}
closedir(dir);
}
When i try to load txt file it loads only two first strings then after it theres some rubbish from memory. Loading the data to another variables changes nothing ;/
This is a sample txt file which should be loaded:
3
RMS Titanic
22.222
33.333
4
5.9
The problem is with your format string. The correct format string is:
" %s %19[a-zA-Z0-9 ] %lf %lf %d %lf"
The field width goes before the conversion specifier. Also, the [...] sequence is a conversion specifier, just like 's'. The problem you're seeing is that fscanf() processes the '3' because it matches the first %s. Then it processes the 'RMS Titanic' because it matches %[a-zA-Z0-9 ] but then processing stops because there is no '19s' in the input. At this point the remaining arguments are uninitialized.
You should check the return value from fscanf(). It will tell you how many conversions were actually performed.
Thanks for posting an interesting question; I learned about fscanf() and the [] notation it accepts.
The [] notation specifies that a string is being read, therefore, the s you have appended to it is considered a literal character that should match. Similarly, the width-specifier you have provided, 19 should appear prior to the [].
Your current code would start working if you had a ship named, e.g., "RMS Titanic19s".
Change your fscanf to:
fscanf(file," %s %19[a-zA-Z0-9 ] %lf %lf %d %lf",
vessel->mmsi,vessel->name,&vessel->latitude,
&vessel->longitude,&vessel->course,&vessel->speed);
and your code will start working.
Notice I fixed some compile warnings by dropping the superfluous & from the char [] members mmsi and name -- these already point to the buffers you wish to fill. You don't need & in front of them. A pedagogical alternative form is &vessel->mmsi[0] -- the address of the first character of mmsi.