473,396 Members | 1,998 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,396 software developers and data experts.

design question (converting class data to csv)

I have a class that I want to turn its contents into csv file. I want
to be able to set the value of the delimiter, the name of the file it
gets saved to, the path of that file, and maybe a few other things.
What would be a good design to accomplish these goals? Here are the
ideas that I have come up with:

Class X = class with data that I want to put into csv.

1. Nested Function Object
use a nested function object to do the conversion.
+ X does not get polluted with functions that really have no place.
e.g. setDelimeter()
+ behaves the way you would expect. e.g. X.toCsv() to convert X into a
csv file
- Setting state may not be clear to those not familar with function
objects e.g. X.toCsv.setDelimeter() Some may ask why a function is
being used like an object.

2. Create a class ConvertXToCsv that gets passed X to do the
conversion.
+ very straight forward and gets the job done.
- proliferation of classes.

Any suggestions?
-Thanks,

Jake

Oct 16 '06 #1
4 2020
<al******@gmail.comschrieb im Newsbeitrag
news:11**********************@k70g2000cwa.googlegr oups.com...
>I have a class that I want to turn its contents into csv file. I want
to be able to set the value of the delimiter, the name of the file it
gets saved to, the path of that file, and maybe a few other things.
What would be a good design to accomplish these goals? Here are the
ideas that I have come up with:

Class X = class with data that I want to put into csv.

1. Nested Function Object
use a nested function object to do the conversion.
+ X does not get polluted with functions that really have no place.
e.g. setDelimeter()
+ behaves the way you would expect. e.g. X.toCsv() to convert X into a
csv file
- Setting state may not be clear to those not familar with function
objects e.g. X.toCsv.setDelimeter() Some may ask why a function is
being used like an object.

2. Create a class ConvertXToCsv that gets passed X to do the
conversion.
+ very straight forward and gets the job done.
- proliferation of classes.
Writing data to a file (or whatever else) can be split into two tasks -
providing the data to be written and formating (and writing) data
"somewhere".

The provider of your data usually doesn't care (or shouldn't) where its data
will be written to. To write data to a CSV file or some other record
oriented medium, a set of functions like OpenRecord, CloseRecord, WriteInt,
WriteString etc. are all your data source needs to write its data. Such
functions can easyly be defined by an (abstract) interface class.

The consumer of such data usually shouldn't know where its input comes from.
But it might need additional information like delimiters or filenames, which
cannot be supplied by the data source itself. Also, such additional
information depends on the type of medium where the (formated) data should
be written to. That information must be supplied by that part of a program,
that actually wants data to be written to a well-known medium.

Your second idea is a step into that direction, but the fixed connection of
a given class X and a single medium (CSV files) makes it difficult to write
data from another class, or to another medium. You would end with x*y
ConvertXToY classes, where x is the number of data classes to write and y
the number of supported media. With an abstract writer class used by all
data classes and implementations for different media, otoh, you only need y
classes and one additional function in each data class.

The C++ stream library is a small step in this direction, but it only
abstracts the representation of data streams (as a file, a string or a
console device).

Consider this small (incomplete and buggy) example:

class Writer
{
public:
virtual void OpenRecord() = 0;
virtual void CloseRecord() = 0;
virtual void Write(int) = 0;
virtual void Write(std::string const&) = 0;
};

class CsvWriter: public Writer
{
// Implementation of Writer class
void OpenRecord()
{
first = true;
}
void CloseRecord()
{
file << std::endl;
}
void Write(int x)
{
NextField();
file << x;
}
void Write(std::string const& x)
{
NextField();
file << '"' << x << '"';
}
public:
CsvWriter(char const* fn, char d)
: file(fn)
, delimiter(d)
{}
private:
void NextFiled()
{
if (!first) file << delimiter;
first = false;
}
private:
std::ofstream file;
char delimiter;
bool first;
};
class MyData
{
public:
...
void Write(Writer& writer)
{
writer.OpenRecord();
writer.Write(name);
writer.Write(age);
writer.CloseRecord();
}
private:
std::string name;
int age;
};
int main()
{
MyData x;
MyData y;
...
CsvWriter datafile("datafile.csv", ';');
x.Write(datafile);
y.Write(datafile);
}

