I have a folder with csv files. Each csv file is named by a date (eg. 01JAN2013.csv, 02JAN2013.csv ). I have to read the files in the order of their date (start date and end date are known).
So I am trying to loop through the dates, from the start date to the end date, in order to generate the file names.
Currently I am doing:
vector<string> dd{"01", "02", "03", "04", "05", "06", "07", "08", "09","10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20","21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31"};
vector<string> mmm{"JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG","SEP", "OCT", "NOV", "DEC"};
vector<string> yyyy{"2011","2012","2013","2014"};
string filePath=DataFolderPath;
for(int i=0;i<yyyy.size();i++)
{
for(int j=0;j<mmm.size();j++)
{
for(int k=0;k<dd.size();k++)
{
filePath.append(dd[k]);
filePath.append(mmm[j]);
filePath.append(yyyy[i]);
filePath.append(".csv");
}
}
}
Definitely ugly, but it gets the job done.
Is there an easier way to loop through dates in C++. Something along the lines of:
for ( currentDate = starDate; currentDate < endDate; currentDate++) {
//Do stuff
}
UPDATE:
This is the approach that I finally used, combination of both the answers below:
typdef struct tm Time;
Time startDate=makeDate(3,1,2011);
Time endDate=makeDate(24,1,2014);
time_t end=mktime(&endDate);
for(Time date=startDate;end>=mktime(&date);++date.tm_mday)
{
char buffer[16];
strftime(buffer, sizeof(buffer), "%d%b%Y.csv", &date);
std::string filename(buffer);
//To convert month to CAPS
std::transform(filename.begin()+2, filename.begin()+5,filename.begin()+2, ::toupper);
std::cout << filename << "\n";
}
I also used a makeDate helper function based on paddy's answer which return a struct tm instead of time_t:
Time makeDate( int day, int month, int year )
{
Time ttm = {0};
ttm.tm_mday= day;
ttm.tm_mon= month-1;
ttm.tm_year= year-1900;
return ttm;
}
It might not actually be easier.... But you can use the time functions from <ctime>. Something like this:
string MakeFileName( time_t t )
{
static const char* months[] = { "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
"JUL", "AUG", "SEP", "OCT", "NOV", "DEC" };
struct tm *ptm = localtime(&t);
char buffer[20];
snprintf( buffer, 20, "%02d%s%04d.CSV",
ptm->tm_day, months[ptm->tm_month], ptm->tm_year+1900 );
return string(buffer);
}
Now just get your start and end dates correct in a time_t value (remember, it's in seconds). You can just use 00:00:00 for the time.
// Note that day and month are 1-based.
time_t GetDate( int day, int month, int year )
{
struct tm ttm = {0};
ttm.tm_day = day;
ttm.tm_month = month-1;
ttm.tm_year = year-1900;
return mktime(&ttm);
}
With that helper you can set your start and end dates easily:
time_t start = GetDate(1, 1, 2011);
time_t end = GetDate(28, 10, 2013);
for( time_t t = start; t <= end; t += 86400 )
{
string filename = MakeFileName(t);
// TODO...
}
If you decide to generate the names, I think @paddy has the right general idea, but the implementation may be open to a little improvement. In particular, mktime not only does conversions, it can...fix its input, so if you give it an input of 30 Feb, it knows that's either 1 or 2 March, and will change it appropriately. Likewise, it knows that 32 December 2011 is really 1 Jan 2012 and (again) adjusts the input appropriately.
At a guess, you also probably don't really want to generate dates into the future, and just had it generating dates through the end of the year because you didn't want to modify it every date. I'm guessing just generating dates up through the date it's run is probably sufficient.
With those in mind, I'd write the code more like this:
#include <time.h>
#include <iostream>
int main() {
struct tm date;
date.tm_mon = 1;
date.tm_mday = 1;
date.tm_year = 2011 - 1900;
time_t end = time(NULL);
for (; mktime(&date) < end; ++date.tm_mday) {
char buffer[16];
strftime(buffer, sizeof(buffer), "%d%b%Y", &date);
std::cout << buffer << "\t";
}
}
For the moment this just prints out the strings, but with them generated correctly, using them as file names will normally be pretty trivial.
If you want to do as I suggested in the comment, and get all the file names, then sort them into order by date, you can convert each file name to a time_t, the sort the time_ts. One fairly easy way to do that is with the std::get_time manipulator:
time_t cvt(std::string const &filename) {
std::istringstream in(filename);
struct tm date;
in >> std::get_time(&date, "%d%b%Y");
return mktime(date);
}
With this, you could (for one possibility) put the time_ts and file names into a std::map, then just walk through the map from beginning to end, and process each file in order.
std::map<time_t, std::string> files;
std::string file_name;
while (get_file_name(&file_name))
files[cvt(file_name)] = file_name;
for (auto const &f : files)
process(f.second);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With