String is not being directed to Output file (C++) - c++

So I'm trying to direct the String that I get from a function to an output file. What's happening is that the buildTree function is creating a binary tree of vector strings and then after it has finished, it calls the printInorder function to print the function in order. It will correctly print out the tree if I replace file << tree.printVector(tempRoot->data); with cout << tree.printVector(tempRoot->data); But trying to direct the returned string from tree.printVector() doesn't seem to do anything. The file.txt is still blank after run. Thanks for the help
here's the code
#include <iostream>
#include "node.h"
#include "tree.h"
#include "cleanString.h"
#include <string>
#include <fstream>
using std::cout;
using std::endl;
BST tree, *root = nullptr;
void buildTree(std::string name){
ifstream file;
file.open(name);
if (file){
std::string word;
file >> word;
word = cleanString(word);
root = tree.Insert(root, word);
while (file >> word){
word = cleanString(word);
tree.Insert(root, word);
}
//tree.Inorder(root);
printInorder(root);
} else{
cout << "not a file" << endl;
}
file.close();
}
void printInorder(BST* tempRoot){
ofstream file;
std::string vecStrings;
file.open("file.txt");
if (!tempRoot) {
return;
}
printInorder(tempRoot->left);
vecStrings = tree.printVector(tempRoot->data);
file << vecStrings << endl; // the issue here: wont put string in file.txt
cout << vecStrings << endl; // but this will print
printInorder(tempRoot->right);
file.close();
}
void printPreorder(){
}
void printPostorder(){
}

Rather than a method that opens and closes the file, how about writing:
std::ostream &operator<<(std::ostream &str, const BST &) {
...
}
Then you have a generic print method that you can send to any output stream.

Related

Overloaded constructor implementing private class members

