Merging Two Text Files Sorted - c++

I need to merge two text files and have them sort by "lastName" on a third outputted file. My code is below, it is outputting gibberish, all on one line. I understand my overload may be stupid, any help is appreciated.
//header
#include <fstream>
#include <string>
#include <iostream>
using namespace std;
struct mergedList {
string firstName;
string lastName;
float gpa;
int hours;
};
ostream& operator << (ostream& os, mergedList A) {
os << A.firstName << "\t" << A.lastName << "\t" << A.gpa << "\t" << A.hours;
return os;
}
istream& operator >> (istream& is, mergedList& A) {
is >> A.firstName >> A.lastName >> A.gpa >> A.hours;
return is;
}
void swap(mergedList D1[], int i, int j);
void sort(mergedList D1[], int size);
int main() {
ifstream indata;
ifstream indata2;
ofstream outdata;
indata.open("merge1.txt");
indata2.open("merge2.txt");
outdata.open("merged.txt");
//begin sentinel controlled loop for both lists
mergedList D1[100];
int index = 0;
indata >> D1[index];
while (!indata.eof()) {
index++;
indata >> D1[index];
}
sort(D1, index);
mergedList D2[100];
int index2 = 0;
indata2 >> D2[index2];
while (!indata2.eof()) {
index2++;
indata2 >> D2[index2];
}
sort(D2, index); {
int i = 0, j = 0;
while ((i < index) && (j < index2)) if (D1[i].lastName < D2[j].lastName) {
outdata << D1[i];
i++;
indata >> D1[i];
} else {
outdata << D2[j];
j++;
indata2 >> D2[j];
}
}
indata.close();
indata2.close();
outdata.close();
return 0;
}
void swap(mergedList D1[], int i, int j) {
mergedList temp;
temp = D1[i];
D1[i] = D1[j];
D1[j] = temp;
return;
}
void sort(mergedList D1[], int size) {
for (int p = 1; p < size; p++) {
for (int c = 0; c < size - p; c++) {
if (D1[c].lastName > D1[c + 1].lastName) swap(D1, c, c + 1);
}
}
return;
}

Here is some code. I have tried to explain as much as I can. If you are using C++ you should try to take advantage of the containers and algorithms that are already available for you.
struct mergedList
{
string firstName;
string lastName;
float gpa;
int hours;
};
ostream& operator <<(ostream& os, mergedList A)
{
os << A.firstName << "\t" << A.lastName << "\t" << A.gpa << "\t" << A.hours;
return os;
}
istream& operator >>(istream& is, mergedList& A)
{
is >> A.firstName >> A.lastName >> A.gpa >> A.hours;
return is;
}
// We use this to compare two MergedList structs. i.e. by first name
// http://www.cplusplus.com/reference/algorithm/sort/ for an example
struct my_sorter {
bool operator() (mergedList one, mergedList two) { return one.firstName < two.firstName ; }
};
int main()
{
ifstream indata;
ifstream indata2;
ofstream outdata;
indata.open("merged.txt");
indata2.open("merged2.txt");
outdata.open("merged.txt");
// This can be a vector. No need for array here.
vector<mergedList> D1;
int index=0, index2 = 0;
mergedList tmp;
// You can read from streams like this if the data is formatted.
while (indata >> tmp)
{
D1.push_back(tmp);
index++; // Maybe you need this??
}
// Read the second file in to the same vector.
// You don't need another one.
while (indata2 >> tmp)
{
D1.push_back(tmp);
index2++;
}
cout << "Before sorting" << endl;
copy(D1.begin(), D1.end(), ostream_iterator<mergedList>(cout, "\n"));
// Sort the vector using the std::sort algorithm.
// http://www.cplusplus.com/reference/algorithm/sort/ for an example
sort(D1.begin(), D1.end(), my_sorter());
cout << "After sorting" << endl;
copy(D1.begin(), D1.end(), ostream_iterator<mergedList>(cout, "\n"));
// Write the sorted list to the output file
copy(D1.begin(), D1.end(), ostream_iterator<mergedList>(outdata, "\n"));
indata.close();
indata2.close();
outdata.close();
return 0;
}

If optimization is an issue for you, I suggest you use STL containers as the proper sort methods are implemented. Do not use static arrays if your code should work on every given input file. I might also add that you can first merge the two files and then sort the third one.

Related

Why can't I put an Object into the start of a vector C++

