Boost, how to parse following string to date/time - c++

I have the following milli/micro second accuracy string to parse into some sort of boost datetime.
std::string cell ="20091201 00:00:04.437";
I have seen documentation regarding facets. Something like this
date_input_facet* f = new date_input_facet();
f->format("%Y%m%d %F *");
but I don't know how to use them.
I tried this program with code scavenged from StackOverflow, but I can't get the millis to show:
#include <string>
#include <iostream>
#include <sstream>
#include <fstream>
#include <map>
#include <boost/algorithm/string.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/date_time.hpp>
namespace bt = boost::posix_time;
const std::locale formats[] =
{
std::locale(std::locale::classic(),new bt::time_input_facet("%Y%m%d %H:%M:%S.f")),
std::locale(std::locale::classic(),new bt::time_input_facet("%Y-%m-%d %H:%M:%S")),
std::locale(std::locale::classic(),new bt::time_input_facet("%Y/%m/%d %H:%M:%S")),
std::locale(std::locale::classic(),new bt::time_input_facet("%d.%m.%Y %H:%M:%S")),
std::locale(std::locale::classic(),new bt::time_input_facet("%Y-%m-%d"))
};
const size_t formats_n = sizeof(formats) / sizeof(formats[0]);
std::time_t pt_to_time_t(const bt::ptime& pt)
{
bt::ptime timet_start(boost::gregorian::date(1970,1,1));
bt::time_duration diff = pt - timet_start;
return diff.ticks()/bt::time_duration::rep_type::ticks_per_second;
}
void seconds_from_epoch(const std::string& s)
{
bt::ptime pt;
for(size_t i = 0; i < formats_n; ++i)
{
std::istringstream is(s);
is.imbue(formats[i]);
is >> pt;
if(pt != bt::ptime()) break;
}
bt::time_duration td = pt.time_of_day();
long fs = td.fractional_seconds();
std::cout << " ptime is " << pt << '\n';
std::cout << " seconds from epoch are " << pt_to_time_t(pt) << " " << fs << '\n';
}
int main(int, char *argv[])
{
std::string cell ="20091201 00:00:04.437";
seconds_from_epoch(cell);
int enterAnumber;
std::
cin >> enterAnumber;
}

boost::posix_time::time_from_string is very rigid when it comes to parsing formats.
You are looking for a different way to create a boost::posix_time::ptime from an std::string. You want to imbue a stringstream with the format, as such:
const std::string cell = "20091201 00:00:04.437";
const std::locale loc = std::locale(std::locale::classic(), new boost::posix_time::time_input_facet("%Y%m%d %H:%M:%S%f"));
std::istringstream is(cell);
is.imbue(loc);
boost::posix_time::ptime t;
is >> t;
Then
std::cout << t << std::endl;
gives
2009-Dec-01 00:00:04.437000

Related

Advice on converting timestamp string in "HH:MM:SS.microseconds" format

