I am attempting to implement an AVL Tree by using key/value pairs from a text file. For example, (0003,09390,Vegas) would be one such key value pair, with 0003 being the key, and 09390, Vegas being the value. I am confused on how to extract each individual item. I would want 0003, 09390, and Vegas stored as each individual variables in a struct.
I recommend using a structure to hold the values:
struct Values
{
std::string value; // such as 09390
std::string city; // such as "Vegas"
};
Next, overload the extraction operator:
struct Values
{
std::string value; // such as 09390
std::string city; // such as "Vegas"
friend std::istream& operator>>(std::istream& inp, Values& v);
};
std::istream& operator>>(std::istream& inp, Values& v)
{
inp >> v.value;
// insert code to skip over delimiter.
inp >> v.city;
return inp;
}
Your input loop could look like this:
std::string key;
std::string delimiter;
Values v;
AVL tree;
//...
while (input_file >> key >> delimiter >> v)
{
tree[key] = v;
}
Note: the above input expression only works when values are not space or tab separated.
For more information about reading from a file, search StackOverflow for:
"c++ read file struct"
"c++ read file csv"
The easiest approach is probably to use ignore() in combination with std::getline():
std::string key, code, city;
if (in.ignore(std::numeric_limits<std::streamsize>::max(), '(')
&& std::getline(in, key, ',')
&& std::getline(in, code, ',')
&& std::getline(in, city, ')')) {
// ...
}
Related
Is there a way to import the content of my text file into a structure?
My code looks like this:
struct customerstruct {
int Customer_ID;
string name;
string adress;
};
And this is the content of my text file:
101
James
221B Baker St.
I would like to mirror the value of my text file into the structure.
Here's a suggestion:
struct customerstruct
{
int Customer_ID;
string name;
string adress;
friend std::istream& operator>>(std::istream& input, customerstruct & c);
};
std::istream& operator>>(std::istream& input, customerstruct & c)
{
input >> c.Customer_ID;
input.ignore(1000, '\n');
std::getline(input, c.name);
std::getline(input, c.adress);
return input;
}
Your input loop could look like this:
customerstruct c;
std::vector<customerstruct> database;
while (my_file >> c)
{
database.push_back(c);
}
The structure overloads operator>> to reading a single instance.
A std::vector is used to contain more than one instance read from a file.
The contents of my file look like:
Barr,3145,7
Rab,12,5513,1412,221,232,179,7121231
Bean,1,1231,219,21,337,9239,312,764,640391,4,7966346,22278,5,116364,56350
Earl,132,230,121,32,425,67
Donut,112,5525,23121,2123,65432,8790,3,4567,444
I want each line to be stored in a 2D vector (vector<vector<string>>) without the comma.
I have tried doing:
while(getline(filestream, line)){
stringstream linestream(line);
while(getline(linestream, anotherLine, ','){
oneDvector.push_back(anotherLine);
}
twoDvector.push_back(oneDvector);
oneDvector.clear();
}
But this does not seem to work. What can I do?
IMHO, you don't want to use a 2d vector or array. You want a std::vector of a class:
struct Record
{
std::string text;
std::vector<int> data;
friend std::istream& operator>>(std::istream& input, Record& r);
};
std::istream& operator>>(std::istream& input, Record& r)
{
std::string text_line;
std::getline(input, text_line);
std::istringstream text_stream(text_line);
std::getline(text_stream, r.text, ',');
int value;
char comma;
while (text_stream >> value)
{
r.data.push_back(value);
text_stream >> comma;
}
return input;
}
Note: In the above input function, a record is read by line into a string. This will make the reading of the numbers easier.
You input loop could look like:
Record r;
std::vector<Record> database;
while (file_stream >> r)
{
database.push_back(r);
}
Usually modeling a structure from an input record makes a better program. Easier to develop and debug.
The input may be simplified by replacing the 2nd occurrence and remaining commas with spaces. The first comma is used to end the text field.
I am trying to count the no. of dates stores in the first line of a CSV file (Separated by commas):-
State,Region,Lat,Long,1/22/20,1/23/20,1/24/20,1/25/20,1/26/20
I have to count the no. of dates after Long(i.e. output = 5).
I have written the code to read the CSV file, which I have shown below, but, how shall I count the no. of dates after long. Would highly appreciate your help. Please feel free to ask for any other piece of information. Thanks.
char** readCSV(const char* csvFileName, int& csvLineCount)
{
ifstream fin(csvFileName);
if (!fin)
{
return nullptr;
}
csvLineCount = 0;
char line[1024];
while(fin.getline(line, 1024))
{
csvLineCount++;
};
char **lines = new char*[csvLineCount];
fin.clear();
fin.seekg(0, ios::beg);
for (int i=0; i<csvLineCount; i++)
{
fin.getline(line, 1024);
lines[i] = new char[strlen(line)+1];
strcpy(lines[i], line);
};
fin.close();
return lines;
}
This looks like you are reading a record that contains one or more subrecords.
I recommend using a Date class as well as a Record class.
class Date
{
public:
unsigned int month_num;
unsigned int day_num;
unsigned int year_num;
friend std::istream& operator>>(std::istream& input, Date& d);
};
std::istream& operator>>(std::istream& input, Date& d)
{
char forward_slash;
input >> d.month_num; input >> forward_slash;
input >> d.day_num; input >> forward_slash;
input >> d.year_num;
return input;
}
Overloading operator>> for the Date class will come in handy later.
Now the record class:
class Record
{
public:
std::string state;
std::string region;
std::string latitude;
std::string longitude;
// A container for the date subrecords
std::vector<Date> dates;
friend std::istream& operator>>(std::istream& input, Record& r);
};
std::istream& operator>>(std::istream& input, Record& r)
{
std::string text_line;
std::getline(input, text_line);
std::istringstream record_stream(text_line);
std::getline(record_stream, r.state, ',');
std::getline(record_stream, r.region, ',');
std::getline(record_stream, r.latitude, ',');
std::getline(record_stream, r.longitude, ',');
Date d;
while (record_stream >> d)
{
r.dates.push_back(d);
char comma;
record_stream >> comma;
}
return input;
}
The above function reads a text line of input, since the records are terminated by a newline. A stream is created from the text line. The stream helps in reading a variable quantity of dates.
Edit 1: Reading in the file
Your input code would look something like this:
std::vector<Record> database;
Record r;
while (fin >> r)
{
database.push_back(r);
}
Yes, you can use C-Strings and arrays, but C++ streams and std::vector simplify the code and simple code has less defects than complicated code.
Also, the std::vector and std::string classes manage dynamic memory for you. They expand as necessary. No need for checking for array overflows. They are also easier to pass. Passing arrays requires passing the capacity also (and maybe the quantity of elements in the array).
I am unable to read the contents of a 30 row .csv file into the following struct:
class Entry {
public:
std::string GID;
std::string DName;
std::string Phone;
std::string POC;
std::string Item;
std::string Category;
double amount;
};
I want to be able to create a function that can read the csv file dubbed "Donations.csv" and remove the commas because I would like to create a vector possibly like this: std::vector<Entry> entries; so that I can sort the multiple items based on whatever element in the vector. So for example say I would want to sort by DName, the code would look something like this:
// sort by DName
std::sort(entries.begin(), entries.end(), [](const Entry& a, const Entry& b) {
return a.DName.compare(b.DName) < 0;
});
I was told not to use multiple vectors as this not safe and efficient however if it may help someone, this is how to read a csv file into multiple vectors:
FAIR WARNING, USING THE EOF CONTROLLER IS WRONG, REFER HERE
Albeit, this code will work just ok, I have personally tested it - numerous times on .csv files.
int main()
{
ifstream inFile; // Input file handler
ofstream outFile; // Output file handler
vector<string> GID; // Vector holding Gift ID
vector<string> DName; // Vector holding Donor Name
vector<string> Phone; // Vector holding Phone Number
vector<string> POC; // Vector holding point of contact
vector<string> Item; // Vector holding item donated
vector<string> Category; // Vector holding type of item donated
vector<double> amount; // Vector holding amount of donated items
int Input; // User selection choice from menu
// opening data file
inFile.open("Donations.csv");
if (!inFile) // If statement to check for file erroe
{
cout << "Input file did not open. Program will exit." << endl; // Does not compute!
exit(0);
}
// reading contents
string str; // temp string to hold parse comma
double temp; // temp double to hold null spaces for atoi function/strtod
getline(inFile, str, ','); // read first GID string outside of loop so that we
// won't accidentally reach the end of the file in the MIDDLE of the loop
while (!inFile.eof()) // end of file not reached
{
GID.push_back(str);
getline(inFile, str, ','); // get the contents of the line until a comma is encountered and push it into the necessary vector
DName.push_back(str);
getline(inFile, str, ',');
Phone.push_back(str);
getline(inFile, str, ',');
POC.push_back(str);
getline(inFile, str, ',');
Item.push_back(str);
getline(inFile, str, ',');
Category.push_back(str);
getline(inFile, str, '\n'); // get until the end of the line but split for parsing
// convert string to double using strtod and c_str
temp = strtod(str.c_str(), NULL);
// now push double onto vector
amount.push_back(temp);
getline(inFile, str, ','); // read in GID here instead of the top. If we are at the end
// of the file, it won't work and end-of-file flag will be set which is what we want.
}
inFile.close();
}
Add this method:
friend std::istream& operator>>(std::istream& input, Entry& e);
to your Entry structure.
Add this to your source code:
std::istream&
operator>>(std::istream& input, Entry& e)
{
input >> e.GID;
input >> e.Dname;
//.. input other members
return input;
}
Change your containers to:
std::vector<Entry> database;
Your input loop will look something like this:
Entry e;
while (my_data_file >> e)
{
database.push_back(e);
}
You may also want to search the internet for "stackoverflow c++ read file struct" for more examples.
In summary, create a struct or class to model the record.
Overload operator >> to read in a record.
Use std::vector of your struct, not a vector of each data member.
If you are serious about a database, then use one; don't rewrite your own.
Edit 1: Sorting
To make sorting easier, overload the > operator:
struct Entry
{
//...
bool operator<(const Entry& other) const
{
// add comparison functionality, example:
return GID < other.GID;
}
}
Now, you can sort the database by using:
std::sort(database.begin(), database.end());
Edit 2: Other sorting
To order the container using different criteria, you can create ordering functions and pass them to the std::sort function:
bool Order_By_Dname(const Entry& e1, const Entry& e2)
{
return e1.Dname < e2.Dname;
}
Your sort becomes:
std::sort(database.begin(), database.end(), Order_By_Dname);
I have a CSV file with data separated with commas. The file looks like this:
1998,MALE,United States,45,566
1993,FEMALE,......
I am going to have a vector of class ROW and each row from the data file will be stored there. My ROW has 5 variables and I need to separate them so I can use set function to row.set(year,sex, country, score, result).
Any idea how to read in the data?
From what I was told I should try to avoid getline. I do not want to convert strings to ints.
Any ideas?
I would probably start with a little operator to verify the presence of (but otherwise ignore) a string, something like this:
std::istream &operator>>(std::istream &is, char const *pat) {
char ch;
while (isspace(static_cast<unsigned char>(is.peek())))
is.get(ch);
while (*pat && is && *pat == is.peek() && is.get(ch)) {
++pat;
}
// if we didn't reach the end of the pattern, matching failed (mismatch, premature EOF, etc.)
if (*pat) {
is.setstate(std::ios::failbit);
}
return is;
}
We can use this to verify the presence of commas where needed, but otherwise ignore them fairly painlessly. I'd then overload operator>> for the ROW type to read the data appropriately:
class ROW {
int year;
enum { MALE, FEMALE} sex;
std::string country;
int foo;
int bar;
friend std::istream &operator>>(std::istream &is, ROW &r) {
is >> r.year >> ",";
std::string s;
is >> s >> ",";
if (s == "MALE")
r.sex = MALE;
else if (s == "FEMALE")
r.sex = FEMALE;
else
error("bad sex");
std::getline(is, r.country, ',');
return is >> r.foo >> "," >> r.bar;
}
};
From there, we can create a vector fairly directly:
// Open the file
std::ifstream in("data.txt");
// Read all the data
std::vector<ROW> rows { std::istream_iterator<ROW>(in), {}};