c - fscanf segmentation fault - c

Really strange problem with fscanf. It seems as if it can't find the file. Heres the code:
char obs_file[255];
FILE *obs_fp;
strcpy(obs_file, "/aber/dap/cetaceans/data/observers_1.txt");
obs_fp = fopen(obs_file, "r");
date_time t;
fscanf(obs_fp, "%d %d %d %d %d %d\n", &t.day, &t.mth, &t.yr, &t.hrs, &t.mns, &t.scs); //This line runs fine
obs_head.obs->time = t;
printf("%d %d %d %d %d %d\n", t.day, t.mth, t.yr, t.hrs, t.mns, t.scs);
while(feof(obs_fp) == 0) {
char id[5];
char a[7];
char b[7];
location loc;
double lng = 0.0, lat = 0.0;
fscanf(obs_fp, "%s %lf %lf", id, &lat, &lng); //Seg fault here on first run of loop
loc.lat = lat;
loc.lng = lng;
add_obs_node(make_obs_node(id, loc, t));
}
File to be read:
05 11 2014 14 53 00
AB01 52.408 -4.217
It seems like the file pointer has changed somewhere around the while statement, I would understand if I was reading over the end of file, but it fails while there are definitely lines left. Also, I know Im opening the file right, as the first fscanf runs fine.
Any ideas?

