Map enum key and value using function pointer in C++ - c++

i am very new to C++ programming. I am doing my master and working on one problem given by professor. the problem is regarding performing the basic operations on binary search tree. i have one file which has below format :
I 1015291402
I 729831403
I 1005116371
F 757970570
D 1005116371
F 729831403
I 1218751282
D 1015291402
I 582339464
I 92421221
There are three basic operations Insert, Delete and Find can be done on search tree. so i need to read this file perform operation line by line. Below is the code i wrote so far.
string line;
ifstream infilesmall("inputfile_small.txt");
//ifstream infilelarge("inputfile_small.txt");
while (getline(infilesmall, line))
{
istringstream iss(line);
vector<string> tokens;
copy(istream_iterator<string>(iss), istream_iterator<string>(), back_inserter(tokens));
string str = ((tokens)._Myfirst)[0];
cout<< ((tokens)._Myfirst)[1];
//char operation = new char(((tokens)._Myfirst)[0]);
/*typedef void (*funcPointer)(int);
void String1Action(int arg);
void String2Action(int arg);
map<string, funcPointer> stringFunctionMap;
stringFunctionMap.add("string1", &String1Action);*/
insert(t,10);
find(t,0);
//Delete(t,10);
}
so the question what is ideal way of calling Insert, Delete and Find by splitting the line? i need to take care the performance. One approach i found out is to create enum with key and value pair and having function pointer. so depending on the key value ("I","D","F") the appropriate function will be called with it's respective value. Can you please suggest me/correct my approach and guide me on this code. Appreciate your time. Thanks

Your code is needlessly complex. You can read the operator and the number from the file, one pair at a time, and use the number appropriately based the value of the operator.
char op;
int number;
while ( infilesmall >> op >> number )
{
switch (op)
{
case 'I':
insertData(number);
break;
case 'D':
deleteData(number);
break;
case 'F':
findData(number);
break;
default:
std::err << "Unknown operator, " << op << std::endl;
}
}

Related

Reading from a file with getline never goes past the first line

