I am having a bit of an issue with a current task of mine. Basically, I am given an XML file and am trying to parse it for key information. For example, some lines will be like this:
<IPAddress>123.45.67</IPAddress>
And I am to get the value of 123.45.67, nothing too bad at all. I was told not to use a XML parser and just parse it manually, which was pretty easy. However, I am having issues with the second part of the task. Basically, I am to make a class with certain member variables and declare them based on the values I parse. So let's say the class is called Something and there is a member variable called IPAddress. I am to then update the value of IPAddress to 123.45.67 so when someone calls Something.IPAddress in the main method, it returns 123.45.67. This was my initial attempt at it:
#include <iostream>
#include <fstream>
#include <string>
#include <sys/stat.h>
using namespace std;
class Something
{
public:
string location;
string IPAddress;
string theName;
int aValue;
//loop through the array from the method below
void fillContent(string* array)
{
for(int i = 0; i < array->size(); i++)
{
string line = array[i];
if((line.find("<") != std::string::npos) && (line.find(">")!= std::string::npos))
{
unsigned first = line.find("<");
unsigned last = line.find(">");
string strNew = line.substr (first + 1, last - first - 1); //this line will get the key, in this case, "IPAddress"
unsigned newfirst = line.find(">");
unsigned newlast = line.find_last_of("<");
string strNew2 = line.substr(newfirst + 1, newlast - newfirst - 1); //this line will get the value, in this case, "123.45.67"
if(strNew == "IPAddress")
{
IPAddress = strNew2; //set the member variable to the IP Address
}
}
}
}
//this method will create an array where each element is a line from the xml
void fillVariables()
{
string line;
ifstream myfile ("content.xml");
long num = //function that gets size that I didn't add to make code shorter!;
string *myArray;
myArray = new string[num];
string str1 = "";
string strNew2 = "";
int counter = 0;
if (myfile.is_open())
{
while ( getline (myfile,line) )
{
myArray[counter] = line;
counter++;
}
myfile.close();
}
fillContent(myArray);
}
};
int main(int argc, char* argv[])
{
Something local;
local.fillVariables();
cout << local.IPAddress<< endl; // should return "123.45.67"
return 0;
}
Now this does do what I want it to do, however, you can see I need the if statement. Assuming I have at least 20 of these member variables, having 20 if-statements would be annoying and just frowned upon. Is there any other way I could somehow access the member variables from the class? Sorry if my question was long, I just wanted to make sure everything that is needed to understand the question is provided! Please let me know if anything crucial that may not be there should be added.
Thanks a lot!
This may be considered bad style, but I usually just do:
// at the top of the 'fillContent' function
std::map<string, string*> varmap{
{"IPAddress", &IPAddress},
{"AnotherField", &AnotherField}
};
// If you're not using C++11, you can also try:
// std::map<string, string*> varmap;
// varmap["IPAddress"] = &IPAddress;
// varmap["AnotherField"] = &AnotherField;
// parsing code goes here
*varmap[strNew] = strNew2;
Related
I am new to C++, and I am a little bit overwhelmed by the class structures available, I have no experience with the different librairies hence I am trying to figure out what is the best structures to use for basics tasks.
I am trying to create a function that reads a file and copy it to a working structure, I first tried without any classes or objects, it was not possible to return a valid pointer to a char* [][] structure (don't know if it's the best way to proceed), so I am trying to come up with another solution based on vector<vector > code I picked up somewhere, hopefully it can work this way.
Below is the code I am trying to work out.
void readFile2(FILE* f) {
string buf = "abc";
int a = countLine(f);
int b = countCol(f);
long bytes;
char c;
int d = 0;
fseek(f, 0L, SEEK_END);
bytes = ftell(f);
fseek(f, 0L, SEEK_SET);
int x,y = 0;
vector<vector<string> > vec;
for (int i = 0;i<a;i++) {
vector<string> v1;
for (int j = 0;j<b;j++) {
v1.push_back(buf);
}
vec.push_back(v1);
}
buf.erase();
for (int i=0;i<bytes;i++) {
c = fgetc(f);
if (c == '\t') {
cout<<"buf : "<<buf<<endl;
***cout<<"vec : "<<vec[y][x]<<endl;***
d = 0;
y += 1;
buf.erase();
} else if (c == '\n') {
cout<<"buf : "<<buf<<endl;
**cout<<"vec : "<<vec[y][x]<<endl;**
d = 0;
x += 1;
y = 0;
buf.erase();
} else {
buf.push_back(c);
d++;
}
}
Question :
The vector[y][x] I am trying to fill up with strings coming the buffer in the second part of the function (inbetween asterisks in the above code) does not display anything, although it is correctly initalized with string elements during the initialisation part of the code.
strcpy(vec[y][x],buf) (not present here) does not work either because is requires char* pointer (strange for a function that is named after str, thus I'm kinda stuck because have no idea what to use nor if the structure is correct.
I am trying to design the function to return a valid pointer to the vector that could be used by another function in the main(), I don't know however if it is the correct way or the most efficient way to load the file in memory and generate an array structure.
Thank you in advance for your time.
A completely new version that is very much straight down the line c++. Your code was basically c code
#include <string>
#include <fstream>
#include <vector>
#include <sstream>
int main() {
std::ifstream input("test.txt");
std::vector<std::vector<std::string>> fields;
int count = 0;
while (input) {
std::string line;
std::getline(input, line);
std::stringstream strs(line);
fields.push_back(std::vector<std::string>());
while (strs) {
std::string field;
std::getline(strs, field, '\t');
fields[count].push_back(field);
}
count++;
}
for (auto& l : fields) {
for (auto& f : l) {
std::cout << f;
}
std::cout << "\n";
}
}
I'm trying to be able to call a function with the vector and for some reason it's saying "expected primary expression before ']'. The vector could hold any number of files, depending on the amount of numbers in myfile, so I'm not sure what I should put there.
#include <fstream>
#include <string>
#include <vector>
#include <iostream>
using namespace std; // not recommended
double averageCalc(string[],int);
int main () {
double average;
string line;
ifstream myfile ("array_pgmdata.txt");
//int index = 0; // not needed
//string myArray[index]; // UB - if it even compiles, it's a VLA of size 0.
std::vector<std::string> myArray; // use this instead to be able to grow it
// dynamically
if (myfile) // open and in a good state
{
// while (! myfile.eof() ) // It'll not be eof when you've read the last line
// only when you try to read beynd the last line,
// so you'll add "line" one extra time at the end
// if you use that. Use this instead:
while(getline(myfile, line))
{
// myArray[index++] << line; // you have 0 elements in the array and
// can't add to it in any way
myArray.push_back(line);
}
}
else cout << "Unable to open file";
for(size_t idx=0; idx < myArray.size(); ++idx) {
std::cout << myArray[idx] << "\n";
}
average = averageCalc(myArray[], line); // error here
return 0;
}
double averageCalc(string nums[], int count)
{
int a, total, elements, averaged1, averaged2;
// string averaged2;
for(a = 0; a < count; a++)
{
total+=a;
elements++;
}
averaged1 = total / elements;
return averaged2;
}
There's a few problems here. Firstly, your function averageCalc expects a parameter of type string[] which is an array of strings. When you call the function, you are trying to pass it a std::vector<string>, which is not an array of strings, it is a class. Presumably, you would want to change your function to take in a vector, like so:
double averageCalc( const std::vector<string> & nums ); // no need for size now
The other issue you have is in calling your function. When you call it, you pass myArray[] as a parameter, which is the error you compiler is giving you. This is not valid syntax, you simply want to pass in myArray.
I think that the error occurs becase firstly you create the array with std::vector<std::string> myArray; so the data is string type but when you want to calculate the average value the function expects a value int, double etc. in order to perform math. Either change the string to int or use a function to convert it:
int main()
{
string s = "12345";
// object from the class stringstream
stringstream geek(s);
// The object has the value 12345 and stream
// it to the integer x
int x = 0;
geek >> x;
// Now the variable x holds the value 12345
cout << "Value of x : " << x;
return 0;
}
I newly started C++ and it feels pretty wired while writing Java for a while. So, I have this array,
char values[][10] = {"miami", "seattle", "berlin"};
int rows = sizeof values / sizeof values[0];
This is this is the function where I would like to pass the value,
// a function to reverse the strings
void App::reverse(char *str) {
}
When I do the loop, I can't apparently pass the value there,
for (int i = 0; i < rows; ++i) {
// first character of the string
char *firstPtr = values[i];
reverse(firstPtr);
}
The line reverse(firstPtr) provides error which I don't understand. The error message says Too few arguments, expected 2.
What is the issue here? I apologize for any mistakes as writing the C++ for the first time and the pointer stuff feels strange.
UPDATE
This is the piece of code I would like to exexute,
void App::reverse(char* str) {
// get the first character of the string
char *ptrEnd = str;
char temp;
if (str){
while (*ptrEnd) {
ptrEnd++;
}
ptrEnd--;
// as long the first adddress is lesser than the end
while (str < ptrEnd) {
temp = *str;
*str++ = *ptrEnd;
*ptrEnd-- = temp;
}
}
}
There is too little information here to be sure, but it looks like you have
using namespace std;
Somewhere in your code. Don't do this! In this case, the standard library has a function reverse() in std that takes two parameters.
Furthermore, you have void App::reverse(char *str), but that cannot be seen from void myArray::reverse(char* str), so your own reverse() cannot be called as-is - you would need to do App::reverse() if the function is a class static.
My program consists of three files. arithcard.hpp (header), assign2.cpp (the main program) and arithcard.cpp(method).
In the array, cards, is store strings read from a text file. For example, cards[0].fileLine has the string 2 + 2 = 4. (fileLine is a private member variable with all the strings), cards[1].fileLine has the string 2 + 4 = 3, etc...
I know cards[i].fileLine contains all these strings because I tested by printing them all out in the readCards method to see if they are actually in there.
Here is my problem, when I try to call cards[i].getQuestion (or .getAnswer()) in the main program. Sometimes, it shows me the question(or answer) or the error
"terminate called after throwing an instance of 'std::length_error'
what(): basic_string::_S_create"
arithcard.hpp
#include <iostream>
#include <string>
/*
Defines the class ArithCard, for handling arithmetic flash cards.
You may not modify the public method declarations provided below. The main
program used to test your solution will assume that these methods exist.
You can add private attributes and methods as you need them.
You may find it convenient to add additional public methods for testing.
Keep in mind that your code must work with the unmodified main program
or it will fail automated testing.
*/
class ArithCard {
public:
// Default constructor
ArithCard() ;
static bool readCards(const std::string &fileName,
int &cardCnt,ArithCard *&cards) ;
void displayQuestion(std::ostream &out) ;
bool checkAnswer(std::istream &in) ;
// Return the question as a string.
std::string getQuestion() ;
// Return the answer as a string.
std::string getAnswer() ;
// Return the answer as an integer.
int getAnswerValue() ;
private:
// Add your private methods and attributes here.
std::string fileLine;
std::string question;
std::string answer;
} ;
arithcard.cpp
#include <iostream>
#include <string>
#include <fstream>
#include "arithcard.hpp"
using namespace std;
ArithCard::ArithCard()
{
//body intentionally empty
}
bool ArithCard::readCards(const string &fileName, int &cardCnt, ArithCard *&cards)
{
//read the file line by line and store it in cards
return (true);
}
void displayQuestion(std::ostream &out)
{
//display output
}
bool checkAnswer(std::istream &in)
{
return (true);
}
// Return the question as a string.
string ArithCard::getQuestion()
{
size_t pos = 0;
pos = fileLine.find("=");
question = fileLine.substr(0, pos);
return question;
}
// Return the answer as a string.
string ArithCard::getAnswer()
{
size_t pos = 0;
pos = fileLine.find("=") + 1;
answer = fileLine.substr(pos);
return answer;
}
// Return the answer as an integer.
int getAnswerValue()
{
return answer;
}
assign2.cpp
#include <iostream>
#include <sstream>
#include <string>
#include <limits>
#include <random>
#include "arithcard.hpp"
// File-local anonymous namespace
namespace {
int verbosity = 1 ;
}
/*
Read in the files of cards, then loop: query the user to specify a
question, print the question, check the answer. Terminate when the user
types an empty line instead of specifying a question.
*/
int main (int argc, const char *argv[])
{
if (argc != 2) {
std::cout << "Usage: " << argv[0] << " <datafile>" << std::endl ;
return (1) ;
}
std::string dataFile(argv[1]) ;
bool result = false ;
int cardCnt = -1 ;
ArithCard *cards = nullptr ;
/*
Parse the file of questions & answers. A false return value indicates an
absent file or other i/o error. It's also possible the file is present but
contains no valid questions.
*/
result = ArithCard::readCards(dataFile,cardCnt,cards) ;
std::cout << cards[0].getQuestion() << std:: endl;
return (0) ;
}
From ยง21.4.2/1 [string.require],
If any operation would cause size() to exceed max_size(), that operation shall throw an exception object of type length_error.
So somewhere in your code you're attempting to create a string whose length exceeds string::max_size(), which is typically a huge number, so you should feel proud of yourself :-)
Jokes aside, it's difficult to tell where the error is because it looks like you've removed relevant sections of code, especially the implementation of ArithCard::readCards. I don't understand why you chose to make this function static and then pass it a pointer to an instance of ArithCard, instead of just making it a non-static member function.
Your code has the pointer you're passing to ArithCard::readCards initialized to nullptr, so I assume you're allocating memory for the object within the function. If you're not, then you most likely have undefined behavior going on within that function.
I'd change that function to
class ArithCard {
public:
...
bool readCards(const std::string &fileName, int &cardCnt) ;
};
And change the code within main() to
ArithCard cards;
result = cards.readCards(dataFile, cardCnt);
Also, in both getQuestion() and getAnswer() you're not checking the return value of string::find, so attempting to extract a substring using a invalid result may be the cause of this error. This is the most likely explanation because string::find will return string::npos, a huge number, if the search term is not found.
I am making a file reading class. It should, when constructed open the file with the given string and depending on which constructor is called use the second string supplied to skip through the file to the line after the string given.
Here is my code as it stands:
SnakeFileReader::SnakeFileReader(string filePath)
{
fileToRead_.open(filePath.c_str(), ios::in);
}
SnakeFileReader::SnakeFileReader(string filePath, string startString)
{
fileToRead_.open(filePath.c_str(), ios::in);
string toFind;
while (toFind != startString && !fileToRead_.eof())
{
fileToRead_ >> toFind;
}
}
string SnakeFileReader::ReadLine()
{
string fileLine;
if (!fileToRead_.fail() && !fileToRead_.eof())
fileToRead_ >> fileLine;
return fileLine;
}
int SnakeFileReader::ReadInt()
{
string fileLine = "";
if (!fileToRead_.fail() && !fileToRead_.eof())
fileToRead_ >> fileLine;
int ret;
istringstream(fileLine) >> ret;
return ret;
}
SnakeFileReader::~SnakeFileReader()
{
fileToRead_.close();
}
The problem I have is that in the second constructor I get a segmentation fault. I get another segmentation fault in the read line function as soon as I declare a string.
[Edit] Here is the extra code requested. I am making a "Snake Game" as a part of the first year of my degree. I want the game to read and save files rather than hard code variable values. I will finally be using this class a lot to setup a level in the game. However here are a few lines that should demonstrate how i intend to use this class:
//Level.cpp
std::string fileToRead = "resources/files/level1.txt";
SnakeFileReader sfr(fileToRead);
std::string mapFilePath = sfr.ReadLine();
ImageFile(mapFilePath).load(map_layout);
mapWidth_ = sfr.ReadInt();
mapHeight_ = sfr.ReadInt();
level_cell_size_ = sfr.ReadInt();
map_ = new TileData*[mapWidth_];
for (int i = 0; i < mapWidth_; i++)
{
map_[i] = new TileData[mapHeight_];
}
Layout of the file:
resources/images/Map1_Layout.bmp
40
30
20
Class declaration:
#ifndef SNAKE_FILE_READER_HPP
#define SNAKE_FILE_READER_HPP
#include <iostream>
#include <fstream>
#include <sstream>
using namespace std;
class SnakeFileReader
{
public:
SnakeFileReader(string filePath);
SnakeFileReader(string filePath, string startString);
~SnakeFileReader();
string ReadLine();
int ReadInt();
private:
ifstream fileToRead_;
};
#endif // SNAKE_FILE_READER_HPP
in the ReadLine function, you return a reference to a variable allocated on the functions stack. you are corrupting the stack, crazy things can happen. your compiler should have warned you about that.
I'm not sure about your constructor, but the problem with ReadLine() is that you're trying to return the memory address of an automatic variable, which is destroyed when you exit the function.
The simplest fix would be to remove the '&' on the return value, and just return a string. But if you're determined to return a memory address, try this instead:
string *SnakeFileReader::ReadLine()
{
string *fileLine = new string;
if (!fileToRead_.fail() && !fileToRead_.eof())
fileToRead_ >> *fileLine;
return fileLine;
}
This will dynamically allocate the string and pass back the pointer. The difference is that dynamic variables are not automatically destroyed when you leave their scope. The string will still exist on the heap until you delete it yourself (which you must remember to do when you're done with it).