I'm given a list of timestamps (suppose we have a ready-made std::vector<std::string>) in a string format of a kind std::vector<std::string> = {"12:27:37.740002", "19:37:17.314002", "20:00:07.140902",...}. No dates, no timezones. What would be a preferable way to parse these strings to some kind of C++ type (std::chrono::time_point ?) to be able to perform some comparisons and sorting later.
For example: compare value, which was parsed from "20:00:07.140902" and value, was parsed from "20:00:07.000000".
C++17 is ok, but I can't use any third-party library (Boost, Date etc).
Keeping microseconds precision essential.
You can build this functionality completly with C++ standard library functionality.
For parsing the string use std::regex.
For time related datatypes use std::chrono
Example :
#include <stdexcept>
#include <regex>
#include <chrono>
#include <iostream>
auto parse_to_timepoint(const std::string& input)
{
// setup a regular expression to parse the input string
// https://regex101.com/
// each part between () is a group and will end up in the match
// [0-2] will match any character from 0 to 2 etc..
// [0-9]{6} will match exactly 6 digits
static const std::regex rx{ "([0-2][0-9]):([0-5][0-9]):([0-5][0-9])\\.([0-9]{6})" };
std::smatch match;
if (!std::regex_search(input, match, rx))
{
throw std::invalid_argument("input string is not a valid time string");
}
// convert each matched group to the corresponding value
// note match[0] is the complete matched string by the regular expression
// we only need the groups which start at index 1
const auto& hours = std::stoul(match[1]);
const auto& minutes = std::stoul(match[2]);
const auto& seconds = std::stoul(match[3]);
const auto& microseconds = std::stoul(match[4]);
// build up a duration
std::chrono::high_resolution_clock::duration duration{};
duration += std::chrono::hours(hours);
duration += std::chrono::minutes(minutes);
duration += std::chrono::seconds(seconds);
duration += std::chrono::microseconds(microseconds);
// then return a time_point (note this will not help you with correctly handling day boundaries)
// since there is no date in the input string
return std::chrono::high_resolution_clock::time_point{ duration };
}
int main()
{
std::string input1{ "20:00:07.140902" };
std::string input2{ "20:00:07.000000" };
auto tp1 = parse_to_timepoint(input1);
auto tp2 = parse_to_timepoint(input2);
std::cout << "start time = " << ((tp1 < tp2) ? input1 : input2) << "\n";
std::cout << "end time = " << ((tp1 >= tp2) ? input1 : input2) << "\n";
return 0;
}
I don't see why this shouldn't work. Using std::chrono::from_stream to parse the string into a time point, then just compare the two time points.
However, I've been trying it now with Visual Studio 2022 17.0.2 (Community Edition) and it fails to parse the string into a tp.
There is this answer from Ted Lyngmo's talking about a bug (fixed in VS2022 17.0.3) when parsing seconds with subseconds. I have to say though that his solution didn't work for me either in my VS2022.
Anyway, you may want to give it a try.
#include <chrono>
#include <iomanip> // boolalpha
#include <iostream> // cout
#include <sstream> // istringstream
#include <string>
auto parse_string_to_tp(const std::string& str)
{
std::istringstream iss{ str };
std::chrono::sys_time<std::chrono::microseconds> tp{};
std::chrono::from_stream(iss, "%H:%M:%S", tp); // or simply "%T"
return tp;
}
int main()
{
const std::string str1{ "12:27:37.740002" };
const std::string str2{ "13:00:00.500000" };
auto tp1{ parse_string_to_tp(str1) };
auto tp2{ parse_string_to_tp(str2) };
std::cout << "tp1 < tp2: " << std::boolalpha << (tp1 < tp2) << "\n";
std::cout << "tp2 < tp1: " << std::boolalpha << (tp2 < tp1) << "\n";
}
EDIT: it works if you just use durations instead of time points:
#include <chrono>
#include <iomanip> // boolalpha
#include <iostream> // cout
#include <sstream> // istringstream
#include <string>
auto parse_string_to_duration(const std::string& str)
{
std::istringstream iss{ str };
std::chrono::microseconds d{};
std::chrono::from_stream(iss, "%T", d);
return d;
}
int main()
{
const std::string str1{ "12:27:37.740002" };
const std::string str2{ "23:39:48.500000" };
auto d1{ parse_string_to_duration(str1) };
auto d2{ parse_string_to_duration(str2) };
std::cout << "d1 < d2: " << std::boolalpha << (d1 < d2) << "\n";
std::cout << "d2 < d1: " << std::boolalpha << (d2 < d1) << "\n";
}

Unexpected Behaviour in Cereal while Retrieving Object From Ignite Server using Redis Client

