I'm struggling with importing lines from a text file into a C program. Below is the text I'm trying to import:
1000|Larry Normal|CELL|3048005191
1001|Shelly Nopers|CELL|3048005191
1002|Shelly Schnats|HOME|3649155831
1003|Terry Crews|HOME|3932281123
Delimited by | and with spaces in names.
Here's the code I'm using below:
FILE* patientFile = NULL;
int patientNum;
char patientName[15];
char phoneDescription[5];
char phoneNumber[11];
patientFile = fopen("patientData.txt", "r");
if (patientFile != NULL)
{
while (fscanf(patientFile, "%d|%14s|%4s|%10s\n", &patientNum, patientName, phoneDescription, phoneNumber ) != EOF)
{
printf("%d %s %s %s\n", patientNum, patientName, phoneDescription, phoneNumber);
}
fclose(patientFile);
}
else
{
printf("Failed to open patients file\n");
}
While the file can be read, I'm getting this as my output:
1024 Larry ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠ ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠
1024 Larry ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠ ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠
1024 Larry ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠ ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠
1024 Larry ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠ ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠
1024 Larry ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠ ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠
Repeating forever. Can anyone offer insight for where I'm going wrong?
"%s" insufficient to read a name
"%14s" will only read and save "Larry" of "Larry Normal". Instead use "14s[^|]" to read to a "|".
Code checks against only 1 undesired return value
As fscanf() may return values other than EOF and 4, rather than check against one of the undesired return values, check against the desired one.
// while (fscanf(patientFile, "%d|%14s|%4s|%10s\n", ...) != EOF)
while (fscanf(patientFile, "%d|%14s|%4s|%10s\n", ...) == 4)
Advanced: Better end-of-line detection via fgets()
"%d", "%s" ignore and consume leading white-space including '\n'. Better to read a line into a buffer and then parse.
// "%d|%14s|%4s|%10s\n"
#define LINE_SIZE (11 + 1 + 14 + 1 + 4 + 1 + 10 + 1 + 1)
char buf[LINE_SIZE * 2]; // Do not be stingy on potential line sizes.
while (fgets(buf, sizeof buf, patientFile) &&
sscanf(buf, "%d|%14[^|]|%4[^|]|%10[^\n]",
&patientNum, patientName, phoneDescription, phoneNumber) == 4) {
printf("%d %s %s %s\n", patientNum, patientName, phoneDescription, phoneNumber);
}
Robust code would also check for trailing text after the phone number, excessive long lines, etc.
Perhaps also start checking for acceptable text like "%10[^0-9#*]" instead of "%10[^\n]" for the phone number. See also longest possible worldwide phone number.
Try this pattern "%d|%[^|]|%[^|]|%s\n" instead. [^|] means read all characters till the |. %s will not work cuz | is not white-space.
Ref: https://cplusplus.com/reference/cstdio/fscanf/
Related
I'm expecting the loop to end when the end of the file is reached. I know that when it reaches it the value returned from fscanf will be different than 1, as it returns 1 whenever it reads something.
If I set i=3 the loop is infinite, but if I set i=2 the loop ends, which I find very weird, as the controlling expression (i!=3) is supposed to be evaluated before the adjustment one, so when I set i=3, it breaks and should test than indeed i!=3 so the loop would end (which it doesn't). When I set it to 2, it ends, so it must be incrementing it one more time and then checking.
So, my first question is why is this happening?
My second question is that with the %[^\n]s it only saves the start of the file, but with only %s it saves the whole file, but wrongly as it only scans until the space, but I want it to scanf until the new line.
My file has 1 element per line (some with spaces)
for(int i=0;i!=3;i++){
switch(i){
case 0:
if(fscanf(recordsRegistry,"%[^\n]s", (recordsArray[recordsArrayPosition]).author)!=1){
i=3;//stop condition
}
break;
case 1:
if(fscanf(recordsRegistry,"%[^\n]s", (recordsArray[recordsArrayPosition]).title)!=1){
i=3;//stop condition
}
break;
case 2:
if(fscanf(recordsRegistry,"%hu", &((recordsArray[recordsArrayPosition]).numberOfSales))!=1){
i=3;//stop condition
}
i=-1;
recordsArrayPosition++;
totalRecords++;
recordsArray=realloc(recordsArray, totalRecords*recordStructSize) ;
if(recordsArray==NULL){
fprintf(stderr, "Could not reallocate memory at line %d.\n", __LINE__);
return 3;
}
break;
}
}
Example of the file being read:
LEANN RIMES
WHAT A WONDERFUL WORLD
4628
BLUE CHEER
WHAT DOESNT KILL YOU
9664
WITHIN TEMPTATION & KEITH CAPUTO
WHAT HAVE YOU DONE
3226
WITHIN TEMPATION
WHAT HAVE YOU DONE
8093
KOKO TAYLOR
WHAT IT TAKES (THE CHESS YEARS)
7160
DOOBIE BROTHERS
WHAT WERE ONCE VICES ARE NOW HABITS
2972
LIL'ED & THE BLUES IMPERIALS
WHAT YOU SEE IS WHAT YOU GET
9443
VARIOUS ARTISTS
WHAT'S SHAKIN
4473
The struct:
typedef struct{
char author[20], title[50];
short unsigned int numberOfSales;
} RECORD;
New for looop:
for(int i=0;i!=3;i++){
switch(i){
case 0:
if(fgets(recordsArray[recordsArrayPosition].author, totalRecords, recordsRegistry)==NULL){
//printf("aa\n");
i=2;//stop condition
}
break;
case 1:
if(fgets(recordsArray[recordsArrayPosition].title, totalRecords, recordsRegistry)==NULL){
//printf("aaa\n");
i=2;//stop condition
}
break;
case 2:
if(fscanf(recordsRegistry,"%hu", &((recordsArray[recordsArrayPosition]).numberOfSales))!=1){
//printf("aaaa\n");
i=2;//stop condition
}
i=-1;
recordsArrayPosition++;
totalRecords++;
recordsArray=realloc(recordsArray, totalRecords*recordStructSize) ;
if(recordsArray==NULL){
fprintf(stderr, "Could not reallocate memory at line %d.\n", __LINE__);
return 3;
}
break;
}
}
... when fscanf returns a value different than 1 causes an infinite loop
when you set i to 3 that value will not be tested in i!=3 because before the test the i++ will be done
set i to 2
with only %s ... it only scans until the space
I want it to scanf until the new line.
if you want to read line per line use fgets rather than fscanf, do not forget to remove the probable newline
in the scanf family 's' matches a sequence of non-white-space characters, spaces are separator
man scanf says :
s Matches a sequence of non-white-space characters; the next
pointer must be a pointer to the initial element of a character
array that is long enough to hold the input sequence and the
terminating null byte ('\0'), which is added automatically. The
input string stops at white space or at the maximum field width,
whichever occurs first.
warning you mix to read line and value, when you read the value the newline is not read, replace "%hu" by "%hu\n" or much secure read the line then extract the number from it (I do that in my proposal)
from your remark
why will i++ test before i!=3?
your :
for(int i=0;i!=3;i++){
<body without continue>
}
is equivalent to
{ int i = 0;
while (i != 3) {
<body without continue>
i++;
}
}
Here a proposal :
#include <stdio.h>
#include <ctype.h>
#include <string.h>
typedef struct{
char author[20], title[50];
short unsigned int numberOfSales;
} RECORD;
#define MAXRECORDS 100
void removeEndSpaces(char * s)
{
char * p = s + strlen(s);
while ((s != p) && isspace((unsigned char) *--p))
*p = 0;
}
int main()
{
FILE * fp = fopen("f", "r");
RECORD records[MAXRECORDS];
int nrecords;
char line[32];
if (fp == NULL){
perror("cannot read f");
return -1;
}
for (nrecords = 0; nrecords != MAXRECORDS; nrecords += 1) {
if (fgets(records[nrecords].author, sizeof(records[nrecords].author), fp) == NULL)
break;
removeEndSpaces(records[nrecords].author);
if (fgets(records[nrecords].title, sizeof(records[nrecords].title), fp) == NULL) {
fprintf(stderr, "invalid input file\n");
break;
}
removeEndSpaces(records[nrecords].title);
/* the more secure way to read the number is first to read the line then read the enumber in that line */
if ((fgets(line, sizeof(line), fp) == NULL) ||
(sscanf(line, "%hu", &records[nrecords].numberOfSales) != 1)) {
fprintf(stderr, "invalid input file\n");
break;
}
}
/* nrecords values the number of records read without error */
for (int i = 0; i != nrecords; i += 1)
printf("%s : %s / %hu\n",
records[i].author, records[i].title, records[i].numberOfSales);
return 0;
}
As you see it is useless to do your stuff with the index and the code is more clear
Supposing the file f contains your input, compilation and execution :
pi#raspberrypi:/tmp $ gcc -Wall -Werror -pedantic a.c -g
pi#raspberrypi:/tmp $ ./a.out
invalid input file
LEANN RIMES : WHAT A WONDERFUL WORLD / 4628
BLUE CHEER : WHAT DOESNT KILL YOU / 9664
pi#raspberrypi:/tmp $
As you see the file is invalid, the reason is the author "WITHIN TEMPTATION & KEITH CAPUTO" more the newline is too long to be saved in 20 characters, this is why you always need to check what happens and never suppose all is ok : in your initial code out of your other problems fscanf write out of the items with an undefined behavior. To read for instance up to 20 characters including the null character in a string with (f/s)scanf use the format "%20s"
If I resize the field author to 40 all is ok :
pi#raspberrypi:/tmp $ gcc -Wall -Werror -pedantic a.c -g
pi#raspberrypi:/tmp $ ./a.out
LEANN RIMES : WHAT A WONDERFUL WORLD / 4628
BLUE CHEER : WHAT DOESNT KILL YOU / 9664
WITHIN TEMPTATION & KEITH CAPUTO : WHAT HAVE YOU DONE / 3226
WITHIN TEMPATION : WHAT HAVE YOU DONE / 8093
KOKO TAYLOR : WHAT IT TAKES (THE CHESS YEARS) / 7160
DOOBIE BROTHERS : WHAT WERE ONCE VICES ARE NOW HABITS / 2972
LIL'ED & THE BLUES IMPERIALS : WHAT YOU SEE IS WHAT YOU GET / 9443
VARIOUS ARTISTS : WHAT'S SHAKIN / 4473
pi#raspberrypi:/tmp $
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
This question already has answers here:
Extra leading zeros when printing float using printf?
(2 answers)
Closed 5 years ago.
I am trying recreate a sample LC-3 simulator as an assignment, and part of that is to have a 4 digit integer. My code is as follows:
while (read_success != NULL && !done) {
// If the line of input begins with an integer, treat
// it as the memory value to read in. Ignore junk
// after the number and ignore blank lines and lines
// that don't begin with a number.
//
words_read = sscanf(buffer, "%04d", &value_read);
// if an integer was actually read in, then
// set memory value at current location to
// value_read and increment location. Exceptions: If
// loc is out of range, complain and quit the loop. If
// value_read is outside -9999...9999, then it's a
// sentinel -- we should say so and quit the loop.
if (value_read < -9999 || value_read > 9999)
{
printf("Sentinel read in place of Memory location %d: quitting loop\n", loc);
break;
}
else if (value_read >= -9999 && value_read <= 9999)
{
cpu -> mem[loc] = value_read;
printf("Memory location: %02d set to %04d \n", loc, value_read);
cpu -> count++;
loc++;
value_read = NULL;
}
if (loc > 99)
{
printf("Reached Memory limit, quitting loop.\n", loc);
break;
}
read_success = fgets(buffer, DATA_BUFFER_LEN, datafile);
// Gets next line and continues the loop
}
fclose(datafile);
I am reading values from an sdc file with the following values:
1234
3456
-4567;
2353
3434
654
0345
7655
555
9999
10000
The problem is that 0345 shows up as 345, i want 645 to be 0645, and so on.
I tried formatting %d based on a post I saw related to this, but it is not working. Any professional insight?
Edit: I did use %04d to start, but that did not work.
If you want leading zeros to be displayed, use %04d in your printf format.
The 0 is a flag used with d (among others) that says to pad on the left with zeros.
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.
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