Trouble with fscanf() in c programming - c++

I am trying to read some data in a file called "data" with specific format. The data in this file is:
0 mpi_write() 100
1 mpi_write() 200
2 mpi_write() 300
4 mpi_write() 400
5 mpi_write() 1000
then code is as follow:
#include<stdlib.h>
#include<stdio.h>
typedef struct tracetype{
int pid;
char* operation;
int size;
}tracetyper;
void main(){
FILE* file1;
file1=fopen("./data","r");
if(file1==NULL){
printf("cannot open file");
exit(1);
}else{
tracetyper* t=(tracetyper*)malloc(sizeof(tracetyper));
while(feof(file1)!=EOF){
fscanf(file1,"%d %s %d\n",&t->pid,t->operation,&t->size);
printf("pid:%d,operation:%s,size:%d",t->pid,t->operation,t->size);
}
free(t);
}
fclose(file1);
}
When running with gdb, I found fscanf doesn't write data to t->pid,t->operation and t->size. Any thing wrong with my code or what? Please help me!

Your program has undefined behavior: you are reading %s data into an uninitialized char* pointer. You need to either allocate operation with malloc, or if you know the max length is, say, 20 characters, you can put a fixed string for it into the struct itself:
typedef struct tracetype{
int pid;
char operation[21]; // +1 for null terminator
int size;
} tracetyper;
When you read %s data, you should always tell fscanf the limit on the length, like this:
fscanf(file1,"%d %20s %d\n",&t->pid,t->operation,&t->size);
Finally, you should remove \n at the end of the string, and check the count of returned values instead of checking feof, like this:
for (;;) { // Infinite loop
...
if (fscanf(file1,"%d %20s %d",&t->pid,t->operation,&t->size) != 3) {
break;
}
...
}

You should loop with something like:
while ( (fscanf(file1,"%d %s %d\n",&t->pid,t->operation,&t->size)) != EOF) {
printf("pid:%d,operation:%s,size:%d",t->pid,t->operation,t->size);
}
You also need to add malloc for char array in the structure.
Also, insert a check for t as
if (t == NULL)
cleanup();

Related

C, C++ extract struct member from binary file

I'm using the following code to extract a struct member from a binary file.
I'm wondering why this prints out multiple times? when there is only one ID record, and only one struct in the file. I need to access just this member, what is the best way to do it?
I don't really understand what the while loop is doing? Is it testing for whether the file is open and returning 1 until that point?
Why use fread inside the while loop?
Does the fread need to be set to the specific size of the struct member?
Is the printf statement reading the binary and outputting an int?
FILE *p;
struct myStruct x;
p=fopen("myfile","rb");
while(1) {
size_t n = fread(&x, sizeof(x), 1, p);
if (n == 0) {
break;
}
printf("\n\nID:%d", x.ID); // Use matching specifier
fflush(stdout); // Insure output occurs promptly
}
fclose(p);
return 0;
The struct looks like this:
struct myStruct
{
int cm;
int bytes;
int ID;
int version;
char chunk[1];
}
Not really an answer but to answer a comment.
Just do
FILE *p = fopen("myfile","rb");
struct myStruct x;
size_t n = fread(&x, sizeof(x), 1, p);
if (n != 1) {
// Some error message
} else {
printf("\n\nID:%d\n", x.ID);
}
...Do as you wish with the rest of the file
I'm wondering why this prints out multiple times? when there is only one ID record, and only one struct in the file.
It won't! So if you have multiple prints the likely explanation is that the file contains more than just one struct. Another explanation could be that the file (aka the struct) was not saved in the same way as you use for reading.
I need to access just this member, what is the best way to do it?
Your approach looks fine to me.
I don't really understand what the while loop is doing?
The while is there because the code should be able to read multiple structs from the file. Using while(1) means something like "loop forever". To get out of such a loop, you use break. In your code the break happens when it can't read more structs from the file, i.e. if (n == 0) { break; }
Is it testing for whether the file is open and returning 1 until that point?
No - see answer above.
Why use fread inside the while loop?
As above: To able to read multiple structs from the file
Does the fread need to be set to the specific size of the struct member?
Well, fread is not "set" to anything. It is told how many elements to read and the size of each element. Therefore you call it with sizeof(x).
Is the printf statement reading the binary and outputting an int?
No, the reading is done by fread. Yes, printf outputs the decimal value.
You can try out this code:
#include <stdio.h>
#include <unistd.h>
struct myStruct
{
int cm;
int bytes;
int ID;
int version;
char chunk[1];
};
void rr()
{
printf("Reading file\n");
FILE *p;
struct myStruct x;
p=fopen("somefile","rb");
while(1) {
size_t n = fread(&x, sizeof(x), 1, p);
if (n == 0) {
break;
}
printf("\n\nID:%d", x.ID); // Use matching specifier
fflush(stdout); // Insure output occurs promptly
}
fclose(p);
}
void ww()
{
printf("Creating file containing a single struct\n");
FILE *p;
struct myStruct x;
x.cm = 1;
x.bytes = 2;
x.ID = 3;
x.version = 4;
x.chunk[0] = 'a';
p=fopen("somefile","wb");
fwrite(&x, sizeof(x), 1, p);
fclose(p);
}
int main(void) {
if( access( "somefile", F_OK ) == -1 )
{
// If "somefile" isn't there already, call ww to create it
ww();
}
rr();
return 0;
}
Answers in-line
I'm wondering why this prints out multiple times? when there is only one ID record, and only one struct in the file. I need to access just this member, what is the best way to do it?
The file size is 2906 bytes and fread is only reading sone 17 bytes at a time, and this goes on in a loop
I don't really understand what the while loop is doing? Is it testing for whether the file is open and returning 1 until that point?
The total number of elements successfully read is returned by fread
Why use fread inside the while loop?
In this case while is not necessary. just one fread is enough. Fread is sometimes used in a while loop when input from some other source like UART is being processed and the program has to wait for the said number of bytes t be read
Does the fread need to be set to the specific size of the struct member?
No. Reading the entire struct is better
Is the printf statement reading the binary and outputting an int?
No