In this code I have an overloaded constructor Record::Record(string s)that reads in a string, I am trying to create a string stream from 's' and use getline(stringStream, line, ",") to read each element from the string with "," as the delimiter, then assign each element to the appropriate object variable. The end goal of this assignment is to open a file, read in its data, assign the data appropriately in a vector, then write and parse the data to a new file.
My understanding of working with private class members is limited. I am unsure how to go about writing the constructor. In the past I've used a pointer for private members (e.g 'this-> foo;), at this point I just need to understand how to implement the contents of Record, so far what I've tried has been incorrect and I can only find references to using pointers to int's.
Normally I would go to my comp-sci lab and ask a TA but it is currently close due to COVID.
Here is the code for my constuctors and overloaded operators.
Record.cpp
#include <string>
#include <sstream>
#include "Record.h"
using namespace std;
Record::Record(string s) {
/** here is where I need to assign data to the following.
std::string department;
std::string item_code;
int quantity;
double cost; **/
}
Record::~Record() {
// TODO Auto-generated destructor stub
}
//overloaded "==" and "<" comparison operators
bool operator ==(const Record &lhs, const Record &rhs){
return (lhs.cost == rhs.cost && lhs.department == rhs.department &&
lhs.item_code == rhs.item_code && lhs.quantity == rhs.quantity);
}
/**bool operator <(const Record &a, const Record &b){ //do not need at this time
}
**/
//Overloaded "<<" operator
std::ostream& operator <<(std::ostream& os, const Record& r){
os << r.department << ',' << r.item_code << ',' << r.quantity << ',' << r.cost;
return os;
}
Record.h
#ifndef RECORD_H_
#define RECORD_H_
#include <iostream>
#include <string>
class Record {
public:
//Constructor
Record(std::string s); //pass this string to our Record class
//De-constructor
virtual ~Record();
//overloaded "==" and "<" comparison operators
friend bool operator ==(const Record &a, const Record &b);
//friend bool operator <(const Record &a, const Record &b); //Do not need at this time.
//Overloaded "<<" operator
friend std::ostream& operator <<(std::ostream&, const Record&);
private:
std::string department;
std::string item_code;
int quantity;
double cost;
};
#endif /* RECORD_H_ */
Main.cpp
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string>
#include <libgen.h>
#include <fstream>
#include <sstream>
#include <vector>
#include <algorithm>
#include "Record.h"
using namespace std;
int main() {
vector<Record> records; //vector of type Records to hold each "Line" of input file
string filename; // File name and path stored as string
/**
* Prompts user for the name of input file and stores in string variable filename
*
*/
cout << "please enter the name of your file with file path:" << endl;
cin >> filename;
ifstream ifs { filename.c_str() };
if (!ifs) {
cerr << " Can't open file " << filename << endl;
return 1;
}
string path = filename.substr(0, filename.find_last_of("\\/"));
string file = filename.substr(filename.find_last_of("\\/") + 1,
filename.length());
if (path.compare(file) == 0) {
path == "";
}
//test for file and file path
cout << "Path portion of " << filename << " is " << path << endl;
cout << "File portion of " << filename << " is " << file << endl; // path + "new_" + file + ".cvs", make new file with new path
/**
* Put each line of input file in to the records vector
*/
string line; //strings for each parameter of the vector object
while (getline(ifs, line)) {
Record newRecord(line); //this should check for duplicates and ignore any found.
int i = 0;
int n = 0;
if((newRecord == records[i]) || (i < n) ){
i++;
}
else{
records.push_back(newRecord);
n++;
i = 0;
}
}
ifs.close(); //closes the stream
//create new file and output data to it
string newFile = ("new_" + file + ".cvs");
//check to see if file path and file name are correct
cout << (path + newFile);
//Open stream to new file and write to it
ofstream ofs(path + newFile);
ofs.open(newFile);
for(size_t i = 0; i < records.size(); i++){
ofs << (i+1) << ' ' << records[i];
}
ofs.close(); //close output stream
return 0;
}
You can do something like:
Record::Record(std::string s){
std::string word;
std::vector<std::string> temp;
std::stringstream ss(s);
while(getline(ss, word, ','))
temp.push_back(word);
department = temp.at(0);
item_code = temp.at(1);
quantity = stoi(temp.at(2));
cost = stod(temp.at(3));
}
I'm assuming you mean that each parmeter is separated by ',', not each line, if that's not the case, say something.
Edit
So there are a couple of issues in your main function, namely, the while getline cycle will probably have out_of_range access, you can use a range-based for loop, which avoids container overflow:
while (getline(ifs, line))
{
bool flag = 1;
Record newRecord(line);
for(Record r : records){
if(r == newRecord){
flag = 0;
break;
}
}
if(flag)
records.push_back(newRecord);
}
The ofstream file is not being properly opened and tested, you can do something like:
ofstream ofs;
ofs.open(path + newFile);
if (!ofs)
{
cerr << "Can't open file " << filename << endl;
return 1;
}
for (size_t i = 0; i < records.size(); i++)
{
ofs << (i + 1) << ' ' << records[i] << endl;
}
This line:
path == "";
I'm sure you meant:
path = "";
Running sample
One last note, using namespace std is not a very good practice.

parsing a file using getline() into a new file, while ignoring duplicate entries

