DilithiumMatrix
DilithiumMatrix

Reputation: 18627

'Good' programming form in maintaining / updating / accessing files by entry

Basic Question:

If I'm storying/modifying data, should I access elements of a file by index hard-coded index, i.e. targetFile.getElement(5); via a hardcoded identifier (internally translated into index), i.e. target.getElementWithID("Desired Element"), or with some intermediate DESIRED_ELEMENT = 5; ... target.getElement(DESIRED_ELEMENT), etc.

Edit: Or something using an enum ??


Background:

My program (c++) stores data in lots of different 'dataFile's. I also keep a list of all of the data-files in another file---a 'listFile'---which also stores some of each one's properties (see below, but i.e. what it's name is, how many lines of information it has etc.). There is an object which manages the data files and the list file, call it a 'fileKeeper'.

The entries of a listFile look something like:

filename , contents name , number of lines , some more numbers ...

Its definitely possible that I may add / remove fields from this list --- but in general, they'll stay static.

Right now, I have a constant string array which holds the identification of each element in each entry, something like:

const string fileKeeper::idKeys[] = { "FileName" , "Contents" , "NumLines" ... };
const int fileKeeper::idKeysNum = 6;   // 6 - for example

I'm trying to manage this stuff in 'good' programatic form. Thus, when I want to retrieve the number of lines in a file (for example), instead of having a method which just retrieves the '3'rd element... Instead I do something like:

string desiredID = "NumLines";
int desiredIndex = indexForID(desiredID);
string desiredElement = elementForIndex(desiredIndex);

where the function indexForID() goes through the entries of idKeys until it finds desiredID then returns the index it corresponds to. And elementForIndex(index) actually goes into the listFile to retrieve the index'th element of the comma-delimited string.


Problem:

This still seems pretty ugly / poor-form. Is there a way I should be doing this? If not, what are some general ways in which this is usually done?

Thanks!

Upvotes: 1

Views: 226

Answers (4)

DilithiumMatrix
DilithiumMatrix

Reputation: 18627

My solution is using a combination of a std::map, and an enum:

class FileKeeper {
    public:
        enum ID_KEY = { FileName , Contents , "Num_Lines };
        string getIDKey(ID_KEY idk);

    private:
        static map<int,string> ID_Key = { { FileName, "Filename" }, { Contents, "Contents" }, { Num_Lines, "Num Lines" } };

}

string FileKeeper::getIDKey(ID_KEY idk) {
    if( idk > 0 && idk < ID_Key.size() ) { return ID_Key[idk]; }
    else { return ""; }
}

This way I can easily access the indices and iterate through the keys easily (in the proper order --- which a map<string,int> wouldn't do) using the enum, and still retrieve the mapping from index to string using the map. Using a get() method prevents adding elements to the map, and keeping the actual map private keeps me from accessing it lazily using integers (even though it is technically of int-index type).

No where in the code do I need to access elements using the actual string idenitified, i.e. get___("Num Lines"); --- which is kind of ugly, and seems to be a faux pas.

Upvotes: 0

Lie Ryan
Lie Ryan

Reputation: 64847

Neither, use a database or at the very least csv. Sqlite is a lightweight, serverless database and bindings are available in almost any languages. It is designed precisely for this kind of scenario, where you need to store an amount of data but don't want to install and configure a database server. You may also want to create a struct/class to hold the data, perhaps with the aid of some sort of ORM.

If you're using csv and you want the ability to be able to rearrange the column order, save the column names as the first entry of the file, most csv library should be able to use the first column as column name.

Upvotes: 1

Israel Unterman
Israel Unterman

Reputation: 13510

I like the way of using strings as keys. You can map a string to a number using std::map<string, int>. You can then do

std::map<string, int> columns;
columns["FileName"] = 0;    
columns["Contents"] = 1;
columns["NumLines"] = 2;

You can then refer to columns["Contents"], for example, to get the column id. Best is to do it via a function, that will inform you if you mispelled a key. Some code like this:

std::map<string, int>::iterator it = columns.find(key);
if (it == columns.end()) { // key not in columns
    // report error here

Upvotes: 1

Karthik T
Karthik T

Reputation: 31952

Make a FileProperty class for a start, listFile would be a list of objects of this class, and your "Keys" would be member variables of this class.

Upvotes: 0

Related Questions