Below is my sample code through which i'm trying to benchmark hiredis client with IgniteServer to Store and Retrieve a C++ Class Object after serializing.
#include <cassert>
#include <sstream>
#include <string>
#include <unordered_map>
#include <time.h>
#include <iostream>
#include <iomanip>
#include <vector>
#include <string>
#include <sstream>
#include <chrono>
#include <time.h>
#include <hiredis/hiredis.h>
#include <cereal/archives/binary.hpp>
#include <cereal/types/memory.hpp>
#include <cereal/types/unordered_map.hpp>
using namespace std::chrono;
struct Person
{
friend class cereal::access;
template <typename Archive>
void serialize(Archive& archive) {
// archive(orgId,firstName, lastName,resume,salary,other);
archive & orgId & firstName & lastName & resume & salary & other;
}
Person() {}
Person(int32_t orgId, const std::string& firstName,
const std::string& lastName, const std::string& resume, double sal, const std::string& other) :
orgId(orgId),
firstName(firstName),
lastName(lastName),
resume(resume),
salary(sal),
other(other)
{
// No-op.
}
int64_t orgId;
std::string firstName;
std::string lastName;
std::string resume;
double salary;
std::string other;
};
auto redis_context = redisConnect("127.0.0.1", 6379);
int _set(int32_t key, Person p){
std::ostringstream oss;
cereal::BinaryOutputArchive archive{oss};
archive(p.orgId,p.firstName, p.lastName,p.resume,p.salary,p.other);
const auto set_reply =
redisCommand(redis_context, "SET %ld %b",(long)key,oss.str().c_str(), oss.str().length());
freeReplyObject(set_reply);
}
int _get(int32_t key){
const auto get_reply =
static_cast<redisReply*>(redisCommand(redis_context, "GET %ld",(long)key));
std::string repr{get_reply->str, static_cast<size_t>(get_reply->len)};
if(static_cast<size_t>(get_reply->len) <= 0) {
std::cout << "No key is matching" << std::endl;
return -1;
}
std::istringstream iss{repr};
cereal::BinaryInputArchive input(iss);
Person g;
input(g);
std::cout << g.orgId << " Name: " << g.firstName << " Last: " << g.lastName << "Resume: " << g.resume<< " Salary: " << g.salary << " Other: " << g.other <<std::endl;
freeReplyObject(get_reply);
iss.clear();
// redisFree(redis_context);
}
int _remove(int32_t key){
const auto del_reply =
static_cast<redisReply*>(redisCommand(redis_context, "DEL %ld",(long)key));
freeReplyObject(del_reply);
_get(key);
}
int main(int argc, char **argv) {
int start =1 ;
int range =3;
std::cout << "Starting Redis Testing " << std::endl;
clock_t begin_time = clock();
high_resolution_clock::time_point t1 = high_resolution_clock::now();
**//Person P{10,"John","Cena","Analyst",450000,"Summa"};// Works Fine when object is created in this way**
**Person P;
for (int32_t i = start ; i < range ; ++i)
{
P.orgId = i;
P.firstName = "Gibbi";
P.lastName = "Prakash";
P.resume = "Analyst";
P.salary = 45000;
P.other = "Summa";
_set(i,P);
}
for (int32_t i = start; i < range; ++i)
{
try{
_get(i);
//break;
}catch(cereal::Exception e){
std::cout << "Caught Exception " << std::endl;
}
}
for (int32_t i = start; i < range; ++i)
{
_remove(i);
}
}
When object is created Person P{10,"John","Cena","Analyst",450000,"Summa"}; in this way the program works as expected.
OUTPUT:
Starting Redis Testing
1 Name: John Last: CenaResume: Analyst Salary: 450000 Other: Summa
2 Name: John Last: CenaResume: Analyst Salary: 450000 Other: Summa
No key is matching
No key is matching
when i created object First then assign values
(
Person P; //when object is created first then values are assigned to it.
P.orgId = i;
P.firstName = "Gibbi";
P.lastName = "Prakash";
P.resume = "Analyst";
P.salary = 45000;
P.other = "Summa";
), the output is empty or the client just hangs.
I'm not sure what was happening behind the scenes since i'm very new to using cereal library. I feel the issue is with cereal, but i couldn't figure it.

Fill vector of pairs of doubles using XML string

I have an XML string such as the one below:
< thing TYPE="array" UNITS="meters">1.0,1.3,1.2,1.7,1.4,1.9< /thing>
I'm trying to put each pair of numbers into a std::vector< std::pair< double,double > >. It should look something like this when finished:
< (1.0,1.3), (1.2,1.7), (1.4,1.9) >
I know one way I could do this is search for each comma in the string to find the individual numbers and create a substring, then convert the substring to a double and fill one of the numbers in the pair. However, this seems like an overly complicated way to accomplish this task. Is there a simple way I could do this, maybe by using std::istringstream? Thanks in advance!
What about using getline()?
#include <vector>
#include <sstream>
#include <iostream>
int main()
{
std::vector<std::pair<double, double>> vpss;
std::string str1, str2;
std::istringstream iss("1.0,1.3,1.2,1.7,1.4,1.9");
while ( std::getline(iss, str1, ',') && std::getline(iss, str2, ',') )
{
std::cout << str1 << ", " << str2 << std::endl;
vpss.emplace_back(std::stod(str1), std::stod(str2));
}
return 0;
}
Another solution could be put the comma in a char variable
#include <vector>
#include <sstream>
#include <iostream>
int main()
{
std::vector<std::pair<double, double>> vpss;
char ch;
double d1, d2;
std::istringstream iss("1.0,1.3,1.2,1.7,1.4,1.9");
while ( iss >> d1 >> ch >> d2 )
{
std::cout << d1 << ", " << d2 << std::endl;
vpss.emplace_back(d1, d2);
iss >> ch;
}
return 0;
}
You can use regex to capture the data you want, and std::stringstream to parse it
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <regex>
#include <utility>
using namespace std;
int main()
{
auto xml_str = R"(< thing TYPE="array" UNITS="meters">1.0,1.3,1.2,1.7,1.4,1.9< /thing>)"s;
auto r = regex(R"(>(\d.*\d)<)"s);
auto m = smatch{};
auto vec = vector<pair<double, double>>{};
// Determine if there is a match
auto beg = cbegin(xml_str);
while (regex_search(beg, cend(xml_str), m, r)) {
// Create a string that holds the 1st capture group
auto str = string(m[1].first, m[1].second);
auto ss = stringstream{str};
auto token1 = ""s;
auto token2 = ""s;
auto d1 = double{};
auto d2 = double{};
// Parse
while (getline(ss, token1, ',')) {
getline(ss, token2, ',');
d1 = stod(token1);
d2 = stod(token2);
vec.emplace_back(make_pair(d1, d2));
}
// Set new start position for next search
beg = m[0].second;
}
// Print vector content
auto count = 1;
for (const auto& i : vec) {
if (count == 3) {
cout << "(" << i.first << ", " << i.second << ")\n";
count = 1;
}
else {
cout << "(" << i.first << ", " << i.second << "), ";
count++;
}
}
}
(Compile with -std=c++14)
Output:
(1, 1.3), (1.2, 1.7), (1.4, 1.9)
Live Demo