I'm trying to write a program that prompts a user for a file and its file path and then reads the file. It then takes the text in the file and stores it into a vector after parsing the data and separating it into a few categories (department, item code, quantity, and cost).
I'm having a few issues knowing how to use getline() correctly, and I'm unsure of a few aspects of it. Overall I'm trying to understand how to assign each element to the appropriate object variable, currently I'm lacking in experience with doing this sort of thing as well as using OOP.
The text contained in the file will look like the following.
21 Music 64679-701 487 28.77
22 Outdoors 63739-141 195 83.23
23 Books 0268-1154 976 65.17
After I am able to get that portion working correctly I'll be working on checking for duplicates and ignoring any that are found.
main.cpp
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string>
#include <libgen.h>
#include <fstream>
#include <sstream>
#include <vector>
#include <algorithm>
#include "Record.h"
using namespace std;
int main() {
vector<Record> records; //vector of type Records to hold each "Line" of input file
string filename; // File name and path stored as string
/**
* Prompts user for the name of input file and stores in string variable filename
*
*/
cout << "please enter the name of your file with file path:" << endl;
cin >> filename;
ifstream ifs { filename.c_str() };
if (!ifs) {
cerr << " Can't open file " << filename << endl;
return 1;
}
string path = filename.substr(0, filename.find_last_of("\\/"));
string file = filename.substr(filename.find_last_of("\\/") + 1,
filename.length());
if (path.compare(file) == 0) {
path == "";
}
//test for file and file path
cout << "Path portion of " << filename << " is " << path << endl; //
cout << "File portion of " << filename << " is " << file << endl; // path + "new_" + file + ".cvs", make new file with new path
/**
* Put each line of input file in to the records vector
*/
string line; //strings for each parameter of the vector object
while (getline(ifs, line)) {
Record newRecord(line);
//Here is where I'm having trouble with using get line to parse and store the information.
//It is incorrect at the moment, and I have the start of how I think I should be going about it.
// check if this record exists in the vector, if not add, else ignore
records.push_back(newRecord);
}
ifs.close(); //closes the stream
return 0;
}
Record.h
#ifndef RECORD_H_
#define RECORD_H_
#include <iostream>
#include <string>
class Record {
public:
//Constructor
Record(std::string s); //pass this string to our Record class
//De-constructor
virtual ~Record();
//overloaded "==" and "<" comparison operators
friend bool operator ==(const Record &a, const Record &b);
//friend bool operator <(const Record &a, const Record &b); //Do not need at this time.
//Overloaded "<<" operator
friend std::ostream& operator <<(std::ostream&, const Record&);
private:
std::string department;
std::string item_code;
int quantity;
double cost;
};
#endif /* RECORD_H_ */
Record.cpp
#include <string>
#include "Record.h"
using namespace std;
Record::Record(string s) {
/**
* Create a string stream from 's' and use getline(stringStream, line, ",") to
* read each element from the string using the "," as the delimiter. Assign
* each element to the appropriate object variable
*/
//getline(s, line.cost, line.department, line.item_code, line.quantity, ",");
}
Record::~Record() {
// TODO Auto-generated destructor stub
}
//overloaded "==" and "<" comparison operators
bool operator ==(const Record &lhs, const Record &rhs){
return (lhs.cost == rhs.cost && lhs.department == rhs.department &&
lhs.item_code == rhs.item_code && lhs.quantity == rhs.quantity);
}
/**bool operator <(const Record &a, const Record &b){ //do not need at this time
}
**/
//Overloaded "<<" operator
std::ostream& operator <<(std::ostream& os, const Record& r){
os << r.department << ',' << r.item_code << ',' << r.quantity << ',' << r.cost;
return os;
}

create multiple text files inside a loop

I want to create some text file in C++. For example: I will run a loop from 1 to 5 and create the following files:
1.txt
2.txt
3.txt
4.txt
5.txt
is it possible? I have made a sample code:
#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;
main()
{
FILE *fp;
int i;
for(i=1;i<=5;i++)
{
//fp=fopen("%d.txt","r",i); //what will go here??
}
}
I am confused about what I will write inside the loop. how can I create those files?
char i;
char fileName[] = "0.txt";
for(i='1';i<='5';i++)
{
fileName[0]=i;
fp=fopen(fileName,"r"); //what will go here??
//...
}
You can use sprintf if this is too simple for your case;
Since you tag c++, I think fstream string is the thing to use.
A simple c++ example
#include <fstream>
#include <string>
using namespace std;
int main(){
string base(".txt");
for(int i=1;i<=5;++i){
ofstream(to_string(i)+base);// to_string() need c++11
}
}
If you still don't have to_string (you don't have c++11 or your compiler just don't have this) you can use this simple version for now. (better put this in your own namespace)
#include <string>
#include <sstream>
std::string to_string(int i){
std::stringstream s;
s << i;
return s.str();
}
You can use a std::stringstream to compose the file name before passing it to the std::ofstream constructor as a std::string.
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <iomanip>
int main()
{
std::cout << "How many files do you want to create? ";
int n;
std::cin >> n;
std::cout << "How many digits do you want to display? ";
int n_digits;
std::cin >> n_digits; // i.e. zeroes == 3 -> 001.txt
std::cout << "Enter a common prefix for all the files: ";
std::string prefix;
std::cin.ignore();
std::getline(std::cin, prefix); // i.e. prefix == "file" -> file001.txt
std::string ext(".txt");
for ( int i = 1; i <= n; ++i )
{ // use a stringstream to create a file names like: prefix001.txt
std::stringstream ss;
ss << prefix << std::setfill('0') << std::setw(n_digits) << i << ext;
// open the file. If not c++11 use ss.str().c_str() instead
std::ofstream file( ss.str() );
if ( !file )
{
std::cerr << "Error: failed to create file " << ss.str() << '\n';
break;
}
// write something to the newly created file
file << "This is file: " << ss.str() << "\n\nHello!\n";
if ( !file )
{
std::cerr << "Error: failed to write to file " << ss.str() << '\n';
break;
}
}
}
#include <iostream>
#include <fstream>
int main(void)
{
std::ofstream out; // you must call out.close() inside loop to be able to open another file for writting otherwise you'll get only the first one "a.txt"
std::string sFileName;
for(char c('a'); c < 'f'; c++)
{
sFileName = c;
sFileName += ".txt";
out.open(sFileName.c_str(), std::ios::out);
// std::ofstream out(sFileName.c_str(), std::ios::out); // here you are not obliged to call out.close() because the first out is not the very second and so on...
out.close(); // very important if you use the same ofstream to open another file
}
std::cout << std::endl;
return 0;
}
*** to be able to use one ostream object in opening many files you must close the precedent file to be able to open the next otherwise it fails trying creating the next one.