Regards
Heinz
Oct 16 '06 #2

al******@gmail.com wrote:
I have a class that I want to turn its contents into csv file. I want
to be able to set the value of the delimiter, the name of the file it
gets saved to, the path of that file, and maybe a few other things.
What would be a good design to accomplish these goals? Here are the
ideas that I have come up with:

Class X = class with data that I want to put into csv.

1. Nested Function Object
use a nested function object to do the conversion.
+ X does not get polluted with functions that really have no place.
e.g. setDelimeter()
+ behaves the way you would expect. e.g. X.toCsv() to convert X into a
csv file
- Setting state may not be clear to those not familar with function
objects e.g. X.toCsv.setDelimeter() Some may ask why a function is
being used like an object.

2. Create a class ConvertXToCsv that gets passed X to do the
conversion.
+ very straight forward and gets the job done.
- proliferation of classes.

Any suggestions?
-Thanks,

Jake
First item, if you have a class that needs to stream its members,
overload the global stream operator and make it a friend of the class.
That way you might one day come along and write a container class to
store all your X instances and you'll be able to stream the entire
container seamlessly using another overload of global op<<.

As far as conversion to csv and delimeters, all you are doing is
opening a std::ofstream to some specific file somewhere. Why not write
a container that will:
1. store your records into a well-known and common container (list,
vector, deque)
2. stream records around using std::ostreams for compatibility and
future expandeability
3. provide the container with a function to serialize the records to
file

I'ld suggest a new line at the end of each record to help later when
using getline(...) to read that file and load the container from file
records. A solution with exception handling would have been preferable
and error checking is kept to a minimum.
Since you have not shown your precious class, i've made one up called
X.
#include <iostream>
#include <ostream>
#include <string>
#include <vector>
#include <fstream>
#include <iterator// for std::ostream_iterator

class X
{
int n;
double d;
std::string s;
public:
X() : n(0), d(0.0), s() { }
X(int n_, double d_, std::string s_)
: n(n_), d(d_), s(s_) { }
/* member functions */
void serialize( std::ostream& ) const;
/* friends */
friend std::ostream& operator<<( std::ostream&, const X& );
};

void X::serialize(std::ostream& os) const
{
os << n << '$' << d << '$' << s << std::endl;
}

std::ostream& operator<<(std::ostream& os, const X& r_x)
{
os << "n = " << r_x.n;
os << "\nd = " << r_x.d;
os << "\ns = " << r_x.s << std::endl;
return os;
}

class XContainer
{
std::vector< X vx;
public:
XContainer() : vx() { }
void push_back( const X& r_x ) { vx.push_back( r_x ); }
size_t size() const { return vx.size(); }
/* member functions */
void serialize( std::ostream& ) const;
bool write( const std::string& ) const;
/* friends */
friend
std::ostream& operator<<( std::ostream&, const XContainer& );
};

void XContainer::serialize(std::ostream& os) const
{
typedef std::vector< X >::const_iterator VIter;
for (VIter xiter = vx.begin(); xiter != vx.end(); ++xiter)
{
(*xiter).serialize(os);
}
}

bool XContainer::write(const std::string& r_filename) const
{
std::ofstream ofs(r_filename.c_str());
if ( !ofs.is_open() )
{
return true;
} else {
serialize(ofs);
}
return false;
}

std::ostream&
operator<<(std::ostream& os, const XContainer& r_con)
{
std::copy( r_con.vx.begin(),
r_con.vx.end(),
std::ostream_iterator< X >(os) );
return os;
}