I am trying to read the contents of an obj file in C++, and I am struggling with it. It's been a couple of years since I have done anything with C++, so I have forgotten a lot, but I am much more proficient in Java.
I am trying to read an obj file with contents like this
# This file uses centimeters as units for non-parametric coordinates.
mtllib crayon.mtl
g default
v 0.927930 -17.363054 -0.279038
...
vt 0.648603 0.107966
... etc
However, it doesn't seem to be able to read past the first line. Here is my code thus far:
void TextureMapper::read_file(string file_name)
{
ifstream file;
file.open(file_name);
if (file.is_open())
{
string line;
while (getline(file, line))
{
std::stringstream ss(line);
string token;
ss >> token;
switch (get_obj_type(token))
{
case GEOMETRIC_VERTEX:
break;
case TEXTURE_VERTEX:
break;
case VERTEX_NORMAL:
break;
case GROUP_NAME:
break;
case SMOOTHING_GROUP:
break;
case FACE:
break;
case MATERIAL_NAME:
break;
case MATERIAL_LIBRARY:
break;
case COMMENT:
break;
case BLANK:
break;
}
}
file.close();
}
else
std::cerr << "Couldn't open " << file_name << " for reading\n";
}
Where the enums (GEOMETRIC_VERTEX, GROUP_NAME, etc) are mapped to the parts of the obj file (like v, g vt, #, etc).
EDIT I realized the blank line in my file might be causing the exception, so I added an enum for that, and added it as a case to my switch statement, and now I don't get an exception, but it just never reads past the first line.
The first line (# This file uses centimeters as units ...) is read in properly, and I can see that when I debug it. However, when it re-enters the while loop again, it looks like it re-reads that same line, again and again.
I'm unsure what I am doing incorrectly when I read in this file. Let me know if you need more information to go on.

outputting from a file stuck in loop and not reading?

So the while loop at the bottom of this following code simply just loops continuously I had it working before I added the line
questionFile >> answersArray[i];
This line seems to break it so that nothing writes into the questionsArray anymore, I'm rather stumped by this so help would be appreciated.
ifstream questionFile;
int i = 0;
switch (x){
case 1:
questionFile.open("Topic1 Questions.txt", ios::app);
break;
case 2:
questionFile.open("Topic2 Questions.txt", ios::app);
break;
case 3:
questionFile.open("Topic3 Questions.txt", ios::app);
break;
case 4:
questionFile.open("Topic4 Questions.txt", ios::app);
break;
}
if (!questionFile)
{
cout << "Cannot load file" << endl;
}
else
{
if (questionFile.peek() != ifstream::traits_type::eof()) {
while (!questionFile.eof())
{
getline(questionFile, questionsArray[i]);
questionFile >> answersArray[i];
i++;
}
}
questionFile.close();
}
Both getline and operator>> extract from the file but getline reads until '\n' (or any character you specify) while operator>> reads until a whitespace. Furthermore, operator>> will leave the '\n' in the stream causes the next invocation of getline to read nothing.
Read the getline and operator>> documentations for more details (those are links).
Also, post the format that the questions/answers are stored in the file because how your code will run is very dependent on that. If you're just expecting alternating lines of questions and answers, just use getline and forget the operator.
Hopefully this helps.
It's a little hard (okay, impossible, really) to be certain what your problem is without seeing things like the type of answersArray. It would also help (a lot) to see what a sample of the input file you want to read.
For the moment, I'm assuming your input file look at least a little like this:
1) air 2) earth 3) fire 4) water
3
1) Solid 2) Liquid 3) Gas 4) Plasma
2
i.e., one line is a string (containing questions or possible answers, or maybe both), and the next line is a single number signifying the correct answer (or something on that order).
Assuming that's the case, I'd read all the data from the input file itself using std::getline. Then I'd split that up into the necessary pieces.
I'd probably also create an answer class (or something similar) to hold both the string and the number, and overload operator>> for that class to read both.
struct answer {
std::string question;
int answer;
friend std::istream &operator>>(std::istream &is, answer &a) {
std::getline(is, a.question);
std::string temp;
std::getline(is, temp);
a.answer = boost::lexical_cast<int>(temp);
return is;
}
};
Using that, reading the data from the file could look something like this:
std::vector<answer> answers{
std::istream_iterator<question>(questionFile),
std::istream_iterator<question>()};
This reads the data from the file, and puts the items it reads into the vector I've named answers.
While we're at it, I'd also change the code to open the file, at least a little bit. ios::app only really makes sense if you're going to write to a file, not just read from it. Second, a case statement to choose hard-coded file names seems a bit clumsy, at least to me.
I'd probably write that part of the code more like this:
if (x<0 || x > 4)
throw std::runtime_error("prohibited file name");
std::stringstream name << "Topic" << x << " Questions.txt";
std::ifstream questionFile(name.str());

Stringstream not reading as wanted

After having tried for a few hours to find out why my C++ code doesn't work as required, I've found out the error should be hiding within this piece of code:
void loadWorld(GameOfLife& game, int rij, int kolom, string fileName){
// Reads a .LIF-file and configures the GameOfLife object based on this info
ifstream ifs(fileName.c_str());
stringstream ls;
if(!ifs) throw new fileNotFound;
string line;
char tempChar;
Block tempBlock;
vector<Cel> tempVector(0);
string rule;
while(ifs.good()){
getline(ifs, line); //get next line from the file
ls.str(line); //put it into a stringstream
if(line!="") { //skip empty strings
ls >> tempChar;
if(tempChar=='#')
ls >> tempChar; //go to the next sign
switch(tempChar){
case 'N':
rule = "23/3"; //default rule
break;
case 'R':
ls >> rule; //set new rule
break;
case 'P' :
if(tempBlock.cellen.size()>0)
loadBlock(game, rij, kolom, tempBlock); //load previous block
//new block
tempBlock.cellen.clear();
ls >> tempBlock.x >> tempBlock.y;
break;
case '.' : case '*' :
cout << tempChar; //for testing
tempVector.clear();
if(tempChar=='.')
tempVector.push_back(Cel(0, fl_rgb_color(0,0,0)));
else
tempVector.push_back(Cel(1, fl_rgb_color(0,0,0)));
while(ls.good()){
ls >> tempChar;
cout << tempChar; //test
if(tempChar=='.')
tempVector.push_back(Cel(0, fl_rgb_color(0,0,0)));
else
tempVector.push_back(Cel(1, fl_rgb_color(0,0,0)));
}
tempBlock.cellen.push_back(tempVector);
cout << endl; //test
break;
default:
break;
}
}
}
loadBlock(game, rij, kolom, tempBlock); //load last block
int survival=0;
int birth=0;
extractRule(rule, survival, birth);
game.setSurvival(survival);
game.setBirth(birth);
}
The code is part of an implementation of Conway's Game Of Life and is supposed to read a file that contains information about a certain configuration, then configure the object 'game' of the type GameOfLife to contain this configuration. An example of a file that should be read is:
#Life 1.05
#D Acorn
#D The most vigorously growing 7-cell
#D "methuselah" pattern. See also RABBITS.
#N
#P -3 -1
.*
...*
**..***
The program should ignore the first four rules and upon reading the fifth rule, should set the rule of game to 23/3, the normal rule. It does all that.
It should also read blocks of code like the one following #P. For some reason, it does not do so. As you can see, I use cout as a debugging tool in the parts of the code that do not work as expected. My expected output would be:
.*
...*
**..***
but it is:
.**
*
*
I have no idea why this is the case. Please let me know if you have any tips on how to find the error. If you need more information (like code for used structures like Cel of Block), please let me know. I didn't include those as I suspected they would distract from the problem; it persists even when excluding the parts that use Block or Cel.
Note: The necessary includes have been made and the program compiles in Eclipse without any errors or warnings.
In addition to ls.str(line); for each line, you need the following line to clear error flags of ls.
ls.clear();
An even simpler way may be construct the stringstream after a line is read and destruct it once the line is done.
while(getline(ifs, line)) {
istringstream ls(line);
// ...
}
Two problems:
ls.good() is still true after you read the last character of the buffer. It's when you attempt to read again that it becomes false. Thus, you should check it after your attempted read from ls to ensure that you actually read in a character.
ls.str() does not clear the error flags, which means that all subsequent reads will fail. You'll need to call ls.clear() after that, or simply construct a new stringstream for each line.