how do i use ignore(), sync(), or clear() to clear out any data before using get line()?

I am using a function to read strings from a normalized file in order to push each string to the top of a stack that is being implemented dynamically using a linked list.
the normalized file is using data like this:
racecar
rotator
rotor
civic
when i call my function i expect my output to look the same after it uses get line to push each string to the stack, however, the actual out put is erasing the first character in the first string in a manner that looks like this:
acecar
rotator
rotor
civic
i have tried to use the ignore(), sync(), and clear functions before each iteration of get line() but i still keep coming up with the same problem.
i need some real help with this one and i hope one of you code masters can help me find the solution since i have yet to be able to get ignore() and the rest of them to ever work correctly!!! :/
here is the code to my function:
void createStack(fstream &normFile, ostream &outFile)
{
string catchNewString;
do
{
DynStrStk stringStk;
getline(normFile,catchNewString,'\n');
stringStk.push(catchNewString);
//tracer rounds
cout << endl;
outFile << catchNewString << endl;
cout << "test: " << catchNewString << endl;
} while(!normFile.eof());
}
this is the entirety of my main function:
//
#include <iostream>
#include <fstream>
#include <ostream>
#include <sstream>
#include <string>
#include "DynStrStk.h"
using namespace std;
void processFile();
void parseFile(ifstream&, fstream&);
void createStack(fstream&, ostream&);
int main()
{
//call function to open file and process
cout << "processing file" << endl;
processFile();
return 0;
}
void processFile()
{
ifstream inFile;
fstream normFile;
ofstream outFile;
cout << "opening files" << endl;
// open files
inFile.open("inFile.txt");
normFile.open("normFile.txt");
outFile.open("outFile.txt");
cout << "parsing file" << endl;
//parse file for capitalization & punctuation
parseFile(inFile, normFile);
//create stack with parsed and normalized normFile
createStack(normFile, outFile);
//close files
outFile.close();
normFile.close();
inFile.close();
}
void parseFile(ifstream &inFile, fstream &normFile)
{
//create and initialize variables
string newString;;
int i;
if(!inFile)
{
cout << "ERROR!!! Cannot read file.";
}
else
{
do
{
//read each line in the input file until EOF
getline(inFile, newString, '\n');
i = 0;
//parse each string for punctuation
while(newString[i])
{
if(isalpha(newString[i])) //check each char in each
//string for punctuation
{
if(isupper(newString[i])) //check each string for
//capitalization
{
newString[i] = tolower(newString[i]); //convert
//string to lower case
}
normFile << newString[i]; //output each line tofile
cout << newString[i];
}
i++;
}
normFile << '\n';
cout << '\n';
} while(!inFile.eof());
}
}
void createStack(fstream &normFile, ostream &outFile)
{
string catchNewString;
do
{
DynStrStk stringStk;
getline(normFile,catchNewString,'\n');
stringStk.push(catchNewString);
//tracer rounds
cout << endl;
outFile << catchNewString << endl;
cout << "test: " << catchNewString << endl;
} while(!normFile.eof());
}
this is my push function in my header file:
//function that pushes the argument onto the list
void DynStrStk::push(string newString)
{
StackNode *newNode = nullptr; //Pointer to a node
//Allocate a new node and store string
newNode = new StackNode;
newNode->newString = newString;
//if there are no nodes in the list make newNode the first node
if(isEmpty())
{
top = newNode;
newNode->next = nullptr;
}
else //otherwise insert NewNode before top
{
newNode->next = top;
top = newNode;
}
}
this is my header file:
#ifndef DynStrStk_h
#define DynStrStk_h
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
class DynStrStk
{
private:
//struct stack nodes
struct StackNode
{
string newString; //string held in the node
StackNode *next; //pointer to the next node
};
StackNode *top; //pointer to the top of the stack
public:
//Constructor
DynStrStk()
{top = nullptr;}
//Destructor
~DynStrStk();
//Stack Operations
void push(string);
void pop(string &);
bool isEmpty();
};
#endif
Your code is almost correct, with couple of exceptions.
you should not test for eof as a loop condition, see Why is iostream::eof inside a loop condition considered wrong?
Declaring DynStrStk stringStk; inside the loop seems very fishy, as at the exit from the loop the variable cease to exist.
Here is a very simple program that uses a std::stack<std::string> instead, and which works, so the problem is probably in your DynStrStk implementation. There is no need to use cin.clear or cin.ignore as long as you're not mixing up cin and getline (in this latter case cin leaves a new line in the buffer which may end up "eaten" by `getline" in case you don't clear the stream).
#include <iostream>
#include <stack>
#include <string>
#include <fstream>
int main()
{
std::stack<std::string> stringStk; // stack of strings
std::ifstream normFile("test.txt"); // input file
std::ofstream output("out.txt"); // output file
std::string catchNewString;
while(getline(normFile,catchNewString)) // read and push to stack
{
stringStk.push(catchNewString); // push to stack
}
while(!stringStk.empty()) // dump the stack into the file
{
output << stringStk.top() << std::endl; // write it
stringStk.pop(); // remove
}
}
Again, you are not losing anything with the getline, nothing is lost in the buffer. There is an error probably when you loop over the individual characters in the string, in void parseFile(ifstream &inFile, fstream &normFile). One more thing: you use parseFile(inFile, normFile); then immediately createStack(normFile, outFile);. However, the normFile will have its position at the end (this is done by parseFile()), don't you need to rewind it before invoking createStack()?
try normFile.seekg(0, ios::beg); adter parseFile() and before createStack(). If it's not working, then try moving as much code as possible in independent functions and test each function step by step, or use a debugger and see live what's going on.