Wrong use of feof() and unlimited fscanf("%s"...
feof() reports if EOF occurred due to previous IO, not if it is about to occur.
Use instead
char id[5];
double lng = 0.0, lat = 0.0;
while(fscanf(obs_fp, "%4s%lf%lf", id, &lat, &lng) == 3) {
loc.lat = lat;
loc.lng = lng;
add_obs_node(make_obs_node(id, loc, t));
}
I suspect original code failed on the 2nd iteration. Assume the last data in the file was "AB01 52.408 -4.217\n". fscanf(obs_fp, "%s %lf %lf" would scan up to the "\n" and put "\n" back into stdin as it is not part of a double. EOF flag is not set. The use of feof() signals no EOF. So fscanf(obs_fp, "%s %lf %lf" happens again, but no data is save in id, as "%s" consume leading white-space but has not non-white-space to save. Code does not check the fscanf() return value (bad), but assumes good data in id, which may be junk. Then add_obs_node() is called with an invalid string id.
Other failure mechanisms could have occurred too - need to see more code.
Bottom line: Check fscanf() results. Limit string input.
Minor: Note that the spaces between "%d %d" are not needed, but OK to have. The final "\n" is also OK but not needed. It is not simply consuming the following '\n', but any and all following white-space.
if (6 != fscanf(obs_fp, "%d%d%d%d%d%d",
&t.day, &t.mth, &t.yr, &t.hrs, &t.mns, &t.scs)) {
Handle_BadData();
}

Related

How can fscanf(), in C, be used to read a .gro file?

I am trying to read the following gro file via a C code.
FJP in Pol Water in water t= 0.00000 step= 0
16
1FJP P 1 5.346 7.418 0.319
2FJP P 2 5.151 7.405 0.499
3FJP P 3 5.260 7.178 0.428
4FJP P 4 5.159 6.961 0.342
5FJP P 5 5.355 6.909 0.220
6FJP P 6 5.169 6.824 0.043
7FJP P 7 5.068 6.669 11.454
8FJP P 8 4.919 6.861 11.482
9FJP P 9 4.835 7.075 11.364
10FJP P 10 4.738 6.987 11.197
11FJP P 11 4.847 7.115 10.993
12FJP P 12 4.642 7.126 10.870
13FJP P 13 4.680 6.940 10.674
14FJP P 14 4.521 7.052 10.545
15FJP P 15 4.321 6.973 10.513
16FJP P 16 4.315 6.728 10.516
11.56681 11.56681 11.56681
My code is:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, const char * argv[])
{
char input_file[]="file.gro";
FILE *input;
char *myfile=malloc(sizeof(char)*80);
sprintf(myfile,"%s",input_file); //the .gro file being read in
input=fopen(myfile,"r");
double dummy1,dummy6,dummy7,dummy8,dummy9,dummy10,dummy11;
int dummy2,dummy3,dummy4,dummy5;
int lines=0;
while (fscanf(input,"FJP in Pol Water in water t= %lf step= %d",&dummy1,&dummy2)==2
||fscanf(input," %d\n",&dummy3)==1
||fscanf(input," %dFJP P %d %lf %lf %lf\n",
&dummy4,&dummy5,&dummy6,&dummy7,&dummy8)==5
||fscanf(input," %lf %lf %lf\n",&dummy9,&dummy10,&dummy11)==3)
{
printf("%lf %d\n",dummy1,dummy2);
printf("%d\n",dummy3);
printf("%d %d\n",dummy4,dummy5);
printf("%lf %lf %lf\n",dummy6,dummy7,dummy8);
printf("%lf %lf %lf\n",dummy9,dummy10,dummy11);
lines=lines+1;
}
printf("lines=%d\n",lines);
fclose(input);
}
The problem is the values printed by the various dummy variables do not match what is in the file. Also, the number of lines being read is 3 as opposed to 19, which matches the file. I am not certain what is incorrect about my fscanf() statements to read this file. Any help for this problem would be much appreciated.
Your main problem is that you are assuming scanf is better than it is.
Scanf will read and parse as many arguments as it can, and then give up. It does not rewind to the start of the scanf. Also it treats spaces and newlines (and tabs) as simply "skip all whitespace"
So the line printf("%d\n",dummy3) will try to parse the main lines, eg 1FJP
It will read the digit 1 OK into dummy3, but then get stuck because P != a whitespace.
All the other rules will then get stuck, because none of them expect a P or any string first.
If you want to do it this way, you will just have to apply the scanf statements more intelligently as and when they are expected.
The problem is that you try to read and match the header repeatedly, before each line read (in the while loop.) you should read the head once, then read the lines. You also only need to skip any given piece of whitespace once. So you end up with code like:
if (fscanf(input,"FJP in Pol Water in water t=%lf step=%d%d", &dummy1, &dummy2, &dummy3) != 3) {
fprintf(stderr, "Invalid header\n");
exit(1); }
while (fscanf(input,"%dFJP P%d%lf%lf%lf", &dummy4, &dummy5, &dummy6, &dummy7, &dummy8) == 5) {
... read a line of the table

Q: About reading data files using fscanf()

Am using Turbo C in a DOS emulator (Dosbox). In the following lines, I am trying to read integer and float data but only get the first (int) field. Have found much Q & A on the subject of reading files using fscanf() and, specifically, with space-delimited data but relevant info was scant or missing (mostly from the questions). Here is code demonstrating the problem:
#include <stdio.h>
int index;
float rtime, volts;
char infilename[10];
int *pti;
float *ptx;
float *pty;
FILE *infp;
void main(void)
{
infp = fopen("data1", "r");
pti = &index;
ptx = &rtime;
pty = &volts;
fscanf(infp, "%d %6.3f %6.3f", &index, &rtime, &volts);
printf("%3d %6.3f %6.3f\n", index, rtime, volts);
}
Here is the first line from the data file:
37 261.100 0.996
printf gives the following output:
37 0.000 0.000
Any obvious goofs? thx
The format %6.3f is incorrect for scanf(). You probably want %f, or possibly %7f. You cannot specify the number of decimals in a scanf() format.

fwrite cuts off character string

I am trying to output the statistics from my program into a file. I first create some empty strings, then amend them using sprintf, this is because I need to turn floats into chars. I then write them to a file.
I sort of works, but the output .txt file only returns 4 digits of precision regardless what I specify in sprintf.
CODE:
METRIC.RESP_TIME =(( (long int )(tval_after.tv_sec*1000000 + tval_after.tv_usec) - (long int )(tval_before.tv_sec*1000000 + tval_before.tv_usec)));
METRIC.RESP_TIME = (float) METRIC.RESP_TIME/1000000;
float ave_resp_time = METRIC.RESP_TIME/R;
float ave_through = METRIC.BYTES_RECEIVED/METRIC.RESP_TIME;
FILE *fp;
char size_str [30]; //malloc(((int)strlen(DOWNLOAD_FILE)+ (int)strlen(Q[LOCAL_QUEUE_COUNT].CHORE_NAME)))];
char rate_str [30];
char through_put_str [30];
sprintf(size_str,"TOTAL BYTES RECIEVED [B]: %5.0d ", METRIC.BYTES_RECEIVED);
sprintf(rate_str,"TOTAL TIME REQUIRED [s]: %2.8f ", ave_resp_time);
sprintf(through_put_str,"AVERAGE THROUGHPUT [B/s]: %2.8f ", ave_through);
fprintf(stdout,"%d\n",METRIC.BYTES_RECEIVED);
fp = fopen( METRICS_FILE, "w");
if(NULL == fp){
printf("Could not make metrics file: error %d ",errno);
return 0;
}
fwrite(size_str,(size_t)sizeof(size_str),1,fp);
fwrite(rate_str,sizeof(rate_str),1,fp);
fwrite(through_put_str,sizeof(through_put_str),1,fp);
fclose(fp);
return 0;
OUTPUT:
TOTAL BYTES RECIEVED [B]: 5526TOTAL TIME REQUIRED [s]: 0.001AVERAGE THROUGHPUT [B/s]: 2992
Hoping to make it look like:
TOTAL BYTES RECIEVED [B]: 55264892
TOTAL TIME REQUIRED [s]: 0.0019634
AVERAGE THROUGHPUT [B/s]: 29929054
You need an explicit new line character; fwrite() does not add one, and also you can directly use fprintf() instead of what you have.
To fix your code, do this
sprintf(size_str, "TOTAL BYTES RECIEVED [B]: %5.0d\n", METRIC.BYTES_RECEIVED);
/* ^ this will break the line */
the strcat(size_str, "\0"); is not needed.
You don't have to do all this, because you can just
fprintf(fp, "TOTAL BYTES RECIEVED [B]: %5.0d\n", METRIC.BYTES_RECEIVED);
/* ^ this will break the line */
As mentioned by #Jonathan Leffler, use well sized buffers rather than hoping that 30 is ample.
One method that has worked well is to size the buffer per the sprintf()
// char size_str [30];
// sprintf(size_str,"TOTAL BYTES RECIEVED [B]: %5.0d ", METRIC.BYTES_RECEIVED);
#define INT_MAX_PRT (sizeof(int) * CHAR_BIT/3 + 3)
const char rcv_fmt[] = "TOTAL BYTES RECEIVED [B]: %5.0d\n";
char size_str [sizeof rcv_fmt + INT_MAX_PRT];
sprintf(size_str, rcv_fmt, METRIC.BYTES_RECEIVED);
This approach is a bit more challenging with floating point as the "%f" width could be so large.
// char rate_str [30];
// sprintf(rate_str,"TOTAL TIME REQUIRED [s]: %2.8f ", ave_resp_time);
#define FLT_MAX_PRT (1 /* sign */ + FLT_MAX_10_EXP + 1)
const char *time_fmt[] = "TOTAL TIME REQUIRED [s]: %2.8f\n";
char rate_str[sizeof time_fmt + FLT_MAX_PRT + 8];
sprintf(rate_str, time_fmt, ave_resp_time);
Still, since it is possible to mistake the needed buffer size, code could also use snprintf() to minimize the harm. But in the end, a proper size buffer is needed.
Note: added '\n' to the formats.
First things first, you should get out of the habit of using sizeof on any array. This will get you into trouble faster than you realize.
Second, you seem to be doing double-duty here. There is no need to use sprintf + fwrite at all. You should just fprintf and pass you fp as the first arg.

C programming, getting the last line of file

I am writing a c program that opens a txt file and want to read the last line of the txt file.
I am not that proficient in C so bear in mind that I may not know all of the concepts in C. I am stuck at the part where I use fscanf to read all the lines of my txt file but I want to take the last line of the txt file and get the values as described below.
Here is my incomplete code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
FILE *sync;
void check()
{
int success; //to hold the results if the timestamps match
sync = fopen("database.txt","r");
char file[] = "database.txt";
while (fscanf(sync, "%d.%06d", &file) != EOF)
{
}
fclose(sync);
}
sample txt file:
/////// / //// ///// ///// //////////////// Time: 1385144574.787665 //////// /
/////// / //// ///// ///// //////////////// Time: 1385144574.787727 //////// /
/////// / //// ///// ///// //////////////// Time: 1385144574.787738 //////// /
/////// / //// ///// ///// //////////////// Time: 1385144574.787746 //////// /
/////// / //// ///// ///// //////////////// Time: 1385144574.787753 //////// /
The / are some words, symbols and numbers I do not want, just the numbers in sample txt as shown above
I appreciate any examples and pointing out errors I made so I can understand this much better.
Since I made some people confused about the text file, here is what it really is. This is the format it will be so I should know the length of each line. However, I will not be able to know how many lines there will be as it may be updated.
Socket: 0 PGN: 65308 Data: 381f008300000000 Time: 1385144574.787925 Address: 28
Socket: 0 PGN: 65398 Data: 0000000100000000 Time: 1385144574.787932 Address: 118
Socket: 0 PGN: 61444 Data: f07d83351f00ffff Time: 1385144574.787940 Address: 4
Socket: 0 PGN: 65266 Data: 260000000000ffff Time: 1385144574.787947 Address: 242
Socket: 0 PGN: 65309 Data: 2600494678fff33c Time: 1385144574.787956 Address: 29
Socket: 0 PGN: 65398 Data: 0000000100000000 Time: 1385144574.787963 Address: 118
Socket: 0 PGN: 61444 Data: f07d833d1f00ffff Time: 1385144574.787971 Address: 4
Socket: 0 PGN: 65398 Data: 0000000100000000 Time: 1385144574.787978 Address: 118
Socket: 0 PGN: 61443 Data: d1000600ffffffff Time: 1385144574.787985 Address: 3
Socket: 0 PGN: 65308 Data: 451f008300000000 Time: 1385144574.787993 Address: 28
Socket: 0 PGN: 65317 Data: e703000000000000 Time: 1385144574.788001 Address: 37
Again I am after the Time values (eg. 1385144574.787925) at the last line of the txt file.
Hope this helps.
Since you're after the last line of the file, and you didn't mention how large the file might be, it could be worth while to start reading the file from the end, and work your way backwards from there:
FILE *fp = fopen("database.txt", "r");
fseek(fp, 0, SEEK_END);//sets fp to the very end of your file
From there, you can use fseek(fp, -x, SEEK_CUR); where x is the number of bytes you want to go back, until you get to where you want... other than that, Jekyll's answer should work just fine.
However, to get the last line, I tend to do something like this:
FILE *fp = fopen("database.txt", "r");
char line[1024] = "";
char c;
int len = 0;
if (fp == NULL) exit (EXIT_FAILURE);
fseek(fp, -1, SEEK_END);//next to last char, last is EOF
c = fgetc(fp);
while(c == '\n')//define macro EOL
{
fseek(fp, -2, SEEK_CUR);
c = fgetc(fp);
}
while(c != '\n')
{
fseek(fp, -2, SEEK_CUR);
++len;
c = fgetc(fp);
}
fseek(fp, 1, SEEK_CUR);
if (fgets(line, len, fp) != NULL) puts(line);
else printf("Error\n");
fclose(fp);
The reasoning behind my len var is so that I can allocate enough memory to accomodate the entire line. Using an array of 1024 chars should suffice, but if you want to play it safe:
char *line = NULL;
//read line
line = calloc(len+1, sizeof(char));
if (line == NULL)
{
fclose(fp);
exit( EXIT_FAILURE);
}
//add:
free(line);//this line!
fclose(fp);
Once you've gotten that line, you can use Jekyll's sscanf examples to determine the best way to extract whatever you want from that line.
The way you are using fscanf is wrong as the actual vector of arguments needs to match what you are collecting (as you can see in the manpage). Instead of using fscanf you may consider using fgets and then filtering for what you are looking for in the latest raw with a regex through sscanf.
Note:: I collected the value in double format, you may choose the format that suits you the most for your problem (string?int.int?float?), in order to do this you should check for regex using scanf. Please come back if you cannot accomplish this task.
update:: due to some requests I wrote some few examples of different pattern matching. These should be a good starting point to fix your problems.
update::
1. I have seen that you added the pattern of your db file so we can now state that both #3 and #4 match and put the 3 here (faster).
2. I removed the feof check as for your request, but note that the check is fine if you know what you are doing. Basically you have to keep in mind that stream's internal position indicator may point to the end-of-file for the next operation, but still, the end-of-file indicator may not be set until an operation attempts to read at that point.
3. You asked to remove the char line[1024]={0,}; This instruction is used to initialize the line[1024] array which will contain the lines that you read from the file. This is needed! To know what that instruction is please see here
Code:
void check()
{
char line[1024]={0,}; // Initialize memory! You have to do this (as for your question)
int n2=0;
int n3=0;
sync = fopen("database.txt", "r");
if( sync ) {
while( fgets(line, 1024, sync) !=NULL ) {
// Just search for the latest line, do nothing in the loop
}
printf("Last line %s\n", line); //<this is just a log... you can remove it
fclose(sync);
// This will look for Time and it will discard it collecting the number you are looking for in n2 and n3
if (sscanf(line, "%*[^T]Time: %d.%d", &n2, &n3) ) {
printf( "%d.%d\n", n2, n3);
}
}
}
Example 2
if for instance you need to collect the value using two integers you will need to replace the sscanf of the example above with the following code:
unsigned int n2, n3;
if (sscanf(line, "%*[^0-9]%d.%d", &n2, &n3) ) {
printf( "%d.%d\n", n2, n3);
}
said this you should figure out how to collect other formats.
Example 3
A better regex. In case there are others number in the file before the giving pattern you may want to match on Time, so let's say that there isn't any T before. A regex for this can be:
if (sscanf(line, "%*[^T]Time: %d.%d", &n2, &n3) ) {
printf( "%d.%d\n", n2, n3);
}
The regex using sscanf can be not suitable for your pattern, in that case you need to consider the usage of gnu regex library or you can mix strstr and sscanf like I did in the following example.
Example 4
This can be useful if you don't find a common pattern. In that case you may want to trigger on the string "Time" using strstr before calling the sscanf
char *ptr = strstr( line, "Time:" );
if( ptr != NULL ) {
if (sscanf(ptr, "%*[^0-9]%d.%d", &n2, &n3) ) {
printf( "%d.%d\n", n2, n3);
}
}
* Note *
You may need to find your way to parse the file and those above can be only suggestions because you may have more specific or different patterns in your file but the instruction I posted here should be enough to give you the instruments to do the job in that case

C: Converting strings of doubles in a module (AKA in the kernel)

I'd love to use the outdated "atoi" kind of stuff, but it turns out I don't have it in kernel space.
I have this code in the write method of my module. Writes are being controlled by me in user space, and it so happens that the user will always write something in this format:
"0.9 9.5 7.6 "
Hence - I have this code to parse it:
ssize_t write_info( struct file *filp, const char __user *buff, unsigned long len, void *data )
{
char *seedid;
char *low_in;
char *high_in;
char *dropper;
unsigned long long seedid_var;
double d_1;
double d_2;
printk(KERN_INFO "Whaddup u writin'?\n");
dropper = kstrdup(buff, 0);
seedid = strsep(&dropper, " ");
printk("HERE IS: %s\n\n", seedid);
sscanf(seedid, "%lld", &seedid_var);
printk("AND BACK AGAIN: %lld\n\n\n", seedid_var);
low_in = strsep(&dropper, " ");
printk("HERE IS: %s\n\n", low_in);
sscanf(low_in, "%f", &d_1);
printk("AND BACK AGAIN: %f\n\n", d_1);
high_in = strsep(&dropper, " ");
printk("HERE IS: %s\n\n", high_in);
sscanf(high_in, "%f", &d_2);
printk("AND BACK AGAIN: %f\n\n", d_2);
...
I then trigger my module by echo'ing on the procfile it creates (calling my write method) like this:
echo "0.9 9.8 3.4 " > name_of_my_proc
With dmesg:
[ 2211.808474] Whaddup u writin'?
[ 2211.808505] HERE IS: 0.9
[ 2211.808508]
[ 2211.808514] AND BACK AGAIN: 0
[ 2211.808516]
[ 2211.808517]
[ 2211.808520] HERE IS: 9.8
[ 2211.808522]
[ 2211.808524] AND BACK AGAIN: %f
[ 2211.808526]
[ 2211.808529] HERE IS: 3.4
[ 2211.808531]
[ 2211.808533] AND BACK AGAIN: %f
When printing back to the kernel...nothing performs like I'd like! My 0.9 gets killed off to 0 (is that not a legit long long value, if so that would make sense). Most importantly, my doubles don't get converted - it just prints the character %f. What can I do to make them print as I typed them?
Thanks
You are using wrong scan codes.
Scan code: Data type:
%d int
%ld long
%f float
%lf double
Also note that long long is an integer data type and cannot store fractional numbers.

Resources