int main()
{
XContainer xcontainer;
xcontainer.push_back( X(0, 0.0, "string 0") );
xcontainer.push_back( X(1, 1.1, "string 1") );
xcontainer.push_back( X(2, 2.2, "string 2") );
std::cout << xcontainer;

// show what file contents will look like on console
std::cout << "\nnumber of records = " << xcontainer.size();
std::cout << std::endl;
xcontainer.serialize( std::cout );

// write the records to file - write protect file to test
const std::string sfilename( "data.cvs" );
if ( xcontainer.write( sfilename ) )
{
std::cout << "\nerror: failed to open " << sfilename;
std::cout << " !!!\n";
} else {
std::cout << "\nsuccess: records written to " << sfilename;
std::cout << std::endl;
}

return 0;
}

/*
n = 0
d = 0
s = string 0
n = 1
d = 1.1
s = string 1
n = 2
d = 2.2
s = string 2

number of records = 3
0$0$string 0
1$1.1$string 1
2$2.2$string 2

success: records written to data.cvs
*/

Oct 16 '06 #3
On Mon, 16 Oct 2006 09:23:08 +0200, "Heinz Ozwirk" wrote:
>class MyData
{
public:
...
void Write(Writer& writer)
{
writer.OpenRecord();
writer.Write(name);
writer.Write(age);
writer.CloseRecord();
}
private:
std::string name;
int age;
};
If you made Write() a non-member function you would completely
decouple class MyData from formatting and writing.

Best wishes,
Roland Pibinger

Oct 16 '06 #4

Salt_Peter wrote:
al******@gmail.com wrote:
I have a class that I want to turn its contents into csv file. I want
to be able to set the value of the delimiter, the name of the file it
gets saved to, the path of that file, and maybe a few other things.
What would be a good design to accomplish these goals? Here are the
ideas that I have come up with:

Class X = class with data that I want to put into csv.

1. Nested Function Object
use a nested function object to do the conversion.
+ X does not get polluted with functions that really have no place.
e.g. setDelimeter()
+ behaves the way you would expect. e.g. X.toCsv() to convert X into a
csv file
- Setting state may not be clear to those not familar with function
objects e.g. X.toCsv.setDelimeter() Some may ask why a function is
being used like an object.

2. Create a class ConvertXToCsv that gets passed X to do the
conversion.
+ very straight forward and gets the job done.
- proliferation of classes.

Any suggestions?
-Thanks,

Jake

First item, if you have a class that needs to stream its members,
overload the global stream operator and make it a friend of the class.
That way you might one day come along and write a container class to
store all your X instances and you'll be able to stream the entire
container seamlessly using another overload of global op<<.

As far as conversion to csv and delimeters, all you are doing is
opening a std::ofstream to some specific file somewhere. Why not write
a container that will:
1. store your records into a well-known and common container (list,
vector, deque)
2. stream records around using std::ostreams for compatibility and
future expandeability
3. provide the container with a function to serialize the records to
file

I'ld suggest a new line at the end of each record to help later when
using getline(...) to read that file and load the container from file
records. A solution with exception handling would have been preferable
and error checking is kept to a minimum.
Since you have not shown your precious class, i've made one up called
X.
#include <iostream>
#include <ostream>
#include <string>
#include <vector>
#include <fstream>
#include <iterator// for std::ostream_iterator

class X
{
int n;
double d;
std::string s;
public:
X() : n(0), d(0.0), s() { }
X(int n_, double d_, std::string s_)
: n(n_), d(d_), s(s_) { }
/* member functions */
void serialize( std::ostream& ) const;
/* friends */
friend std::ostream& operator<<( std::ostream&, const X& );
};

void X::serialize(std::ostream& os) const
{
os << n << '$' << d << '$' << s << std::endl;
}

std::ostream& operator<<(std::ostream& os, const X& r_x)
{
os << "n = " << r_x.n;
os << "\nd = " << r_x.d;
os << "\ns = " << r_x.s << std::endl;
return os;
}

class XContainer
{
std::vector< X vx;
public:
XContainer() : vx() { }
void push_back( const X& r_x ) { vx.push_back( r_x ); }
size_t size() const { return vx.size(); }
/* member functions */
void serialize( std::ostream& ) const;
bool write( const std::string& ) const;
/* friends */
friend
std::ostream& operator<<( std::ostream&, const XContainer& );
};