I'm writing a program in which I have to read complex numbers from the console into a vector, containing Complex objects. I have overwritten the >> operator in order to do this, but my vector gets indexed in the in the interval [1..vector.size()] instead of [0..vector.size()). I want it to go from zero, but I can't figure it out.
Here is a minimal reproducible example:
#include <iostream>
#include <vector>
#include <sstream>
using namespace std;
class Complex
{
private:
double real, im;
public:
Complex(double v_real = 0, double v_im = 0): real(v_real), im(v_im) {};
// getters
double getReal() const
{
return this->real;
}
double getImaginary() const
{
return this->im;
}
// setters
void setReal(double v_real)
{
this->real = v_real;
}
void setImaginary(double v_im)
{
this->im = v_im;
}
};
void operator>>(istream& i, Complex& c)
{
string line;
getline(i,line);
istringstream ss (line);
double temp_real;
double temp_im;
char oper;
ss >> temp_real;
ss >> oper;
ss >> temp_im;
if (oper == '-') temp_im *= -1;
char junk_i;
ss >> junk_i;
c.setReal(temp_real);
c.setImaginary(temp_im);
}
ostream& operator<<(ostream& o, Complex c)
{
if (c.getImaginary() > 0)
{
o<<c.getReal()<<'+'<<c.getImaginary()<<'i'<<endl;
}
else
{
o<<c.getReal()<<c.getImaginary()<<'i'<<endl;
}
return o;
}
int main(){
cout << "How many numbers will you create?" << endl;
int num_of_complex;
cin >> num_of_complex;
vector<Complex> v;
Complex temp_c;
for (int i = 0; i < num_of_complex; i++)
{
cin >> temp_c;
v.push_back(temp_c);
}
for (int i = 0; i < v.size(); i++)
{
cout << v[i] << endl;
}
}
Here is the input/output:
The problem is that you're reading the first number with cin >> num_of_complex;, but this does not move the cursor to a new line! This means that the next call (i.e. the first call to your overridden >>) with getline will read only an empty line and try to convert nothing into a complex number.
You can fix this by ignoring everything in the input buffer until the next newline or to the end of the buffer after reading the first number. This is done with the method:
cin.ignore(numeric_limits<streamsize>::max(), '\n');
Full working example:
#include <iostream>
#include <vector>
#include <stream>
#include <limits>
using namespace std;
class Complex
{
private:
double real, im;
public:
Complex(double v_real = 0, double v_im = 0): real(v_real), im(v_im) {};
// Getters.
double getReal() const
{
return this->real;
}
double getImaginary() const
{
return this->im;
}
// Setters.
void setReal(double v_real)
{
this->real = v_real;
}
void setImaginary(double v_im)
{
this->im = v_im;
}
};
istream& operator>>(istream& i, Complex& c)
{
string line;
getline(i, line);
istringstream ss (line);
double temp_real;
double temp_im;
char oper;
ss >> temp_real;
ss >> oper;
ss >> temp_im;
if (oper == '-') temp_im *= -1;
char junk_i;
ss >> junk_i;
c.setReal(temp_real);
c.setImaginary(temp_im);
return i;
}
ostream& operator<<(ostream& o, Complex c)
{
if (c.getImaginary() > 0)
o << c.getReal() << '+' << c.getImaginary() << 'i' << endl;
else
o << c.getReal() << c.getImaginary() << 'i' << endl;
return o;
}
int main()
{
cout << "How many numbers will you create?" << endl;
int num_of_complex;
cin >> num_of_complex;
// Ignore everything in the input buffer until a new line or the end
// of the buffer.
cin.ignore(numeric_limits<streamsize>::max(), '\n');
vector<Complex> v;
Complex temp_c;
for (int i = 0; i < num_of_complex; i++)
{
cin >> temp_c;
v.push_back(temp_c);
}
for (int i = 0; i < v.size(); i++)
{
cout << v[i] << endl;
}
}
but my vector gets indexed in the [1..vector.size()] interval instead of [0..vector.size()). I want it to go from zero, but I can't figure it out.
Subtract 1 from the 1 based index to get the 0 based index that is used by the standard vector. Example:
std::vector<int> v{5, 6, 7};
std::size_t index = 2; // should access value 6
std::cout << v[index - 1];

C++ reading file search for something in that file