Parsing with strings into different types with different delimiters

This is actually the first time I have used this website: usually I am able to find the a solution from a previous post, but this time I am to no avail. Anyways, my problem is that I am trying to parse a file that contains commas and parentheses. It also contains strings and doubles that I need to change from the string into a double so I can use the data. I made a struct to store the data.
struct Artery{
string segment; string name;
double length; double radius; double wall_thickness;
double young_modulus; double compliance;
Artery(string seg, string n, double l, double r, double w, double y, double c)
: segment(seg), name(n), length(l), radius(r), wall_thickness(w), young_modulus(y), compliance(c){}
};
The data will be in the format as follows:
(Segment, Name, Length, Radius, Wall Thickness, Young's Modulus, Compliance)
(2A, Aorta_Ascendens, 2, 1.47, .164, 4, 53.4)
(2B, Aorta_Ascendens, 2, 1.44, .161, 4, 51.0)
(3A, Arcus_Aorta, 2, 1.12, .132, 4, 29.6)
I'm really just a beginner into the C++ language (taking a college course on the subject right now actually) and we haven't covered low-level programming with pointers and such that I know is what is usually used to parse this kind of thing. Could I get some help with this? I really don't have any idea on how to get this working.
Below is what I currently have in the process of attempting to parse the file. I wanted to eventually at the end have a vector of Artery that will serve as the basis for me accessing the information.
void read_data(vector<Artery>& v, ifstream& ifs){
//reads and seperates file for arterial data (currently from the westerhoff model)
vector<string> data_string;
while (!ifs.eof){
string data_set = " ";
getline(ifs, data_set);
data_string.push_back(data_set);
}
for (int i = 0; i < data_string.size(); ++i){
string seg; string n;
double l; double r; double w; double y; double c;
istringstream iss(data_string[i]);
}
}
To recap: I need help with parsing the file with the above format and then convert that format into an Artery. I want to then compile them into a vector of Artery to later have access to them.
Thank you,
Zav
There are many ways to do this.
Assuming your filestream, ifs is already initialized and is_open (that is you've already done the checking of the stream, then you do all operations in one go (reducing your runtime by at least a factor of two compared to your version):
void read_data(std::vector<Artery>& v, std::ifstream ifs) {
std::vector<std::string> data_strings;
std::string temp;
//read the file by lines
while(std::getline(ifs, temp)) {
//store the line (included for completeness)
data_strings.push_back(temp);
//as you didn't state if your lines began and ended with parentheses
//I opted to cover removal of them:
size_t begin = temp.find("(");//as there is only 1
size_t end = temp.find(")");//as there is only 1
temp = temp.substr(begin+1, end-1);
std::istringstream iss(temp);
Artery artery;
int col = 1;//the current column
//here we're going to use the stringstream to tokenize the string by commas
//using std::getline
while (std::getline(iss, temp, ',')) {
std::istringstream is(temp);//this is another string stream initialized to the token (which may or may not be a string)
//determine which artery property to modify based on the column number
switch(col) {
case 1:
is >> artery.segment;
break;
case 2:
is >> artery.name;
break;
case 3:
is >> artery.length;
break;
case 4:
is >> artery.radius;
break;
case 5:
is >> artery.wall_thickness;
break;
case 6:
is >> artery.young_modulus;
break;
case 7:
is >> artery.compliance;
break;
default:
std::cerr << "Invalid column!\n";
}
++col;
}
//now the artery has been correctly initialized, we can store it:
v.push_back(artery);
}
}
You'll notice that this version doesn't utilize the Artery Constructor that you supplied. It could have been used, but from how you've defined Artery, it highly resembles a POD (Plain-Old-Data type); hence, I've treated it in this solution.
Also, as you I wasn't sure if parentheses were part of each constituent line, I've treated them accordingly by removing them. (Of course, if your data doesn't contain these, you can remove the code that handles that deletion, easily).
NOTE:
My use of the stringstream extraction operator (>>) doesn't check for proper assignment. But, as this is a naive example, that is something you can do for yourself.

Which character is first among 4 characters in c++

In my project I take a string from user and then I need to check if vowels a, e, I, O, U are present. If so, I have to find out which one comes first in the string and which one comes next after that. For example, if a user gave input something like this:
char expr[] = "this is for something real";
I comes first, then I again, then O and so on. I checked whether the characters are in the string or not using strchr(expr,'character here'). To find which character comes first, I find the index of each character using
const char *ptr = strchr(expr, characters here);
if(ptr) {
int index = ptr - expr;
}
After that I check which index is bigger. But this is very long process.
Is there a smarter way to do this?
If you don't need the locations in the original string, but only the order, you can use std::remove_copy_if with a functor that detects non-vowel characters (i.e. returns true for vowels):
std::string only_vowels;
std::remove_copy_if( std::begin(expr), std::end(expr),
std::back_inserter(only_vowels),
[]( char ch ) { char upper = toupper(ch);
return upper!='A'
&& upper!='E'
&& upper!='I'
&& upper!='O'
&& upper!='U'; } );
(Using C++11 features to obtain the iterators and a lambda instead of creating a functor, but the same can be written in C++03 with a bit of extra boiler plate code. Code is untested)
After the algorithm completes, only_vowels will contain only the vowels present in the original string and in the exact order where they appeared. Case is not modified by the algorithm.
Alternatively you can iterate manually over the elements in the string, and test each character to see whether it is a vowel, then print it or do whatever needs to be done. If this is homework, this is probably what is expected from you.
This can be done pretty easily by simply iterating over the input expression and noting when the target letters are encountered. Here's a C++11 way that will fill a std::vector with the vowels in the order they are seen in the input expression. If you just need to print them out, then just don't fill the results vector (print them instead).
char expr[] = "this is for something real";
std::vector<char> results;
std::for_each(
expr,
expr + strlen(expr),
[&results] (const char c)
{
switch(c)
{
case 'a':
case 'A':
case 'e':
case 'E':
case 'i':
case 'I':
case 'o':
case 'O':
case 'u':
case 'U':
results.push_back(c);
}
});
Here's a method that gives you the characters found, as well as the indices (in order automatically). It also makes use of some C++ features, rather than C:
int main() {
std::string expr = "this is for something real";
std::string toCheck = "AaEeIiOoUu"; //checking for these letters
std::map<int, char> indices; //stores index-character pairs
for (char c : toCheck) { //go through each char in string (ranged-for, C++11)
if (expr.find (c) != std::string::npos)
indices [expr.find (c)] = c; //map sorts itself by key
}
for (const auto &element : indices) { //print results
std::cout << "'" << element.second << "'"
<< " found at index " << element.first << '\n';
}
}
I'm not sure if it offers anything else you need over David's answer, but it's one way to do it, and does allow for more information to be pulled. The current code, however, will only give you the first occurrence of each. I'm not sure whether you wanted all or the first, but it is adaptable. At that point, though, David's answer fits in quite nicely.