fgets and sscanf to read file into an array

I am trying to implement a function that reads txt files that contain 2 double and int per line with a space as a delimiter, for example (data.txt):
3.1 2.5 1
3.4 4.5 2
.....
I implemented the function below, it takes as arguments the file name, pointers to 2 double and an int. Each pointer will be used to retrieve a column from the file. I used
fgets
to get the line and then
sscanf
to extract the three values from the line. The program crushes at run time (error: Windows has triggered a breakpont in program.exe )
... When I put a breaking point before returning from the main function the retrieved values are corrected but the program crushes when I hit continue.
To debug the function I added the following lines of code to the function ReadFile below:
if (buf[strlen(buf)-1] != '\0')
{
putchar(buf[strlen(buf)-1]); //surprisingly this line prints 1 instead of a space...
exit(1);
}
With this change the code exit without reaching the end of the function. I don't understand this because the buf variable is long enough to hold all the characters of one line from the sample file.
I would appreciate your comments to fix this problem.
Thank you.
bool ReadFile(const char* fileName, double* x, double* y, int* t, int size)
{
FILE* fp;
char buf[5000];
fp = fopen( fileName, "r" );
for( int i = 0; i < size && fgets(buf, 5000, fp) != NULL; i++)
{
buf[strlen(buf)-1] = '\0';
if (buf[strlen(buf)-2] != '\0')
{
putchar(buf[strlen(buf)-1]);//surprisingly this line prints 1 instead of a space...
exit(1);
}
sscanf(buf, "%lf %lf %d", &x[i], &y[i], &t[i]);
}
fclose(fp);
return true;
}
The problem was in the caller, the size was set to 1 instead of the number of lines in the data file.

Reading and printing characters from a user defined text file