So I have a .txt file looking like this:
1:Meat Dish:Steak:11.5
2:Fish Dish:Fish and chips:12
The first number is the itemNo, 'Meat Dish' is my category, 'Steak' is my description and finally '11.5' is my price.
So basically I want to search for the itemNo and I want it to display the price from that line. This is what I have until now:
#include <iostream>
#include <fstream>
#include <string>
#include <vector> // We will use this to store Players
using std::string;
using std::ofstream;
using std::ifstream;
using std::cout;
using std::cin;
struct MenuList // Define a "Player" data structure
{
string itemNo;
string category;
string descript;
double price;
};
std::istream& operator>>(std::istream& infile, MenuList& menu)
{
// In this function we will define how information is inputted into the player struct
// std::cin is derived from the istream class
getline(infile, menu.itemNo, ':');
getline(infile, menu.category, ':');
getline(infile, menu.descript, ':');
infile >> menu.price;
// When we have extracted all of our information, return the stream
return infile;
}
std::ostream& operator<<(std::ostream& os, MenuList& menu)
{
// Just like the istream, we define how a player struct is displayed when using std::cout
os << "" << menu.itemNo << " " << menu.category << " - " << menu.descript;
// When we have extracted all of our information, return the stream
return os;
}
void Load(std::vector<MenuList>& r, string filename)
{
std::ifstream ifs(filename.c_str()); // Open the file name
if(ifs)
{
while(ifs.good()) // While contents are left to be extracted
{
MenuList temp;
ifs >> temp; // Extract record into a temp object
r.push_back(temp); // Copy it to the record database
}
cout << "Read " << r.size() << " records.\n\n";
}
else
{
cout << "Could not open file.\n\n";
}
}
void Read(std::vector<MenuList>& r) // Read record contents
{
for(unsigned int i = 0; i < r.size(); i++)
cout << r[i] << "\n";
}
void Search(std::vector<MenuList>& r) // Search records for name
{
string n;
cout << "Search for: ";
cin >> n;
for(int i = 0; i < r.size(); i++)
{
if(r[i].itemNo.find(n) != string::npos)
cout << r[i];
}
}
int main()
{
std::vector<MenuList> records;
Load(records, "delete.txt");
Read(records);
Search(records);
return 0;
}
I don't really know how to make it so it shows just the price without showing the whole line.
I have written my own code that reads in your text file and stores the information in a struct. Once you have a vector of MenuList, it is dead simple to only print what you want.
include "stdafx.h"
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace std;
struct MenuList // Define a "Player" data structure
{
string itemNo;
string category;
string descript;
double price;
};
void addToList(vector<MenuList*>& vector_menu_list, string& itemNo, string& category, string& descript, string& price)
{
MenuList* p_menu_list = new MenuList;
p_menu_list->itemNo = itemNo;
p_menu_list->category = category;
p_menu_list->descript = descript;
p_menu_list->price = std::stod(price);
vector_menu_list.push_back(p_menu_list);
}
vector<MenuList*> readFile(string filename, bool& error_encountered)
{
vector<MenuList*> vector_menu_list;
ifstream file;
file.open(filename);
if (file.is_open())
{
string itemNo;
string category;
string descript;
string price;
short number_of_colons_encountered = 0;
char c;
while (file.get(c))
{
if ('\n' != c && EOF != c)
{
if (':' == c)
{
number_of_colons_encountered++;
continue;
}
switch (number_of_colons_encountered)
{
case 0:
itemNo += c;
break;
case 1:
category += c;
break;
case 2:
descript += c;
break;
case 3:
price += c;
break;
default:
error_encountered = true;
file.close();
return vector_menu_list;
}
}
else
{
addToList(vector_menu_list, itemNo, category, descript, price);
itemNo.clear();
category.clear();
descript.clear();
price.clear();
number_of_colons_encountered = 0;
}
}
addToList(vector_menu_list, itemNo, category, descript, price);
error_encountered = false;
}
else
{
error_encountered = true;
}
file.close();
return vector_menu_list;
}
int main()
{
bool error_encountered;
vector<MenuList*> p_menu_list = readFile("menu list.txt", error_encountered);
if (true == error_encountered)
{
return -1;
}
cout << "List menu items:" << endl;
for (unsigned long long i = 0; i < p_menu_list.size(); i++)
{
cout << p_menu_list.at(i)->itemNo << " ";
cout << p_menu_list.at(i)->category << " ";
cout << p_menu_list.at(i)->descript << " ";
cout << p_menu_list.at(i)->price << endl;
}
cout << "\n\nHere are only the prices: " << endl;
for (unsigned long long i = 0; i < p_menu_list.size(); i++)
{
cout << p_menu_list.at(i)->price << endl;
}
for (unsigned long long i = 0; i < p_menu_list.size(); i++)
{
delete p_menu_list.at(i);
}
return 0;
}

Reading file to class object