List Iterator Not incrementable - Runtime Error

I am getting a List Iterator Not incrementable on the code below after adding a stringstream for movieName and am unsure as how to go about solving the error. I am trying to read in from a file and add the items into a list then itterate through the list and add eash word from the title of the movie into another list. Any help would be appreciated!
#include <iostream>
#include <string>
#include <list>
#include <fstream>
#include <sstream>
using namespace std;
int main()
{
list <string> movieList;
list <string>::iterator iterMovie;
list <string> *titleWordList = new list <string>;
list <string>::iterator iterTitleWord;
ifstream inFile;
inFile.open("JamesBond.txt");
if (!inFile)
{
cout << "Could not find the specified file.";
}
else
{
movieList.clear();
string movieName;
while(getline(inFile,movieName))
{
movieList.push_back((movieName));
/*
for each(string movieName in movieList)
{
titleWordList->push_back(movieName);
}
*/
stringstream ss(movieName);
while (ss >> movieName)
{
titleWordList->push_back(movieName);
}
}
}
if (movieList.empty())
{
cout << "No Data Found!" << endl;
}
else
{
cout << "Writing Output: \n\n";
for (iterMovie=movieList.begin(); iterMovie !=movieList.end(); ++iterMovie)
{
cout << *iterMovie << endl;
}
for (iterTitleWord=movieList.begin(); iterTitleWord != movieList.end(); ++iterMovie)
{
cout << *iterTitleWord << endl;
cout << &titleWordList;
}
}
}
In the second for there is a copy paste error, it should be ++iterTitleWord instead of ++iterMovie.
Also, as #Greg suggested, iterTitleWord=movieList.begin() and iterTitleWord != movieList.end() should be iterTitleWord=titleWordList->begin() and iterTitleWord != titleWordList->end() respectively.