问题
I am reading from a file in a program using C. in the file I have some dates each in a separate line exactly like this:
20190101
20190304
20180922
Now I want the program to read these as dates and find the difference between the current date and these dates. is it possible to convert these dates into a format which is readable by C? something like this: 2019.01.01
Currently, I read it using fgets
and I am not able to convert it to the format above.
This is my code:
#include <stdio.h>
#include <stdlib.h>
void FileRead(FILE *pr)
{
char date [15];
fgets(date,15,pr);
printf("date : %s", date);
}
int main()
{
char x;
FILE *pr;
pr=fopen("mytextfile.txt","r");
if(pr==NULL)
{
printf("File can not be opened.");
return 0;
}
while(scanf("%c", &x))
{
switch(x)
{
case 'v' :
FileRead(pr);
break;
case 'k' :
return 0;
}
}
fclose(pr);
return 0;
}
回答1:
The easiest way to approach the difference from now to then is to read the dates from the file and parse them into month, day and year (m, d, y
). If you are not going to do a complete validation of each conversion, then a simple way to separate the 4-digit year and 2-digit month and day is to use fscanf
with the appropriate field-width modifiers to limit the conversion to the required number of digits, e.g.
while (fscanf (fp, "%4d%2d%2d", &y, &m, &d) == 3) {
Then all that is needed within the loop is to populate the struct tm
with the year, month and day values (remembering to subtract 1900
from the year, and set each of the hour, minute and second members zero and the daylight savings time member to -1
). A simple function can do this and return time_t
after calling mktime
, e.g.
time_t fill_broken_down_time (int y, int m, int d)
{ /* initialize struct members */
struct tm bdt = { .tm_sec=0, .tm_min=0, .tm_hour=0, .tm_mday=d,
.tm_mon=m>0?m-1:0, .tm_year=y-1900, .tm_isdst=-1 };
return mktime(&bdt); /* return mktime conversion to time_t */
}
To finish, all you need is to get the time_t
values and call difftime
to get the difference in seconds between the current time and time read from the file as a double
value. Continuing the loop in main()
, e.g.
while (fscanf (fp, "%4d%2d%2d", &y, &m, &d) == 3) {
time_t now = time(NULL),
then = fill_broken_down_time (y, m, d);
printf ("date[%d] %d/%d/%d is %.2f seconds from now.\n",
n++, m, d, y, difftime (now, then));
}
Putting it altogether, you could do something like:
#include <stdio.h>
#include <time.h>
time_t fill_broken_down_time (int y, int m, int d)
{ /* initialize struct members */
struct tm bdt = { .tm_sec=0, .tm_min=0, .tm_hour=0, .tm_mday=d,
.tm_mon=m>0?m-1:0, .tm_year=y-1900, .tm_isdst=-1 };
return mktime(&bdt); /* return mktime conversion to time_t */
}
int main (int argc, char **argv) {
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
int y, m, d, n = 1;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
while (fscanf (fp, "%4d%2d%2d", &y, &m, &d) == 3) {
time_t now = time(NULL),
then = fill_broken_down_time (y, m, d);
printf ("date[%d] %d/%d/%d is %.2f seconds from now.\n",
n++, m, d, y, difftime (now, then));
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
return 0;
}
(note: the program expects the filename to read dates from as the first argument to the program (or the program will read from stdin
by default if no argument is given))
Example Input File
$ cat dat/3dates.txt
20190101
20190304
20180922
Example Use/Output
$ ./bin/time_from_now dat/3dates.txt
date[1] 1/1/2019 is 6300212.00 seconds from now.
date[2] 3/4/2019 is 943412.00 seconds from now.
date[3] 9/22/2018 is 15030212.00 seconds from now.
Edit Per-Comment Modifying Input File Format
If your data file is actually different than the three-lines of dates you originally posted with your question and it contains heading rows before the date information, then you will need to read, identify and handle those lines before handle the date line. Since you want output in days as opposed to seconds, you simply divide the number of seconds by 86400
to obtain the time difference in days.
To read an handle heading lines, simply adjust your read to read an entire line at a time into an adequately sized buffer. Declared a constant of sufficient size to ensure your buffer will be large enough, e.g.
#define MAXC 1024u /* if you need a constant, #define one (or more) */
...
int main (int argc, char **argv) {
...
char buf[MAXC]; /* buffer to hold each line read from file */
Then you will simply use sscanf
instead of fscanf
to do the exact same parse of information from each line. If the line format does not satisfy the yyyymmdd
format, you know it's not a date line - handle those lines any way you want (they are just output in the example below with the prefix "non-date line: "
.
Combined with dividing the number of seconds since the time in the file with 86400
seconds per-day, your new read loop would be similar to:
while (fgets (buf, MAXC, fp)) { /* read each line in file */
/* if line isn't a date line, just output line as non-date line */
if (sscanf (buf, "%4d%2d%2d", &y, &m, &d) != 3) {
printf ("non-date line: %s", buf);
continue;
}
time_t now = time(NULL),
then = fill_broken_down_time (y, m, d);
double secs = difftime (now, then); /* get seconds between dates */
printf ("date[%d] %02d/%02d/%04d is %11.2f sec (%g days) from now.\n",
n++, m, d, y, secs, secs / 86400.0);
}
You say:
"I am not able to open the file"
The program expects you to provide the filename to read as the first argument to the program, otherwise the program will read from stdin
by default. The means you have to provide the filename to the program, e.g.
./yourprogram your_date_file
or you have to provide that data on stdin
by either piping that information to the program from the output of some other program, or simply redirecting the file as input on stdin
, e.g.
some_utility_making_dates | ./yourprogram
or
./yourprogram < your_date_file
Incorporating all the changes, your program would look like:
#include <stdio.h>
#include <time.h>
#define MAXC 1024u /* if you need a constant, #define one (or more) */
time_t fill_broken_down_time (int y, int m, int d)
{ /* initialize struct members */
struct tm bdt = { .tm_sec=0, .tm_min=0, .tm_hour=0, .tm_mday=d,
.tm_mon=m>0?m-1:0, .tm_year=y-1900, .tm_isdst=-1 };
return mktime(&bdt); /* return mktime conversion to time_t */
}
int main (int argc, char **argv) {
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
int y, m, d, n = 1;
char buf[MAXC]; /* buffer to hold each line read from file */
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
while (fgets (buf, MAXC, fp)) { /* read each line in file */
/* if line isn't a date line, just output line as non-date line */
if (sscanf (buf, "%4d%2d%2d", &y, &m, &d) != 3) {
printf ("non-date line: %s", buf);
continue;
}
time_t now = time(NULL),
then = fill_broken_down_time (y, m, d);
double secs = difftime (now, then); /* get seconds between dates */
printf ("date[%d] %02d/%02d/%04d is %11.2f sec (%g days) from now.\n",
n++, m, d, y, secs, secs / 86400.0);
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
return 0;
}
Example Input File w/Headings
$ cat dat/3dates-w-headers.txt
This file contains dates to read and convert to days.
The file also contains this description and dates in the format:
yyyymmdd
20190101
20190304
20180922
Example Use/Output
$ ./bin/time_from_now2 dat/3dates-w-headers.txt
non-date line: This file contains dates to read and convert to days.
non-date line: The file also contains this description and dates in the format:
non-date line:
non-date line: yyyymmdd
date[1] 01/01/2019 is 6348645.00 sec (73.4797 days) from now.
date[2] 03/04/2019 is 991845.00 sec (11.4797 days) from now.
date[3] 09/22/2018 is 15078645.00 sec (174.521 days) from now.
Look things over and let me know if you have further questions.
回答2:
Once you have char*
via fgets
, use atoi
to convert to integer. Then you have the date in YYYYMMDD format. Then you can do
const int day = dateYYYYMMDD % 100;
const int month = (dateYYYYMMDD % 10000)/100;
const int year = dateYYYYMMDD / 10000;
You can use C Date and time functions to get current date.
To get different between current date and these dates, one option is to convert all dates to Julian date and then do a number difference on julian dates.
If getting julian date is difficult, normalize to same year (adding 365 or 366 based on year is leap or not as you go from lower to higher year). Then get to the same month and then do day difference.
回答3:
The output is, you just loop the file lines and set a if condition in it
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
int main(void)
{
char buff[20];
time_t now = time(NULL);
strftime(buff, 20, "%Y%m%d\n", localtime(&now));
printf("Today : %s \n\n", buff);
static const char filename[] = "file.txt";
FILE *file = fopen(filename, "r");
if (file != NULL)
{
char line[128]; /* or other suitable maximum line size */
while (fgets(line, sizeof line, file) != NULL) /* read a line */
{
fputs(line, stdout); /* write the line */
if (strcmp(line, buff) == 0)
printf("\nThis line of date equal to current date\n");
}
fclose(file);
}
else
{
perror(filename); /* why didn't the file open? */
}
return 0;
}
file.txt
20190315
20190304
20180922
来源:https://stackoverflow.com/questions/55174868/how-to-read-a-date-from-file-in-c