(Let's see if i can explain myself) I'm working on a code that reads a text file and follows the instructions written in that file. An example of the text file:
I want to read the text file line by line, each time I find an instruction (that is, the keywords *sum or *subtract) I want to make a summation (in the case of *sum) of all the numbers in each of the following lines, and repeat until the next instruction appear. So the text file above should generate:
Ok, so here is my question, how can I implement a dictionary(I'm guessing a dictionary is the best way to go here) so that when I read the string *sum i can call a function that does the summation of the following lines.
Just a little note, I have simplified the size of the problem in my explanation, the size of the text file may be big (several mb) and the number of different instructions to search for may be in the magnitude of hundreds.
- Read full lines in a loop
- After trimming away whitespace, see if it starts with an asterisk
- if it does, change the current operation (I recommend a single variable that is an enumeration type, or if you want some runtime modularity, use strings and a map from string to function)
- if there is no asterisk, take the string and construct a std::istringstream from it and extract each number until the extraction operation fails (I recommend storing into a vector)
- Then, when you get to another command, apply the operation and print the result
You will want to ignore the first lines that are not commands.
LB, thanks a lot for your guidance. But, can you expand on:
"change the current operation (I recommend a single variable that is an enumeration type, or if you want some runtime modularity, use strings and a map from string to function)"
It is the part where I'm stuck, and I really didn't get what you wrote there.
Let me explain further, I'm trying to create a (simple) Finite Element solver, a typical input file may look like this:
So, for example, i would like to create a Nodes Class, and store there the information under *nodes there. So I was thinking in calling a function to create the class and it's memebers whenever i find the *nodes keyword in my file.
Your actual goal is entirely different from what you proposed in your original post, and as such needs to be solved in a completely different way.
Your format is simple enough that you can write your own parser, though if you want you can use an existing data format, such as JSON (in which you would use an existing library to do the parsing for you - I recommend it).
Either way, you just need to figure out what information is on a line (is it a heading, or something under a heading?) and then from there take the appropriate action. If your class types are known and limited, a while loop that reads full lines at a time and investigates (as I suggested before) would be sufficient:
if(std::ifstream in {"data.txt"})
{
std::string line;
std::string mode;
while(std::getline(in, line))
{
if(line.size() > 1 && line[0] == '*') //is this line a header?
{
mode = line.substr(1); //extract the header name
continue; //no data on this line
}
std::istringstream inline {line};
if(mode == "nodes")
{
int w, x, y, z;
inline >> w >> x >> y >> z;
//assume nodes is a std::vector<Node>
nodes.emplace_back(w, x, y, z); //relying on Node having a 4-param ctor
}
elseif(mode == "material")
{
int a;
double b;
inline >> a >> b;
//assume materials is a std::vector<Material>
materials.emplace_back(a, b); //relying on Material having a 2-param ctor
}
//etc...
}
}
Exactly, you got the idea.
My problem is that I only used *nodes, *material, *elements... as an example.
The scope of my project involves having a lot of these *headers. They are indeed known and limited, but I don't think that lots of elseif woud be the best way to code it. That's why I was asking for some data structure or some way to keep a better organization (i'm thinking in doing lots of changes over the time).
As I mentioned in my original post, I was thinking in a dictionary or something like that...
I'm not a professional programmer, I really don't know how many options are out there to achieve my goal.
Thanks for this second answer, I may use the same outline if there is no other option.
I would suggest a std::map<std::string, std::function<std::unique_ptr<Base> (std::istream &)>> - you map strings to functions that take an input stream and return an object. The input stream would be inline from my example.
Note that I had forgotten that inline is a reserved word when I wrote the example - it can't be used as an identifier.