How to convert a string to json format in c++?

So I'm reading some values from a sensor via SPI. I already converted those values to a string (don't know if I should but I was trying something). Now I can't convert them to json format.
Here is my code:
#include "ad7490Spi.h"
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
using namespace std;
string IntToString (int a2dVal)
{
ostringstream oss;
oss << a2dVal;
return oss.str();
}
int main(void)
{
ad7490Spi a2d("/dev/spidev0.0", SPI_MODE_0, 1000000, 16);
int i = 5;
int a2dVal = 0;
int a2dChannel = 0;
unsigned char data[3];
while(i > 0)
{
data[0] = 1; // first byte transmitted -> start bit
data[1] = 0b1000000000000000 |( ((a2dChannel & 15) << 4)); // second byte transmitted -> (SGL/DIF = 1, D2=D1=D0=0)
data[2] = 0; // third byte transmitted....don't care
a2d.spiWriteRead(data, sizeof(data) );
a2dVal = 0;
a2dVal = (data[1]<< 8) & 0b1100000000; //merge data[1] & data[2] to get result
a2dVal |= (data[2] & 0xff);
sleep(1);
i--;
string result = IntToString (a2dVal);
cout << " " + result + " ";
}
return 0;
}
This is the result:
1023 1023 1023 1023 1023
I want the result to be this way:
{
"values": [ "1023", "1023", "1023", "1023", "1023" ]
}
Can you guys help me with this?
this code:
#include <iostream>
#include <string>
#include <iomanip>
#include <vector>
void print_values(std::ostream& os, const std::vector<int>& v)
{
using namespace std;
os << "{\n";
os << "\t\"values\" : [";
auto sep = " ";
for (const auto& i : v) {
os << sep << quoted(to_string(i));
sep = ", ";
}
os << " ]\n";
os << "}\n";
}
auto main() -> int
{
print_values(std::cout, {1,2,3,4,5,6});
return 0;
}
results in:
{
"values" : [ "1", "2", "3", "4", "5", "6" ]
}
update:
this version will behave correctly with a c++11 compiler (and highlights some 'new' features of c++14 - but let's not live in the stone age too long eh?)
#include <iostream>
#include <string>
#include <iomanip>
#include <vector>
#if __cplusplus >= 201402L
#else
std::string quoted(const std::string& s) {
using namespace std;
return string(1, '"') + s + '"';
}
#endif
void print_values(std::ostream& os, const std::vector<int>& v)
{
using namespace std;
os << "{\n";
os << "\t\"values\" : [";
auto sep = " ";
for (const auto& i : v) {
os << sep << quoted(to_string(i));
sep = ", ";
}
os << " ]\n";
os << "}\n";
}
auto main() -> int
{
using namespace std;
print_values(cout,
#if __cplusplus >= 201402L
{1,2,3,4,5,6}
#else
[]() -> vector<int> {
vector<int> v;
for (int i = 1 ; i < 7 ; ++i )
v.push_back(i);
return v;
}()
#endif
);
return 0;
}
As you appear to be using C++ and the STL in an embedded system, I would recommend you using picojson. It is just a 1-header library and would be better than implementing the serialization yourself with some string manipulation. If you do so, it will get uglier later on if you extend your json output.

Reading from file separated with semicolons and storing into array

I am completely lost and have been trying for hours to read from a file named "movies.txt" and storing the info from it into arrays, because it has semicolons. Any help? Thanks.
movies.txt:
The Avengers ; 2012 ; 89 ; 623357910.79
Guardians of the Galaxy ; 2014 ; 96 ; 333130696.46
Code:
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
struct Movie {
std::string name;
int year;
int rating;
double earnings;
};
int main()
{
const int MAX_SIZE = 100;
Movie movieList[MAX_SIZE];
std::string line;
int i = 0;
std::ifstream movieFile;
movieFile.open("movies.txt");
while (getline(movieFile, line, ';'))
{
movieFile >> movieList[i].name >> movieList[i].year >> movieList[i].rating >> movieList[i].earnings;
i++;
}
movieFile.close();
std::cout << movieList[0].name << " " << movieList[0].year << " " << movieList[0].rating << " " << movieList[0].earnings << std::endl;
std::cout << movieList[1].name << " " << movieList[1].year << " " << movieList[1].rating << " " << movieList[1].earnings << std::endl;
return 0;
}
What I want is to have:
movieList[0].name = "The Avengers";
movieList[0].year = 2012;
movieList[0].rating = 89;
movieList[0].earnings = 623357910.79;
movieList[1].name = "Guardians of the Galaxy";
movieList[1].year = 2014;
movieList[1].rating = 96;
movieList[1].earnings = 333130696.46;
I amended your code.
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <vector>
struct Movie {
std::string name;
int year;
int rating;
double earnings;
};
std::vector<std::string>
split(const std::string &s, char delim = ',')
{
std::vector<std::string> elems;
std::stringstream ss(s);
std::string item;
while (std::getline(ss, item, delim))
{
elems.push_back(item);
}
return elems;
}
int main()
{
std::vector<Movie> movieList;
std::string line;
std::ifstream movieFile;
movieFile.open("movies.txt");
while (getline(movieFile, line))
{
std::vector<std::string> columns = split(line,';');
Movie movie;
movie.name = columns[0];
movie.year = std::stoi(columns[1]);
movie.rating = std::stoi(columns[2]);
movie.earnings = std::stof(columns[3]);
movieList.push_back(movie);
}
movieFile.close();
for (const Movie & m: movieList)
{
std::cout << m.name << " " << m.year << " " << m.rating << " " << m.earnings << std::endl;
}
return 0;
}
Basicly, I added a split function that splits the lines using ';'. Also I use vector to store the movies rather than hard coded array of movies. Much better this way.
P.S. Second version without vectors
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <vector>
struct Movie {
std::string name;
int year;
int rating;
double earnings;
};
void split(const std::string &s, char delim, std::string elems[])
{
std::stringstream ss(s);
std::string item;
int i = 0;
while (std::getline(ss, item, delim))
{
elems[i++] = item;
}
}
int main()
{
//std::vector<Movie> movieList;
const int MAX_SIZE = 100;
Movie movieList[MAX_SIZE];
int movieNo = 0;
std::string line;
std::ifstream movieFile;
movieFile.open("/home/marcin/testing/movies.txt");
std::string columns[4];
while (getline(movieFile, line))
{
split(line,';', columns);
movieList[movieNo].name = columns[0];
movieList[movieNo].year = std::stoi(columns[1]);
movieList[movieNo].rating = std::stoi(columns[2]);
movieList[movieNo].earnings = std::stof(columns[3]);
++movieNo;
}
movieFile.close();
for (int i =0; i < movieNo; ++i) {
std::cout << movieList[i].name
<< " "
<< movieList[i].year
<< " "
<< movieList[i].rating
<< " "
<< movieList[i].earnings
<< std::endl;
}
return 0;
}
Use getline(my_movieFile, movie_name, ';') to get the name of the movie up to the ;.
You'll need to figure out how to remove the trailing whitespace from the name if necessary.. you can search for examples.
Read the rest of the line using getline(movieFile, line)
Use std::replace to replace all ; with a space in line
Put line into a std::stringstream.
Then extract the remaining fields from the stringstream using the >> operators.
Put this in loop do { ... } while (movieFile);
Also, don't hardcode an arbitrary number of movies. Use a std::vector<Movie> and push_back to add new ones.
I think you want to break your line into tokens using something like std::strtok. Check out the reference here. The example given on that page uses a blank as a separator, you would use a semicolon.