I am trying to work out how I can print character by character the contents of a user-defined text file. I believe I have got the retrieval of the file correct but I am unsure how I can print each character.
#include <stdio.h>
#include <ctype.h>
#define ELEMENT 300
#define LENGTH 20
void main(char str[ELEMENT][LENGTH])
{
FILE *infile;
char textfile[1000];
char read_char;
int endoff;
int poswithin = 0;
int wordnum= 0;
printf("What is the name of your text file?: ");
scanf("%s", &textfile);
infile=fopen(textfile,"r");
if (infile == NULL) {
printf("Unable to open the file.");
}
else
{
endoff=fscanf(infile,"%c",&read_char);
while(endoff!=EOF);
{
This is where I believe I'm stuck. The first character is read into the variable read_char but then it doesn't seem to print anything?
if(read_char>=65&&read_char<=90 || read_char<=65)
{
str[wordnum][poswithin]=read_char;
printf("%c", read_char);
poswithin++;
}
else
{
str[wordnum][poswithin]=(char)"\n";
poswithin=0; wordnum++;
}
endoff=fscanf(infile, "%s", &read_char);
}
}
fclose(infile);
}
Typo in the format specifier to your second call to fscanf
endoff=fscanf(infile, "%s", &read_char);
should be
endoff=fscanf(infile, "%c", &read_char);
Also,
str[wordnum][poswithin]=(char)"\n";
shouldn't be casting a string literal to char and probably should be adding a NULL terminator rather than a newline:
str[wordnum][poswithin]='\0';
Finally, you shouldn't try to declare str as an argument to main.
char str[ELEMENT][LENGTH];
int main() // or int main(int argc, char* argv[])
Using fscanf with %c format specifier is overkill for reading a single character from a file.
Try fgetc to read one character. The function avoids the overhead of parsing a format specifier string and variable number of arguments.
A more efficient method is to allocate a buffer or array and read "chunks" of chars from a file, using fread. You can then scan the buffer or array. This has less function call overhead than many calls to read single bytes. Efficient buffer sizes are multiples of 512 to conform with disk drive sector sizes.

Searching for a word in a text using C, and display the info after that word