I am having trouble reading a file to my class object's members. It says it cannot read the file.
This is my class:
const int SIZE_OF = 5;
class Student
{
public:
Student();
Student(const Student &);
Student(string, int, int, int, int, int);
friend std::istream& operator >> (std::istream& in, Student& S);
void display();
private:
string lastName;
int grades[SIZE_OF];
};
The cpp file associated with my class object to define the functions:
#include "Student.h"
Student::Student()
{
int i;
string lastName = "default";
for (i = 0; i < 5; i++)
{
grades[i] = 0;
}
}
Student::Student(const Student & S)
{
int i;
lastName = S.lastName;
for (i = 0; i < 5; i++)
{
grades[i] = S.grades[i];
}
}
Student::Student(string S, int a, int b, int c, int d, int e)
{
lastName = S;
grades[0] = a;
grades[1] = b;
grades[2] = c;
grades[3] = d;
grades[4] = e;
}
std::istream& operator >> (std::istream& in, Student& S)
{
char dummy;
in >> S.lastName >> S.grades[0]
>> dummy >> S.grades[1]
>> dummy >> S.grades[2]
>> dummy >> S.grades[3]
>> dummy >> S.grades[4];
return in;
}
void Student::display()
{
int i;
int sum = 0;
double average;
cout << "Last Name: " << lastName << endl;
cout << "Grades: " << endl;
for (i = 0; i < 5; i++)
{
cout << grades[i] << endl;
}
for (i = 0; i < 5; i++)
{
sum = sum + grades[i];
}
average = sum / 5;
cout << "Average: " << average;
}
And finally, the main function that I have so far to test the file opening and reading it to the various variables inside the class.
void main()
{
fstream File;
string FileName = "ProgramSixData.txt";
bool FoundFile;
string Line;
Student testStudent;
do {
File.open(FileName, ios_base::in | ios_base::out);
FoundFile = File.is_open();
if (!FoundFile)
{
cout << "Could not open file named " << FileName << endl;
File.open(FileName, ios_base::out); // try to create it
FoundFile = File.is_open();
if (!FoundFile)
{
cout << "Could not create file named " << FileName << endl;
exit(0);
}
else;
}
else;
} while (!FoundFile);
do {
File >> testStudent;
if (File.fail())
{
cout << "Read Failed" << endl;
cout << "Bye" << endl;
exit(0);
}
else;
testStudent.display();
} while (!File.eof());
cout << "Bye" << endl;
File.close();
}
The text document that I am reading from is the following:
George
75,85,95,100,44
Peter
100,100,100,100,100
Frank
44,55,66,77,88
Alfred
99,88,77,66,55
How do I save each of the names and the associated 5 grades to a particular object of the student class?
You are digging too deep. I made an example solution for you, focusing on the parsing. Things could be way shorter and we could instantly make the students instead of doing it the map way, but I want you to understand how to parse the file, because that is obviously what you are struggling with. Ask me anything about the code if you don't understand it.
void main()
{
string FileName = "ProgramSixData.txt";
bool FoundFile;
string Line;
vector<Student> Students;
ifstream file(FileName); //an ifstream is an INPUTstream (same as a fstream with ::in flag. Passing the FileName as argument opens that file
if (file.fail()) //check if the file opened correctly
{
cout << "Failed to open inputfile\n";
return;
}
map <string, vector<int>> studentAndGrades; //map is a container that uses keys and values, every key has a value, we will use the name of the student as the key to access his marks (a vector of int)
vector<string> allLines;
string line;
while (file >> line) //these 2 linessimply reads ALL the lines to the allLines vector
allLines.push_back(line);
for (size_t i = 0; i < allLines.size(); i += 2) //loop over all lines, by 2 at a time (1 for the students name and 1 for his marks)
{
vector<int> numbers;
size_t lastCommaIdx = 0;
size_t currentCount = 0;
string scores(allLines[i + 1]); //make a copy of allLines[i + 1] for convenient use
bool firstLine = true;
for (size_t i = 0; i < scores.size(); ++i) //following code is just to split the numbers from the comma's and put them in a vector of int
{
if (scores[i] == ',')
{
if (firstLine)
{
numbers.push_back(stoi(scores.substr(lastCommaIdx, currentCount)));
firstLine = false;
}
else
{
numbers.push_back(stoi(scores.substr(lastCommaIdx + 1, currentCount)));
}
lastCommaIdx = i;
currentCount = 0;
}
else
{
++currentCount;
}
}
numbers.push_back(stoi(scores.substr(lastCommaIdx + 1))); //last number
studentAndGrades.insert(make_pair(allLines[i], numbers)); //finally, insert them in the map
}
for (const auto& student : studentAndGrades)
Students.push_back(Student(student.first, student.second[0], student.second[1], student.second[2], student.second[3], student.second[4])); //make students from the information that we read into the map
for (auto& student : Students) //display all students with a range based for loop
student.display();
file.close();
}

