To format a string representing date received from other part some transform is required:
source:
std::string s = "20190510";
target:
std::string t = "05/10/2019";
One way is to copy char by char, is there an elegant way to do it beautiful and fast?
UPDATE: Sorry the transform should be from "yyyymmdd" to "mm/dd/yyyy".
Try insert:
int main() {
std::string s = "20190510";
s.insert(4, "/");
s.insert(7, "/");
std::cout << s << std::endl;
}
If you don't want to modify the string or copy it, then you are left with the option of formatting it only when needed. This can be accomplished with a small utility:
struct date_format {
std::string const& str;
date_format(std::string const& str) : str(str) {}
friend std::ostream& operator<< (std::ostream& os, date_format const& df) {
return os.write(&df.str[4], 2)
.put('/')
.write(&df.str[6], 2)
.put('/')
.write(&df.str[0], 4);
}
};
To be used like this for instance std::cout << date_format(source);, see it live.
Otherwise it's definitely going to entail copying or moving characters about.
Related
I have a json like :
{
"answer": {
"everything": 42
}
}
Using nlohmann json library in C++, I can access at this nested value like this :
std::cout << my_json["answer"]["everything"];
I'm looking for a way to access it with something like :
std::cout << my_json["answer.everything"];
It won't be possible to implement this with the syntax j["name1.name2"] you used. Many overloads for operators, including the bracket operators () and [] can only be declared inside a class. Furthermore nlohmann::json has already defined the [] operator to work like j["name1"]["name2"]! This means you would have to modify the library in order to achieve this precise syntax. But it seems you are only trying to have an arbitrarily nested Json and be still able to browse through it with a simple input string without necessarily sticking to the square bracket operator.
A simple solution is writing a function get(nlohmann::json, std::string) that takes an nlohmann::json as well as the corresponding dot-separeted names of the form name1.name2.name3 etc. and returns the corresponding value. This can be achieved by splitting the string according to the delimiter and then calling the nlohmann::json[] operator to browse the Json string:
std::vector<std::string> split(std::string const& str, char const delim) noexcept {
std::vector<std::string> res = {};
std::size_t start {0};
std::size_t end {0};
while ((start = str.find_first_not_of(delim, end)) != std::string::npos) {
end = str.find(delim, start);
res.push_back(str.substr(start, end - start));
}
return res;
}
nlohmann::json get(nlohmann::json const& root, std::string const& dot_separated_names) {
std::vector<std::string> const names = split(dot_separated_names, '.');
nlohmann::json const* leaf = &root;
for (auto const& name : names) {
if (leaf->contains(name)) {
leaf = &leaf->at(name);
} else {
// Error handling (e.g. throw error)
std::cerr << "Name '" << name << "' not found!" << std::endl;
}
}
return *leaf;
}
With the functions above you can achieve the desired output with get(j, "name1.name2");.
If you want to be able to set values as well you can similarly write a function set
nlohmann::json& set(nlohmann::json& root, std::string const& dot_separated_names) {
std::vector<std::string> const names = split(dot_separated_names, '.');
nlohmann::json* leaf = &root;
for (auto const& name : names) {
if (leaf->contains(name)) {
leaf = &leaf->at(name);
} else {
// Error handling (e.g. throw error)
std::cerr << "Name '" << name << "' not found!" << std::endl;
}
}
return *leaf;
}
that can be called as set(j, "name1.name2") = "value";. It might be unintuitive to set a value like this so you might want to modify it to something like template <typename T> set(nlohmann::json& root, std::string const& dot_separated_names, T&& value).
Probably it is even better to write this function for std::vector<std::string> instead of std::string and move the splitting of the string to a different routine. This way you can switch delimiter easily and can also browse through a Json just given a vector of names.
If you really wanted to use it as j["name1.name2"] you would have to write your own wrapper class for a nlohmann::json that defines the bracket operator in this way. But in this case you lose a lot of functionality of the wrapped nlohmann::json that you would have to supply by yourself, e.g. the other operators such as << etc.
class YourJsonWrapper {
public:
YourJsonWrapper(nlohmann::json const& j) noexcept
: j{j} {
return;
}
// Your custom bracket operator
nlohmann::json const& operator [] (std::string const& dot_separated_names) const {
// Get implementation (like above)
// ...
}
nlohmann::json& operator [] (std::string const& dot_separated_names) {
// Set implementation (like above)
// ...
}
// Add the other operators you need and forward them to the underlying nlohmann::json
nlohmann::json j; ///< The wrapped nlohmann::json
};
Furthermore - as pointed out in the comments - using dot-separated strings could be problematic as a Json name itself might contain a dot as well. Therefore make sure your Json names can't contain dots by design.
I have three different text files called a.txt, b.txt and c.txt to be taken from the command line. I have also three classes called class A, class B and class C each of which corresponds to each of those files. For instance, "a.txt" contains:
12 title1
15 title2
20 title3
where the first column is the "id" and the second one is the title(in each row there is a space between the id and the title). Corresponds to this file is Class A which its header file is like:
class A {
public:
A();
// the id doesn't need to be int
A(const string &id, const string &title);
void setId(const string &id);
void setTitle(const string &title);
private:
string id_;
string title_;
};
The program should take the arguments (which are the files) from command line, read the contents of each file and extracts its content to assign the variables of the corresponding class. Following the above example, the program through setID should assign "12" to a1.id_ and through setTitle assign "title1" to a1.title_ and the same for the other two instances. Is it possible to do the all mentioned jobs with all of files and the classes through just one efficient loop? In either case (if possible or not possible) what is the suggested solution?
Edit
Creating so many differently-named variables for each class is not cost effective at all
We will store all the data in a container. And for this task, we need a dynamic container, which can grow. A std::vector is the way to go.
To make this code efficient, we will overwrite the inserter and extractor operator of your class A. The inserter will simply insert the ID and the Title into the output stream. Easy to understand.
The extractor is slightly more complicated, but also easy to understand. We first extract the id, with a standard ">>" operation. Then we read the rest of the line with std::getline because the title may contain blanks. That would be a delimiter for the ">>" operator. Please note, the std::getline will read leading/trailing blanks. Maybe you need to "trim" your Title-string.
In main, we simply loop through the command line arguments and open the file for each argument. If it could be opened, the we use std::copy to copy all lines of the file, so the complete content, into our std::vector of type A.
The std::istream_terator will call the overwritten extractor operator of our A-class, line for line, until the file is at the end. The std::back_inserter will "push_back" the read A instances into the vector.
At the end of main, we show the complete content of the A-vector.
Please see:
#include <iostream>
#include <fstream>
#include <string>
#include <iomanip>
#include <vector>
#include <iterator>
#include <algorithm>
class A {
public:
// Constructors
A() : id_(), title_() {}
A(const std::string& id, const std::string& title) : id_(id), title_(title) {}
// Setters
void setId(const std::string& id) { id_ = id; }
void setTitle(const std::string& title) { title_ = title; }
// Overwrite extractor for easier reading
friend std::istream& operator >> (std::istream& is, A& a) {
if (std::string id{}, title{}; is >> id && getline(is, title)) {
a.id_ = id; a.title_ = title;
}
return is;
}
// Overwrite inserter for easier writing
friend std::ostream& operator << (std::ostream& os, const A& a) {
return os << "Id: " << std::setw(5) << a.id_ << " Title: " << a.title_ << "\n";
}
private:
std::string id_{};
std::string title_{};
};
int main(int argc, char* argv[]) {
// Here we will store all read data
std::vector<A> data{};
// Work on all command line arguments
for (size_t fileNumber = 1; fileNumber < argc; ++fileNumber) {
// Open the corresponding file and check, if it is open
if (std::ifstream ifs(argv[fileNumber]); ifs) {
// Copy all data from this file into the class and the vector
std::copy(std::istream_iterator<A>(ifs), {}, std::back_inserter(data));
}
}
// Show the result on the screen
std::copy(data.begin(), data.end(), std::ostream_iterator<A>(std::cout));
return 0;
}
I'm trying to write a stream manipulator with arguments.
I have class with 3 int's CDate(Year, Month, Day).
So I need to make manipulator date_format(const char*).
e.g. :
CDate a(2006, 5, 15);
cout <<"DATE IS : " << date_format("%Y-hello-%d-world-%m-something-%d%d") << a;
Output will be :
DATE IS : 2006-hello-15-world-5-something-1515
Guess i need to use that
ios_base & dummy_date_format_manipulator ( ios_base & x )
{
return x;
}
ios_base & ( * ( date_format ( const char * fmt ) ) )( ios_base & x )
{
return dummy_date_format_manipulator;
}
but i don't know how.
You can use pword array for this.
Every iostream in C++ has two arrays associated with it.
ios_base::iword - array of ints
ios_base::pword - array of void* pointers
You can store you own data in it. To obtain an index, that refers to an empty element in all iword and pword arrays you should use function std::ios_base::xalloc(). It returns int that you can use as an unique index in *word.
You should obtain that index once on the start-up, and than use it for all operations with *word.
Then programming your own manip will look like:
Manipulator function, that receives reference to ios_base object and pointer to the format string, simply stores that pointer in pword
iosObject.pword(index_from_xalloc) = formatString
Then overloaded operator << (>>) obtains format string from the iostream object in the same way. After that you just make a conversion referencing to the format.
Manipulators with arguments don't work the same as those without arguments! The are just classes with a suitable output operator which instead of outputting a value manipulate the stream's state. To manipulate the stream state you'll probably set up a suitabe value stored with an iword() or a pword() associated with the dtream and used by the output operator.
As chris suggested, I'd say that you should just use tm rather than your custom date class:
tm a{0, 0, 0, 15, 5, 2006 - 1900};
cout << put_time(&a, "%Y-hello-%d-world-%m-something-%d%d");
If you must implement come custom functionality that cannot be accomplished with get_time and put_time then you'd probably want to use a tm member as part of your class so you could just extend the functionality that is already there:
class CDate{
tm m_date;
public:
CDate(int year, int month, int day): m_date{0, 0, 0, day, month, year - 1900}{}
const tm& getDate() const{return m_date;}
};
ostream& operator<<(ostream& lhs, const CDate& rhs){
auto date = rhs.getDate();
return lhs << put_time(&a, "%Y-hello-%d-world-%m-something-%d%d");
}
You could then use CDate as follows:
CDate a(2006, 5, 15);
cout << "DATE IS:" << a;
EDIT:
After looking at your question again, I think that you have a misconception about how the insertion operator works, you cannot pass in both an object and a format: https://msdn.microsoft.com/en-us/library/1z2f6c2k.aspx
If you want to specify a format but still retain your CDate class, I'd again suggest the use of put_time:
cout << put_time(&a.getDate(), "%Y-hello-%d-world-%m-something-%d%d");
If you again insist on writing your own format accepting function you'll need to create a helper class that can be constructed inline and support that with the insertion operator:
class put_CDate{
const CDate* m_pCDate;
const char* m_szFormat;
public:
put_CDate(const CDate* pCDate, const char* szFormat) : m_pCDate(pCDate), m_szFormat(szFormat) {}
const CDate* getPCDate() const { return m_pCDate; }
const char* getSZFormat() const { return m_szFormat; }
};
ostream& operator<<(ostream& lhs, const put_CDate& rhs){
return lhs << put_time(&rhs.getPCDate()->getDate(), rhs.getSZFormat());
}
You could use this as follows:
cout << put_CDate(&a, "%Y-hello-%d-world-%m-something-%d%d") << endl;
Like Dietmar said you can push the params into the iword() but i find this kind of solution to be tedious and annoying..
I prefer to just install lambda functions as a iomanips and use them to directly call into the various classes methods or otherwise build the custom print in place. For that purpose I have created a simple 100 line template installer/helper class for mainpulators which can add a lambda function as the manipulator of any class..
So for your CDate you might define you manip as
std::ostream& dummy_date_format_manipulator (std::ostream& os)
{
CustomManip<CDate>::install(os,
[](std::ostream& oos, const CDate& a)
{
os << a.year()
<< "-hello-"
<< a.day()
<< "-world-"
<< a.month()
<< "-something-"
<< a.day() << a.day();
});
return os;
}
Then just direct the << op to use the manip installers print helper:
std::ostream& operator<<(std::ostream& os, const CDate& a)
{
CustomManip<CDate>::print(os, a);
return os;
}
And your basically done..
The mainp installer code and a fully working example is in my blog post at:
http://code-slim-jim.blogspot.jp/2015/04/creating-iomanip-for-class-easy-way.html
But to be nice.. here is the key part you want to put in a .h somewhere less all the printouts to demonstrate how it works:
//g++ -g --std=c++11 custom_class_manip.cpp
#include <iostream>
#include <ios>
#include <sstream>
#include <functional>
template <typename TYPE>
class CustomManip
{
private:
typedef std::function<void(std::ostream&, const TYPE&)> ManipFunc;
struct CustomManipHandle
{
ManipFunc func_;
};
static int handleIndex()
{
// the id for this Custommaniputors params
// in the os_base parameter maps
static int index = std::ios_base::xalloc();
return index;
}
public:
static void install(std::ostream& os, ManipFunc func)
{
CustomManipHandle* handle =
static_cast<CustomManipHandle*>(os.pword(handleIndex()));
// check if its installed on this ostream
if (handle == NULL)
{
// install it
handle = new CustomManipHandle();
os.pword(handleIndex()) = handle;
// install the callback so we can destroy it
os.register_callback (CustomManip<TYPE>::streamEvent,0);
}
handle->func_ = func;
}
static void uninstall(std::ios_base& os)
{
CustomManipHandle* handle =
static_cast<CustomManipHandle*>(os.pword(handleIndex()));
//delete the installed Custommanipulator handle
if (handle != NULL)
{
os.pword(handleIndex()) = NULL;
delete handle;
}
}
static void streamEvent (std::ios::event ev,
std::ios_base& os,
int id)
{
switch (ev)
{
case os.erase_event:
uninstall(os);
break;
case os.copyfmt_event:
case os.imbue_event:
break;
}
}
static void print(std::ostream& os, const TYPE& data)
{
CustomManipHandle* handle
= static_cast<CustomManipHandle*>(os.pword(handleIndex()));
if (handle != NULL)
{
handle->func_(os, data);
return;
}
data.printDefault(os);
}
};
Of course if you really do need the parameters then use the CustomManip::make_installer(...) function to get it done but for that you will have to visit the blog..
I'm using ANSI color codes to format my output in an Unix console.
const auto& getCode(Color mColor)
{
static std::map<Color, std::string> codes;
// ...
return codes[mColor]
}
cout << getCode(Color::Red) << "red text";
When using manipulators such as std::setw or std::left, however, the results are affected by the color code, as it is a bunch of characters.
How should I deal with this issue? Is there a way to make stream manipulators ignore color codes?
What is the type returned by getCode? If it isn't
std::string or char const*, all you need to do is write
a << for it which ignores the formatting data you don't want
to affect it. If it's one of C++'s string types, then you
should probably wrap the call in a special object, with an <<
for that object type, e.g.:
class ColorCode
{
ColorType myColor;
public:
ColorCode(ColorType color) : myColor( color ) {}
friend std::ostream& operator<<( std::ostream& dest, ColorCode const& cc )
{
std::string escapeSequence = getCode( myColor );
for ( char ch : escapeSequence ) {
dest.put( ch );
}
return dest;
}
};
Greetings!
I'd like to make small program what reverses part of a stream between markers using stream effectors and/or manipulators For example:
From this:
cout << "something" << revstream::start << "asdf" << 3.14 << revstream::end << "something";
To this:
something41.3fdsasomething
I'd like it to work not just on the standard cout and I'd like to embed them several times.
I'm new in c++ and my main problems are:
- I can't create a new stream to store what is inside the markers
- How to reverse the temp stream?
I tried so many things and I stuck here:
class revstream {
public:
static ostream& start(ostream &os) {
//do the reversing
return ???;
}
static ostream& end(ostream &os) {
return reversedstream;
}
};
You can do this, but it's ugly as butt:
class revstream: public std::ostream
{
public:
static std::ostream &start(std::ostream &os)
{
return *(new revstream(os));
}
static std::ostream &end(std::ostream &rev)
{
revstream *actual_rev= dynamic_cast<revstream *>(&rev);
// logic for reversing output goes here
std::ostream &result= actual_rev->os;
delete actual_rev;
return result;
}
revstream(std::ostream &in_os): std::ostream(&reversebuf), os(in_os)
{
return;
}
std::ostream &os;
std::stringbuf reversebuf;
};
Your ostream manipulator has to return a reference to ostream, so you have to allocate within revstream::start.
This isn't how you asked to go about it, but I would approach the problem in a different way. Instead of writing some kind of extension to the streams, why not just write a function that reverses a string?
#include <string>
#include <iostream>
using namespace std;
string reverse(const string& str)
{
return string(str.rbegin(), str.rend());
}
int main()
{
cout << reverse("asdf") << "\n";
}