Say I have a text file like this:
User: John
Device: 12345
Date: 12/12/12
EDIT:
I have my code to successfully search for a word, and display the info after that word. However when I try to edit the code to search for 2 or 3 words and display the info after them instead of just 1 word, I cannot get it to work. I have tried adding codes into the same while loop, and creating a new while loop for the other word, but both doesn't work. There must be something I am doing wrong/not doing.
Please advice, thanks!
Here is my code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
char file[100];
char c[100];
printf ("Enter file name and directory:");
scanf ("%s",file);
FILE * fs = fopen (file, "r") ;
if ( fs == NULL )
{
puts ( "Cannot open source file" ) ;
exit( 1 ) ;
}
FILE * ft = fopen ( "book5.txt", "w" ) ;
if ( ft == NULL )
{
puts ( "Cannot open target file" ) ;
exit( 1 ) ;
}
while(!feof(fs)) {
char *Data;
char *Device;
char const * rc = fgets(c, 99, fs);
if(rc==NULL) { break; }
if((Data = strstr(rc, "Date:"))!= NULL)
printf(Data+5);
if((Data = strstr(rc, "Device:"))!=NULL)
printf(Device+6);
}
fclose ( fs ) ;
fclose ( ft ) ;
return 0;
}
Ok, hope I can clear it this time. Sorry if I get confusing sometimes but my english is not the best.
I'll explain the implementation inside comments:
#define BUFFSIZE 1024
int main()....
char buff[BUFFSIZE];
char delims[] = " "; /*Where your strtok will split the string*/
char *result = NULL;
char *device; /*To save your device - in your example: 12345*/
char *date; /*To save the date*/
int stop = 0;
fp = fopen("yourFile", "r");
while( fgets(buff, BUFFSIZE,fp) != NULL ) /*This returns null when the file is over*/
{
result = strtok( buff, delims ); /*You just need to do reference to buff here, after this, strtok uses delims to know where to do the next token*/
while(result != NULL){ /*Strtok returns null when finishes reading the given string*/
if(strcmp(result,"Device")==0){ /*strcmp returns 0 if the strings are equal*/
result = strtok(NULL, delims); /*this one gets the 12345*/
device = (char*)malloc((strlen(result)+1)*sizeof(char)); /*Alocate the right amount of memory for the variable device*/
strcpy(device, result); /*Now, device is "12345"*/
}
/*Here you do the same but for the string 'Date'*/
if(strcmp(result,"Date")==0){ /*strcmp returns 0 if the strings are equal*/
result = strtok(NULL, delims); /*this one gets the 12345*/
date = (char*)malloc((strlen(result)+1)*sizeof(char)); /*Alocate the right amount of memory for the variable device*/
strcpy(date, result); /*Now, device is "12/12/12"*/
}
/*And you can repeat the if statement for every string you're looking for*/
result = strtok(NULL,delims); /*Get the next token*/
}
}
/*No strtok necessary here */
...
Hope this helps.
fgetc returns an integer value, which is character, promoted to int.
I suppose you meant fgets which reads a whole line, but you need to reserve memory for it, for example:
#define BUF 100
...
char c[BUF];
fgets(c, BUF, fs);
Some helpful links.
There are a couple of problems in your code: basically it never compiled.
Here is a version with small cleanups - which at least compiles:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
char file[100];
char c[100];
printf ("Enter file name and directory:");
scanf ("%s",file);
FILE * fs = fopen (file, "r") ;
if ( fs == NULL ) {
puts( "Cannot open source file" ) ;
exit(1 ) ;
}
while(!feof(fs)) {
char *Data;
char const * rc = fgets(c, 99, fs);
if(rc==NULL) { break; }
if((Data = strstr(rc, "Device"))!= NULL)
printf("%s", Data);
}
fclose ( fs ) ;
return 0;
}
Problems I found:
Missing include for exit()
Missing parameter for exit()
Missing while loop to run through the whole input file.
The output file was never used.
Missing return value of 'main'
Fancy Data[5]
Changed fgetc() to fgets()
I only did minimal edits - it's not perfect at all....
IMHO I would go for C++: many things are much simpler there.
If printf() isn't a hard/fast rule, and the input requirements are really this simple, I'd prefer a state-machine and a constant-memory input:
int c, x = 0; // c is character, x is state
while(EOF!=(c=getchar())){ // scanner entry point
if(c == '\n') x=0; // newline resets scanner
else if(x == -1) continue; // -1 is invalid state
else if (x < 7 && c=="Device:"[x])x++; // advance state
else if (x == 7 && isspace(c)) continue; // skip leading/trailing whitespace
else if (x == 7) putchar(c); // successful terminator (exits at \n)
else x = -1; // otherwise move to invalid state
}
I would do that with two loops: one to get a line from the file and other to make tokens from the line read.
something like:
#define BUFFSIZE 1024
int main()....
char buff[BUFFSIZE];
char delims[] = " ";
char *result = NULL;
int stop = 0;
fp = fopen("yourFile", "r");
while( fgets(buff, BUFFSIZE,fp) != NULL ) /*This returns null when the file is over*/
{
result = strtok( buff, delims ); /*You just need to do reference to buff here, after this, strtok uses delims to know where to do the next token*/
while(result != NULL){ /*Strtok returns null when finishes reading the given string*/
if(strcmp(result,"Device")==0){ /*strcmp returns 0 if the strings are equal*/
stop = 1; /*Update the flag*/
break; /*Is now possible to break the loop*/
}
result = strtok(NULL,delims); /*Get the next token*/
}
if(stop == 1) break; /*This uses the inside flag to stop the outer loop*/
}
result = strtok(NULL, delims); /*Result, now, has the string you want: 12345 */
...
this code is not very accurate and I didn't tested it, but thats how I would try to do it.
Hope this helps.
My suggestion is to use fread to read all the file.You could read it character by character, but IMHO (a personal taste here) it's simpler to get a string containing all the characters and then manipulating it.
This is the function prototype:
size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
It returns the number of elements read.
For example:
char buffer[100];
size_t n= fread(buffer, 1,100, fs);
Then you can manipulate the string and divide it in tokens.
EDIT
There is a nice reference with also an example of how dividing a string into tokens here:
http://www.cplusplus.com/reference/cstring/strtok/
c and Data are char-pointers, pointers to (the start of a list of) character value(s).
fgetc's prototype is int fgetc ( FILE * stream ); meaning that it returns (one) integer value (an integer is convertible to a single char value).
If fgetc's prototype would've been int * fgetc ( FILE * stream ); the warning wouldn't have appeared.
#Dave Wang
My answer was too big to be a comment. So here it goes:
You're welcome. Glad to help.
If you make a new loop, the fgets won't work because you are already 'down' in the text file. Imagine something like a pointer to the file, every time you 'fget it' from a file pointer, you advance that pointer. You have functions to reload the file or push that pointer up, but it is not efficient, you've already passed by the information you want, there must be a way to know when.
If you're using my implementation, that is done by using another string compare inside the loop:
if(strcmp(result,"date") == 0)
If you enter this if, you know that the next value in result token with strtok is the actual date.
Since you have now two conditions to be tested, you can't break the outer loop before having both of them. This can be accomplished by two ways:
1-Instead of a flag, use a counter that is incremented everytime you want an information. If that counter has the same number of information you want, you can break the outer loop.
2-Don't break the outer loop at all! :)
But in both, since there are 2 conditions, make sure you treat them inside the ifs so you know that you dealing with the right information.
Hope this helps. Anything, just ask.