How to assign values to a variable when reading in a CSV file?

struct LLGM{
float Lat;
float Long;
};
int main ()
{
string Filename;
int count = 0;
string value;
string temp;
ifstream infile2;
Filename = "LLMGReadingsv2.csv";
infile2.open(Filename);
if(infile2.fail())
{
cout << "Error opening file" << endl;
exit(1);
}
while(!infile2.eof())
{
getline(infile2, temp, ',');
count++;
}
cout << count << endl;
cout << endl;
infile2.close();
ifstream infile;
infile.open(Filename);
LLGM *points;
points = new LLGM [count];
for (int i = 0; i < count; i++)
{
infile >> points[i].Lat;
infile >> points[i].Long;
cout << points[i].Lat;
cout << points[i].Long;
}
cout << endl;
return 0;
}
My question is, how can I assign the values being read in from the CSV file to individual variables?
For Example:
35.123445,-85.888762 (values in one row from the file)
I would like the first number before the comma to be Latitude, and the second value to be Longitude.
Any help would be greatly appreciated!
You can create your own std::ctype facet that will interpret the comma character as the delimiter. Then you can imbue it on your file stream and insert the contents of that stream into the array.
#include <iostream>
#include <locale>
#include <sstream>
struct my_facet : std::ctype<wchar_t>
{
bool do_is(mask m, char_type c) const
{
if ((m & space) && c == L' ') {
return false;
}
if ((m & space) && c == L',')
{
return true;
}
return ctype::do_is(m, c);
}
};
int main()
{
std::wifstream infile(Filename);
infile.imbue(std::locale(infile.getloc(), new my_facet));
for (int i = 0; i < count; ++i)
{
if ((infile >> points[i].Lat) && (infile >> points[i].Long))
{
std::wcout << points[i].Lat;
std::wcout << points[i].Long;
}
}
}
Here is a demo that uses a stringstream instead of a file (for demonstrating purposes only).

Operator Overloading C++; too many parameters for << operation

I have some code below that will take some names and ages and do some stuff with them. Eventually it will print them out. I need to change my print() function with a global operator<<. I saw on a different forum that <<operator takes two parameters, but when I try it I get a "too many parameters for << operation error. Is there something I am doing wrong? I am newer to C++ and I really do not get the point of operator overloading.
#include <iostream>;
#include <string>;
#include <vector>;
#include <string.h>;
#include <fstream>;
#include <algorithm>;
using namespace::std;
class Name_Pairs{
vector<string> names;
vector<double> ages;
public:
void read_Names(/*string file*/){
ifstream stream;
string name;
//Open new file
stream.open("names.txt");
//Read file
while(getline(stream, name)){
//Push
names.push_back(name);
}
//Close
stream.close();
}
void read_Ages(){
double age;
//Prompt user for each age
for(int x = 0; x < names.size(); x++)
{
cout << "How old is " + names[x] + "? ";
cin >> age;
cout<<endl;
//Push
ages.push_back(age);
}
}
bool sortNames(){
int size = names.size();
string tName;
//Somethine went wrong
if(size < 1) return false;
//Temp
vector<string> temp = names;
vector<double> tempA = ages;
//Sort Names
sort(names.begin(), names.end());
//High on performance, but ok for small amounts of data
for (int x = 0; x < size; x++){
tName = names[x];
for (int y = 0; y < size; y++){
//If the names are the same, then swap
if (temp[y] == names[x]){
ages[x] = tempA[y];
}
}
}
}
void print(){
for(int x = 0; x < names.size(); x++){
cout << names[x] << " " << ages[x] << endl;
}
}
ostream& operator<<(ostream& out, int x){
return out << names[x] << " " << ages[x] <<endl;
}
};
You are overloading << operator as a member function, therefore, the first parameter is implicitly the calling object.
You should either overload it as friend function or as a free function. For example:
overloading as friend function.
friend ostream& operator<<(ostream& out, int x){
out << names[x] << " " << ages[x] <<endl;
return out;
}
However, the canonical way is to overload it as free function. You can find very good information from this post: C++ operator overloading
declare operator overloading function as friend.
friend ostream& operator<<(ostream& out, int x)
{
out << names[x] << " " << ages[x] <<endl;
return out;
}