void XContainer::serialize(std::ostream& os) const
{
typedef std::vector< X >::const_iterator VIter;
for (VIter xiter = vx.begin(); xiter != vx.end(); ++xiter)
{
(*xiter).serialize(os);
}
}

bool XContainer::write(const std::string& r_filename) const
{
std::ofstream ofs(r_filename.c_str());
if ( !ofs.is_open() )
{
return true;
} else {
serialize(ofs);
}
return false;
}

std::ostream&
operator<<(std::ostream& os, const XContainer& r_con)
{
std::copy( r_con.vx.begin(),
r_con.vx.end(),
std::ostream_iterator< X >(os) );
return os;
}

int main()
{
XContainer xcontainer;
xcontainer.push_back( X(0, 0.0, "string 0") );
xcontainer.push_back( X(1, 1.1, "string 1") );
xcontainer.push_back( X(2, 2.2, "string 2") );
std::cout << xcontainer;

// show what file contents will look like on console
std::cout << "\nnumber of records = " << xcontainer.size();
std::cout << std::endl;
xcontainer.serialize( std::cout );

// write the records to file - write protect file to test
const std::string sfilename( "data.cvs" );
if ( xcontainer.write( sfilename ) )
{
std::cout << "\nerror: failed to open " << sfilename;
std::cout << " !!!\n";
} else {
std::cout << "\nsuccess: records written to " << sfilename;
std::cout << std::endl;
}

return 0;
}

/*
n = 0
d = 0
s = string 0
n = 1
d = 1.1
s = string 1
n = 2
d = 2.2
s = string 2

number of records = 3
0$0$string 0
1$1.1$string 1
2$2.2$string 2

success: records written to data.cvs
*/
Thank you for the time put into your suggestions. I mean it.

There seems to be two different approaches to the design. Either tell
the class to serialize itself or Have a class whose purpose it is to
serialize classes. What are the design trade offs to each? From what I
understand of OOP (and I am some what of a newb so please if you can
help me understand) the idea is that the class knows about its
internals and it should be the only object that does. So telling the
class to serialize its self makes more sense to me be cause the class
knows what members it has and you telling that class to run operations
on it own data which is part of the reason for classes.

I am interested first if my assessment of the serialize method is
correct. Also, I am interested in knowing more about the benefits of
having a class that does the serialization as in Heinz's design.

Thanks

Oct 16 '06 #5

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

9
by: chris | last post by:
I've been scripting with python for a while now. Basically writing a few functions and running in the ipython shell. That's been very useful. But the more I do this the more I see that I'm doing...
0
by: KS | last post by:
Hi, We are trying to do mail merge (Word 2000) from a SQL2000 server in a network environment. We has built a Web base Visual.Net project (C#) to query the SQL2000 database and format into a...
9
by: Brian Henry | last post by:
If i inherite a queue class into my class, and do an override of the enqueue member, how would i then go about actually doing an enqueue of an item? I am a little confused on this one... does over...
1
by: Johanna Pfalz | last post by:
Is there a module/method in python to convert a file from .DBF format to ..CSV format? Johanna Pfalz
8
by: Michael B. Trausch | last post by:
I was wondering if anyone has had any experience with this. Someone I know is trying to move away from Microsoft Works, and I am trying to look into a solution that would convert their data in a...
1
by: alacrite | last post by:
I have a class that represents a record in a database table. class tableName { int col1; string col2; int col3; ... other fields and relevant operations }
21
by: py_genetic | last post by:
Hello, I'm importing large text files of data using csv. I would like to add some more auto sensing abilities. I'm considing sampling the data file and doing some fuzzy logic scoring on the...
8
by: =?Utf-8?B?eWRibg==?= | last post by:
I need to write a program validate a text file in CSV format. So I will have a class DataType and a lot of of derived class for various type, e.g. IntType, StringType, FloatType, MoneyType,...
22
by: Chuck Connors | last post by:
Hey guys. I'm working on a little program to help my wife catalog her/ our coupons. I found a good resource but need help formatting the text data so that I can import it into a mysql database....
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.