Passing a character array to function | Strange error

Basically I have a buffer in which i am looking for various flags to read certain fields from a binary file format. I have file read into a buffer but as i started to write code to search the buffer for the flags i immediately hit a wall. I am a C++ noob, but here is what i have:
void FileReader::parseBuffer(char * buffer, int length)
{
//start by looking for a vrsn
//Header seek around for a vrns followed by 32 bit size descriptor
//read 32 bits at a time
int cursor = 0;
char vrsn[4] = {'v','r','s','n'};
cursor = this->searchForMarker(cursor, length, vrsn, buffer);
}
int FileReader::searchForMarker(int startPos, int eof, char marker[], char * buffer)
{
int cursor = startPos;
while(cursor < eof) {
//read ahead 4 bytes from the cursor into a tmpbuffer
char tmpbuffer[4] = {buffer[cursor], buffer[cursor+1], buffer[cursor+2], buffer[cursor+3]};
if (strcmp(marker, tmpbuffer)) {
cout << "Found: " << tmpbuffer;
return cursor;
}
else {
cout << "Didn't Find Value: " << marker << " != " << tmpbuffer;
}
cursor = cursor + 4;
}
}
my header looks like this:
#ifndef __FILEREADER_H_INCLUDED__
#define __FILEREADER_H_INCLUDED__
#include <iostream>
#include <fstream>
#include <sys/stat.h>
class FileReader {
public:
FileReader();
~FileReader();
int open(char *);
int getcode();
private:
void parseBuffer(char *, int);
int searchForMarker(int, int, char[], char *);
char *buffer;
};
#endif
I would expect to get back a match for vrsn with strcmp but my result looks like this
Didn't Find Value: vrsn != vrsn
Found:
It looks like it finds it on the second pass after its passed the char array i am looking for.
Relevant hexcode
Your problem is two-fold:
strcmp returns "0" on success, not on failure. Read the documentation.
strcmp expects null-terminated strings. You say that you have chosen non-terminated char arrays because that's what your DB library uses. Well, fine. But still, you are violating the requirements of strcmp. Use strncmp instead (which takes a length argument) or, preferably, actually write C++ and start using std::vector<char> and friends.
Shouldn't that be something like int FileReader::searchForMarker(...) { .... }?
For the second query, I guess the strcmp works when it has two null terminated strings as its arguments. For example str1[]="AAA"; and str2[]="AAA"; then strcmp() would be used as
if(strcmp(str1,str2)==0) which will return 0 to indicate that they are equal. In your case, the tmpbuffer that you have created is not a null terminated string unless you add \0 in the end.So you might want to add \0 in the end of your tmpbuffer to create a string of 'v' 'r' 'n' 's'.
char vrsn[4] = {'v','r','s','n'};
Contains only the 4 characters specified. There is no room for a null character at the end.
char tmpbuffer[4] = {buffer[cursor], buffer[cursor+1], buffer[cursor+2], buffer[cursor+3]};
Contains only the 4 characters from buffer. There is no room for a null character at the end.
Eventually you call:
if (strcmp(marker, tmpbuffer)) {
The strcmp() function expects each of its parameters to end with a null character ('\0'). It wants to work with strings, which are null terminated.
Since your data is not null terminated, you probably want to use memcmp() instead of strcmp().
Also, strcmp() returns zero when its arguments are equal, so the condition in the if statement is inverted. (Zero is false, everything else is true.) The memcmp() function will also return zero when its arguments are equal.