How to read a stream with comma separated values in C++? - c++

I would like to emphasize on a fundamental question below:
Assume you have a CSV file and fill cells with Input Headers
The code should read this from .csv file and write the results into .csv file. It is nice to also code output total number of cases, plus average of cases. Here is a taken sample form SO and I would like to see how this can be efficiently adopted to complete this basic example.
void create()
{
// file pointer
fstream fout;
// opens an existing csv file or creates a new file.
fout.open("reportcard.csv", ios::out | ios::app);
cout << "Enter the details of 5 students:"
<< " roll name maths phy chem bio";
<< endl;
int i, roll, phy, chem, math, bio;
string name;
// Read the input
for (i = 0; i < 5; i++) {
cin >> roll
>> name
>> math
>> phy
>> chem
>> bio;
// Insert the data to file
fout << roll << ", "
<< name << ", "
<< math << ", "
<< phy << ", "
<< chem << ", "
<< bio
<< "\n";
}
}
Also, Read a particular record
void read_record()
{
// File pointer
fstream fin;
// Open an existing file
fin.open("reportcard.csv", ios::in);
// Get the roll number
// of which the data is required
int rollnum, roll2, count = 0;
cout << "Enter the roll number "
<< "of the student to display details: ";
cin >> rollnum;
// Read the Data from the file
// as String Vector
vector<string> row;
string line, word, temp;
while (fin >> temp) {
row.clear();
// read an entire row and
// store it in a string variable 'line'
getline(fin, line);
// used for breaking words
stringstream s(line);
// read every column data of a row and
// store it in a string variable, 'word'
while (getline(s, word, ', ')) {
// add all the column data
// of a row to a vector
row.push_back(word);
}
// convert string to integer for comparision
roll2 = stoi(row[0]);
// Compare the roll number
if (roll2 == rollnum) {
// Print the found data
count = 1;
cout << "Details of Roll " << row[0] << " : \n";
cout << "Name: " << row[1] << "\n";
cout << "Maths: " << row[2] << "\n";
cout << "Physics: " << row[3] << "\n";
cout << "Chemistry: " << row[4] << "\n";
cout << "Biology: " << row[5] << "\n";
break;
}
}
if (count == 0)
cout << "Record not found\n";
}
[1]: https://i.stack.imgur.com/q6VfZ.png

I've mainly concentrated on adding overloads for operator<< and operator>> since you showed some interest in those earlier and describe what they are doing in comments in the code.
Since you are mixing input and output from streams that are comma separated and other streams I've added an adapter for CSV streaming as well as overloads for streaming to/from a user.
First, create a class to keep all data that belongs together in one data record. I've made it a simple struct here, which is a class with public access to its members per default.
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
// your student record
struct student {
std::string name; // It's usually good to have larger types first so name goes first
int roll;
int math;
int phy;
int chem;
int bio;
};
// read a student from an istream (like std::cin) - whitespace separated
std::istream& operator>>(std::istream& is, student& s) {
return is >> s.roll >> s.name >> s.math >> s.phy >> s.chem >> s.bio;
}
// write a student to an ostream (like std::cout)
std::ostream& operator<<(std::ostream& os, const student& s) {
return os << "Details of Roll " << s.roll << ":\n"
<< "Name: " << s.name << '\n'
<< "Maths: " << s.math << '\n'
<< "Physics: " << s.phy << '\n'
<< "Chemistry: " << s.chem << '\n'
<< "Biology: " << s.bio << '\n';
}
//--------------------------------------------------------------------------------------
// An adapter for comma separated streaming
struct CSVStudent {
CSVStudent(student& s) : stud(s) {}
CSVStudent(const CSVStudent&) = delete;
// The CSVStudent holds a reference to a "student"
student& stud;
};
// read a record from an istream - comma separated
std::istream& operator>>(std::istream& is, CSVStudent& csvstud) {
std::string line;
student& s = csvstud.stud; // an alias to the student to have to type less
if(std::getline(is, line)) { // read a complete line
// put the line in an istringstream for extraction:
std::istringstream ss(line);
char delim; // a dummy for reading commas
// Extract the comma separated values. "delim" is not checked so it could be
// any char breaking up the int:s.
//
// The below does things in the following order:
// 1. "ss >> s.roll >> delim"
// This extracts roll and a comma and returns
// a reference to ss, which is used in 2.
// 2. std::getline(ss, s.name, ',')
// Extracts a string until a comma is encountered.
// 3. Normal extraction for the rest of the int:s with the
// dummy variable "delim" where the commas are supposed to be.
if(not(std::getline(ss >> s.roll >> delim, s.name, ',') >> s.math >> delim >>
s.phy >> delim >> s.chem >> delim >> s.bio)) {
// If we get here, the extraction from the istringstream failed, so set
// the failstate on the istream too. Note the "not" on the line above.
is.setstate(std::ios::failbit);
}
}
return is;
}
// write a record to an ostream - comma separated
std::ostream& operator<<(std::ostream& os, const CSVStudent& csvstud) {
const student& s = csvstud.stud;
os << s.roll << ',' << s.name << ',' << s.math << ',' << s.phy << ',' << s.chem
<< ',' << s.bio << '\n';
return os;
}
//--------------------------------------------------------------------------------------
// get all students in the file as a std::vector<student>
std::vector<student> read_student_file(const std::string& filename) {
std::vector<student> retval;
std::ifstream fin(filename);
if(fin) { // file opened successfully
student stud;
CSVStudent csvstud{stud}; // holds a reference to stud
// loop for as long as student records can be read successfully
while(fin >> csvstud) // use the csv sdapter
retval.push_back(stud); // and put the stud in the vector
}
return retval;
}
//--------------------------------------------------------------------------------------
void create(const std::string& filename) {
// open an existing csv file or creates a new file.
std::ofstream fout(filename, std::ios::out | std::ios::app);
if(fout) {
std::cout << "Enter the details of 5 students:"
" roll name maths phy chem bio\n";
// Read the input
for(int i = 0; i < 5; i++) {
student stud;
std::cout << (i + 1) << ": ";
if(std::cin >> stud) {
// Insert the data to file if one was entered successfully
fout << CSVStudent(stud); // uses the adapters operator<<
} else {
std::cerr << "You failed to enter data for student " << (i + 1) << '\n';
break;
}
}
}
}
//--------------------------------------------------------------------------------------
int main() {
std::string filename = "reportcard.csv";
std::vector<student> students = read_student_file(filename);
std::cout << "There are " << students.size() << " students in the file.\n";
if(not students.empty()) {
// show the last record if there are any records in the file
std::cout << "Record " << students.size() << " is:\n\n";
std::cout << students.back() << '\n';
}
// create 5 new records
create(filename);
}
If reportcard.csv contains this:
1,Ted,1,2,3,4
2,Foo,2,3,4,5
3,Bar,3,4,5,6
4,Baz,4,5,6,7
5,Bork,5,6,7,8
The program should start up like this:
There are 5 students in the file.
Record 5 is:
Details of Roll 5:
Name: Bork
Maths: 5
Physics: 6
Chemistry: 7
Biology: 8
Enter the details of 5 students: roll name maths phy chem bio
1: <and here is where you're supposed to enter the first of 5 new students>

Related

Can anybody tell me what I did wrong (cpp) formatting with getline

trying to format with c++ getline function. The output puts everything at the first record number forename instead of where it should go.
Code:
#include <fstream>
#include <string>
#include <iostream>
using namespace std;
int main()
{
const int RANGE = 12;
string tab[RANGE];
int i = 0, j = 0;
ifstream reader("records.txt");
if (!reader)
{
cout << "Error opening input file" << endl;
return -1;
}
while (!reader.eof())
{
if ( ( i + 1) % 4 == 0)
getline( reader, tab[i++], '\n');
else
getline( reader, tab[i++], '\t');
}
reader.close();
i = 0;
while (i < RANGE)
{
cout << endl << "Record Number: " << ++j << endl;
cout << "Forename: " << tab[i++] << endl;
cout << "Surname: " << tab[i++] << endl;
cout << "Department: " << tab[i++] << endl;
cout << "Telephone: " << tab[i++] << endl;
}
return 0;
}
Contents of TXT file:
John Smith Sales 555-1234
Mary Jones Wages 555-9876
Paul Harris Accts 555-4321
Please run the code for yourself to understand what happens and put the txt file in the same folder as your code.
Hope someone can help me thanks.
See Why is iostream::eof inside a loop condition (i.e. while (!stream.eof())) considered wrong?.
Also, your final while loop should only output the strings that were actually read into the array, not the full array, if the file has less than 12 strings. But unless you can guarantee that your file never exceeds 12 strings, you should use std::vector instead of a fixed array.
Also, instead of alternating the getline() delimiter in a single loop, I would just use an outer loop to read whole lines only, and then separately read tab-delimited values from each line. And then store the values in an array/vector of struct instead of individually.
Try something more like this:
#include <fstream>
#include <sstream>
#include <string>
#include <iostream>
#include <vector>
using namespace std;
struct Person
{
string foreName;
string surName;
string department;
string phoneNumber;
};
int main()
{
ifstream reader("records.txt");
if (!reader)
{
cout << "Error opening input file" << endl;
return -1;
}
vector<Person> people;
string line;
while (getline(reader, line))
{
istringstream iss(line);
Person p;
getline(iss, p.foreName, '\t');
getline(iss, p.surName, '\t');
getline(iss, p.department, '\t');
getline(iss, p.phoneNumber, '\t');
people.push_back(p);
}
reader.close();
int j = 0;
for (Person &p : people)
{
cout << endl << "Record Number: " << ++j << endl;
cout << "Forename: " << p.foreName << endl;
cout << "Surname: " << p.surName << endl;
cout << "Department: " << p.department << endl;
cout << "Telephone: " << p.phoneNumber << endl;
}
return 0;
}
There are easier ways to separate words in an istream, namely C++ sring stream tools:
#include <fstream>
#include <iostream>
#include <sstream> //<-- string stream library
using namespace std; //<-- should not be used, use scope std::
int main() {
const int RANGE = 12;
string tab[RANGE];
string temp; //<--to store each field temporarily
int i = 0, j = 0;
ifstream reader("records.txt");
if (!reader) {
cout << "Error opening input file" << endl;
return -1;
}
while (getline(reader, temp)) { //<-- read one full line
stringstream ss(temp); // <-- input to a string stream
while(ss >> tab[i]){ // <-- passing strings to the string array one by one
i++;
}
}
reader.close();
i = 0;
while (i < RANGE) {
cout << endl << "Record Number: " << ++j << endl;
cout << "Forename: " << tab[i++] << endl;
cout << "Surname: " << tab[i++] << endl;
cout << "Department: " << tab[i++] << endl;
cout << "Telephone: " << tab[i++] << endl;
}
return 0;
}
The idea here was to mess as little as possible with your code, one thing I would advise is to use std::vector instead of normal fixed size arrays. Also, as it was said and linked, eof is very unreliable.
The source of your problem, I think, is explained in Why is iostream::eof inside a loop condition (i.e. `while (!stream.eof())`) considered wrong?.
You are reading into tab[12], tab[13], tab[13], and tab[14] due to that error. Of course, that leads to undefined behavior.
Change the loop to:
// Read the contents of the file line by line
std::string line;
while (getline( reader, line))
{
// Process each line's contents.
std::istringstream str(line);
getline(str, tab[i++], '\t');
getline(str, tab[i++], '\t');
getline(str, tab[i++], '\t');
getline(str, tab[i++], '\n');
}
Make sure to add
#include <sstream>
To be doubly sure that you are not using the array using out of bounds indices, add a check.
while ( i+4 < RANGE && getline( reader, line))
{
...
}
First, while (!reader.eof()) is not doing the right thing.
The immediate problem you see is caused by the fact that your file does not contain '\t', hence already the very first getline reads all the contents of the file into tab[0]. (At least thats what I got after 1-to-1 copying your file contents)
Your code is rather difficult, because you declare variables long before you use them and later reuse them. You have a fixed size array, but when there are more lines in the file your code will just crash. Also reading everything into a plain array of strings is making things complicated. Accessing forename or other fields requires you to compute the offset into the array. Better use a data structure:
struct file_entry {
std::string first_name;
std::string last_name;
std::string departure;
std::string phone;
};
Then you can define an input operator:
std::istream& operator>>(std::istream& in,file_entry& fe) {
return in >> fe.first_name >> fe.last_name >> fe.departure >> fe.phone;
};
And use a std::vector to store as many entries as there are in the file:
int main() {
std::string contents{"John Smith Sales 555-1234\n"
"Mary Jones Wages 555-9876\n"
"Paul Harris Accts 555-4321\n"};
std::stringstream reader{contents};
std::vector<file_entry> data;
std::string line;
while (std::getline(reader,line)) {
file_entry fe;
std::stringstream{line} >> fe;
data.push_back(fe);
}
for (const auto& fe : data) {
std::cout << "Forename: " << fe.first_name << '\n';
std::cout << "Surname: " << fe.last_name << '\n';
std::cout << "Department: " << fe.departure << '\n';
std::cout << "Telephone: " << fe.phone << '\n';
}
}
live example
PS you do not need to call close on the file, this is already done in its destructor. Not calling it explicitly has the benefit that the same code that works for a file stream also works for a stringstream.

Unable to populate array of structures

my program is supposed to parse through strings from a file and store in the array of structs.
Example: Skyfall, 1.109, Sam Mendes, 11/9/12, 143. Program will parse the string and store the title, gross, director name etc.
Whenever i run the code it doesn't seem to store it properly.
Also, I'm getting this error.
terminate called after throwing an instance of 'std::logic_error'
what(): basic_string::_M_construct null not valid
Here's my struct:
struct Movie
{
string Title; // Movie title
string Gross; // Gross total in billion dollars
string Director; // Director name
string Date; // Release date
string Runtime; // Runtime in minutes
};
This function is to create the array of objects and open the file
Movie* createDatabase(int& number_Of_Lines)
{
// input file
ifstream movie_file;
string filename;
do
{
cout << "Please enter filename: " ;
getline (cin , filename);
movie_file.open(filename.c_str());
if(movie_file.fail())
cout << "Invalid file" << endl ;
}while(movie_file.fail());
// array of objects
number_Of_Lines = numberOfLines(movie_file);
Movie* ptr = new Movie [number_Of_Lines];
//Looping through array of objects
for(int i = 0 ; i < number_Of_Lines ; i++)
populateMovieFromFile(movie_file, ptr[i]);
return ptr;
}
This function populates the objects
void populateMovieFromFile(ifstream& movie_file, Movie& movies)
{
getline(movie_file, movies.Title, ',');
movie_file.ignore();
getline(movie_file, movies.Gross, ',');
movie_file.ignore();
getline(movie_file, movies.Director, ',');
movie_file.ignore();
getline(movie_file, movies.Date, ',');
movie_file.ignore();
getline(movie_file, movies.Runtime);
}
Full program:
#include <iostream>
#include <string>
#include <fstream>
#include <iomanip>
#include <cctype>
using namespace std;
struct Movie
{
string Title; // Movie title
string Gross; // Gross total in billion dollars
string Director; // Director name
string Date; // Release date
string Runtime; // Runtime in minutes
};
int numberOfLines(ifstream&);
void populateMovieFromFile(ifstream&, Movie&);
void displayMovie(const Movie&);
Movie* createDatabase(int&);
bool caseInsensitiveCmp(string, string);
void findMovie(Movie*, int);
void saveToFile(const Movie&);
bool promptToContinue();
//void displayFavorites();
int main ()
{
int number_Of_Lines = 0;
Movie* ptr_movies = createDatabase(number_Of_Lines);
do
{
findMovie(ptr_movies , number_Of_Lines);
}while (!promptToContinue());
//displayFavorites();
return 0;
}
int numberOfLines(ifstream& movie_file)
{
int number_Of_Lines = 0;
string lines;
if(movie_file)
{
while(getline(movie_file, lines))
number_Of_Lines++ ;
}
movie_file.seekg (0, ios::beg);
return number_Of_Lines;
}
void populateMovieFromFile(ifstream& movie_file, Movie& movies)
{
getline(movie_file, movies.Title, ',');
movie_file.ignore();
getline(movie_file, movies.Gross, ',');
movie_file.ignore();
getline(movie_file, movies.Director, ',');
movie_file.ignore();
getline(movie_file, movies.Date, ',');
movie_file.ignore();
getline(movie_file, movies.Runtime);
}
void displayMovie(const Movie& movie)
{
cout << right << setw(13) << "Title: " << left << movie.Title << endl;
cout << right << setw(13) << "Gross Total: " << left << movie.Gross << " billion dollars" << endl;
cout << right << setw(13) << "Director: " << left << movie.Director << endl;
cout << right << setw(13) << "Release date: " << left << movie.Date << endl;
cout << right << setw(13) << "Runtime: " << left << movie.Runtime << " minutes" << endl;
}
Movie* createDatabase(int& number_Of_Lines)
{
// input file
ifstream movie_file;
string filename;
do
{
cout << "Please enter filename: " ;
getline (cin , filename);
movie_file.open(filename.c_str());
if(movie_file.fail())
cout << "Invalid file" << endl ;
}while(movie_file.fail());
// array of objects
number_Of_Lines = numberOfLines(movie_file);
Movie* ptr = new Movie [number_Of_Lines];
//Looping through array of objects
for(int i = 0 ; i < number_Of_Lines ; i++)
populateMovieFromFile(movie_file, ptr[i]);
return ptr;
}
bool caseInsensitiveCmp(string input, string list) //list will be from the object of array
{
int i = 0 , j = 0;
while (input[i])
{
char c = input[i];
input[i] = tolower(c);
i++;
}
while (list[j])
{
char c = list[j];
list[j] = tolower(c);
j++;
}
if (input == list)
return true;
else
return false;
}
void findMovie(Movie* ptr_movie, int number_Of_Lines)
{
cout << endl;
int i = 0;
char save;
string input_title;
bool found = false;
bool No_Match = false;
cout << "Enter a movie title to search for: ";
getline(cin , input_title);
do
{
found = caseInsensitiveCmp(ptr_movie[i].Title , input_title); //loop it
if (found == false)
i++;
if (i>=number_Of_Lines)
No_Match = true;
} while (found == false || No_Match == false);
if(found == true)
{
displayMovie(ptr_movie[i]);
cout << endl ;
cout << "Would you like to save the above movie? (Y or N)" << endl;
cin >> save;
if (save == 'y' || save == 'Y')
saveToFile(ptr_movie[i]);
}
else
cout << input_title << " not found in database. Please try again." << endl;
}
void saveToFile(const Movie& movie)
{
ofstream outfile;
outfile.open("favourites.txt", ios::app);
cout << movie.Title << "," << movie.Gross << ","
<< movie.Director << "," << movie.Date << ","
<< movie.Runtime << endl;
}
bool promptToContinue()
{
char Quit;
bool exit = false;
cout << "Would you like to exit? (Y or N): ";
cin >> Quit;
switch (Quit)
{
case 'Y':
case 'y':
exit = true;
case 'N':
case 'n':
exit = false;
}
return exit;
}
Any help is appreciated
The problem is:
In your "saveToFile" function, you open a ofstream, but do not use it. Instead you write to std::cout. So, you do not store the data.
Additionally, you are calling new but never delete. With that you are creating a memory leak.
Then, you still thinking to much in C. You should not use new or plain C-Style arrays. Never.
You should instead use STL containers and a more object oriented approach. At the moment you are using a lot of global functions, working with data.
In C++ you should use objects and associated data and methods. For example, a Movie knows how to read and store its data. Therefore implement that as a method.
And a Movie database is an additional object that contains a vector of movies.
To give you an idea of a more object oriented approach, I create a small example for you.
Please see and try to understand.
#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
#include <iterator>
#include <regex>
#include <string>
class Movie
{
public:
// Overload Extractor Operator to read data from somewhere
friend std::istream& operator >> (std::istream& is, Movie& m);
// Overload Inserter operator. Insert data into output stream
friend std::ostream& operator << (std::ostream& os, const Movie& m);
// Show movie data on std::out
void display() const;
// Check if a movie has a certain title
bool hasTitle(const std::string& t) const { return t == title; }
private:
// Data
std::string title{}; // Movie title
std::string gross{}; // Gross total in billion dollars
std::string director{}; // Director name
std::string date{}; // Release date
std::string runtime{}; // Runtime in minutes
};
// Overload Extractor Operator to read data from somewhere
std::istream& operator >> (std::istream& is, Movie& m) {
std::vector<std::string> dataInOneLine{}; // Here we will store all data that we read in one line;
std::string wholeLine; // Temporary storage for the complete line that we will get by getline
std::regex separator(","); ; // Separator for a CSV file
std::getline(is, wholeLine); // Read one complete line
// Parse the line and split it into parts
std::copy(std::sregex_token_iterator(wholeLine.begin(), wholeLine.end(), separator, -1),
std::sregex_token_iterator(),
std::back_inserter(dataInOneLine));
// If we have read all expted strings, then store them in our struct
if (dataInOneLine.size() == 5) {
m.title = dataInOneLine[0];
m.gross = dataInOneLine[1];
m.director = dataInOneLine[2];
m.date = dataInOneLine[3];
m.runtime = dataInOneLine[4];
}
return is;
}
std::ostream& operator << (std::ostream& os, const Movie& m) {
// Copy csv data to ostream
return os << m.title << "," << m.gross << "," << m.director << "," << m.date << "," << m.runtime << "\n";
}
void Movie::display() const {
std::cout << " Title: " << title << "\n Gross Total: " << gross << " billion dollars\n Director: " << director
<< "\nRelease date: " << date << "\n Runtime: " << runtime << " minutes\n";
}
// Database for Movies
class MovieDatabase {
public:
// Constructor. Open and read the database
explicit MovieDatabase(const std::string pafn) : pathAndFileName(pafn) { open(); }
// Destructor automatically saves and closes the database
~MovieDatabase() { close(); };
// Open/close the database
bool open();
void close();
// Add a new movie
void addMovie(const Movie& m) { data.push_back(m); }
// Find and display a movie
bool findAndDisplay (const std::string& title);
private:
const std::string pathAndFileName{};
std::vector<Movie> data{};
};
// Destructor
void MovieDatabase::close() {
// Save data
std::ofstream outFileStream{ pathAndFileName, std::ios::trunc };
if (outFileStream) {
// then save all data in csv format
std::copy(data.begin(), data.end(), std::ostream_iterator<Movie>(outFileStream));
}
}
// Open database and read the data from disk
bool MovieDatabase::open() {
bool success{ false };
// Open the file
std::ifstream inFileStream{ pathAndFileName };
// If the file could be opened
if (inFileStream) {
success = true;
// Then copy all data from disk, parse the csv and store it in our data vector
std::copy(std::istream_iterator<Movie>(inFileStream), std::istream_iterator<Movie>(), std::back_inserter(data));
}
return success;
}
// Find and display a value
bool MovieDatabase::findAndDisplay (const std::string& title) {
bool found { false };
// Search for a given title
std::vector<Movie>::iterator md = std::find_if(data.begin(), data.end(), [&title](const Movie &m) { return m.hasTitle(title); });
if (data.end() != md) {
// If found, then display it
md->display();
found = true;
}
else {
std::cerr << "\n\nTitle '" << title << "' not found in database\n\n";
}
return found;
}
int main() {
// Get the name of the database
std::string pathNameDatabase{};
std::cout << "Enter the path/filename of the database:\n";
std::cin >> pathNameDatabase;
// Define database and open it
MovieDatabase md{ pathNameDatabase };
// Do some stuff
std::cout << "\n\nSearch for title. Please enter title:\n";
std::string title{};
std::cin >> title; std::cin.ignore();
// Search and display data
md.findAndDisplay(title);
// Add a new record
std::cout << "\n\nAdd new movie data\nPlease enter title, gross, director, date, runtime (in one line, seperated by comma):\n";
Movie m{};
std::cin >> m;
m.display();
md.addMovie(m);
return 0;
}

How to open and read a file based on user input c++

I have an assignment that wants me to write a program that reads a text file, then outputs a modified version of that file using a version of Ceasar cipher that shifts the characters of the file that the user calls based on the shift amount that they input. For example if my file reads "hi" and they shift it by 1 it should read "ij". But when I run the program it doesnt recognize when I call in1 or out1 and I just get an error message. Can anyone help me figure out what I'm doing wrong or offer any advice on how to move forward? Thank you in advance!
#include <iostream>
#include <iomanip>
#include <fstream>
#include <cctype>
using namespace std;
int main()
{
//Declare user variables
int shift, file1chars = 0;
char filename[25];
ifstream in1;
ofstream out1;
do
{
in1.clear(); //clear status flags
//Prompt user to enter name of input file and amount of shift
cout << "Please enter the name of the input file." << endl;
cout << "Filename: ";
cin >> filename;
//Open file name
in1.open(filename);
//Error message if no file
if (!in1)
cout << "That is not a valid file. Try again\n";
} while (!in1);
do
{
out1.clear(); //clear status flags
//prompt user to input out file
cout << "Please enter the name of the output file." << endl;
cout << "Filename: ";
cin >> filename;
out1.open(filename);
//Error message if no file
if (!out1)
cout << "That is not a valid file. Try again\n";
} while (!out1);
//write some code to the input file
in1 >> "hi"
//write the integers in a different order to the output file
out1 << //idk where to go from here to initiate shift
//prompt user to enter shift
cout << "Please intput the shift amount: ";
cin >> shift;
cout << "Processing complete" << endl;
//Call file (?)
//Tell user file input is complete and is now printing statistics
cout << "\nShifted input file Complete. Now printing statistics " << endl;
//Show statistics for file
cout << "\nStatistics for file: " << filename << endl;
cout << "------------------------------------------------";
//Show characters in file and stats before shift
cout << "\n\nTotal # of characters in file: " << file1chars << endl;
cout << "Statistics before shift: " << endl;
//Show file before shift
//Show user stats after shift
cout << "\nStatistics after shift: " << endl;
//File after shift
//Close files
out1.close();
in1.close();
return 0;
}
Instead of looking at your code for line by line to see where the problem(s) could be, I ask you to think about your requirements and write code that expresses the intent as closely as possible. Create functions that follow the intended functionality.
Get the input file name.
Get the output file name.
Read the contents of the input file.
Transform the contents of the input file to create the output string.
Write the output string to the output file.
In pseudo code,
int main()
{
infile = get_input_filename()
outfile = get_output_filename()
contents = get_file_contents(infile)
output = transform_input(contents)
write_output(outfile, output)
}
Convert that to C++ code:
// Declare the functions.
std::string get_input_filename();
std::string get_output_filename();
std::string get_file_contents(std::string const& infile)
std::string transform_input(std::string const& contents)j
void write_output(std::string const& outfile, std::string const& output);
// Use them in main.
int main()
{
std::string infile = get_input_filename();
std::string outfile = get_output_filename();
std::string contents = get_file_contents(infile);
std::string output = transform_input(contents);
write_output(outfile, output);
}
// Implement the functions.
std::string get_input_filename()
{
std::string filename;
cout << "Please enter the name of the input file." << endl;
cout << "Filename: ";
cin >> filename;
return filename;
}
std::string get_output_filename()
{
std::string filename;
cout << "Please enter the name of the output file." << endl;
cout << "Filename: ";
cin >> filename;
return filename;
}
std::string get_file_contents(std::string const& infile)
{
std::ifstream inf(infile);
std::string contents;
int c;
while ( (c = inf.get()) != EOF )
{
contents += c;
}
return contents;
}
std::string transform_input(std::string const& contents)
{
std::string res;
// Do the needful to transform contents to res.
return res;
}
void write_output(std::string const& outfile, std::string const& output)
{
std::ofstream outf(outfile);
outf.write(output.c_str(), output.size();
}
If you are able to use a class or struct and functions I would propose something like this:
main.cpp
#include <string>
#include <iostream>
#include <fstream>
#include "CaesarShift.h"
int main() {
std::string filename;
std::cout << "Please enter the name of the input file. ";
std::cin >> filename;
std::ifstream fileIn;
std::string text;
fileIn.open( filename );
if ( !fileIn.is_open() ) {
std::cout << "Failed to open file: " << filename << "." << std::endl;
}
fileIn >> text;
fileIn.close();
CaesarText caesarText;
caesarText.addText( text );
std::cout << "Contents of the Caesar Text before peforming a Caesar Shift:\n"
<< caesarText << std::endl;
int amount = 0;
std::cout << "Please enter the amount to shift the text ";
std::cin >> amount;
std::cout << "Now performing the Caesar Shift: " << std::endl;
caesarShift( caesarText, amount );
std::cout << "Caesar Text after performing a Caesar Shift:\n"
<< ceasarText << std::endl;
std::ofstream fileOut;
fileOut.open( std::string( "shifted_" + filename ) );
if ( !fileOut.is_open() ) {
std::cout << "Failed to open shifted_" << filename << std::endl;
}
// Uncomment to print original text to file otherwise only modified text will be printed.
// fileOut << caesarText.originalText() << std::endl;
fileOut << caesarText.shiftedText() << std::endl;
fileOut.close();
system( "PAUSE" );
return 0;
}
CaesarShift.h
#ifndef CAESAR_SHIFT_H
#define CAESAR_SHIFT_H
class CaesarText {
std::string _originalText;
std::string _shiftedText;
public:
caesarText() = default;
explicit CeasarText( const std::string& text ) :
_originalText( text ) {}
void addText( const std::string& text ) {
_originalText = text;
}
std::string originalText() const {
return _originalText;
}
std::string shiftedText() const {
return _shiftedText;
}
friend void caesarShift( caesarText& c, int amount );
friend std::ostream& operator<<( std::ostream& out, const caesarText& ceasarText );
};
#endif // !CAESAR_SHIFT_H
CaesarShift.cpp
#include "CaesarShift.h"
#include <string>
#include <iostream>
#include <algorithm>
// Overloaded ostream operator
std::ostream& operator<<( std::ostream& o, const CaesarText& c ) {
o << "Original Text: " << c._originalText << "\n";
o << "Shifted Text: " << c._shiftedText << "\n";
return o;
}
// public friend function (visible in main) not actually a part of the class
// but performs operations on it.
// To perform the Caesar Shift here are 3 possible variations of implementing the same task.
void caesarShift( Caesar Text& text, int amount ) {
// Bound amount to the number of characters in the alphabet
// the same value used in any of the three variations below.
amount %= 26;
// Older c++ style loop.
/*for ( std::size_t i = 0; i < text._originalText.length(); i++ ) {
char c = text._originalText[i] + amount;
text._shiftedText += c;
}*/
// Modern C++ style loop
/*for ( auto& c : text._originalText ) {
text._shiftedText += c + amount;
}*/
// std::transform( s1.begin, s1.end, back_inserter( s2 ), lamda as predicate );
/*std::transform( text._originalText.begin(), text._originalText.end(),
std::back_inserter( text._shiftedText ),
[amount]( unsigned char c ) -> unsigned char { return c + amount; }
);*/
}
As for the 3 different variations of performing the Caesar Shift you can just uncomment the appropriate block section.

Assigning Different Variables to the Input From cin C++

In my lab I'm trying to process a line that represents a destination on Earth. An example of this is ....
"152/N 200/E California Ave"
In my professor's notes, he said that if you did this ...
std::cin >> latitude >> latitudeDirection >> longitude >> longitudeDirection >> address
Which consumes all the way up until California, where from there on out the string is consumed one at a time at each white space. How do I make it so I consume the rest of the input? Here's how my variables look when assigned ...
latitude = 152
latitudeDirection = "/N"
longitude = 200
longitudeDirection = "/E"
address = "California"
address only holds "California" when I want it to hold "California Ave". Here's the code I have so far.
int numberOfLocations;
std::cin >> numberOfLocations;
std::cin.ignore();
for (int x = 0; x < numberOfLocations; x++) {
double longitude, latitude;
std::string longitudeDirection, latitudeDirection, address;
/*
std::cin >> latitude >> latitudeDirection >> longitude >> longitudeDirection >> address;
std::cout << latitude << latitudeDirection << latitude << latitudeDirection << address << std::endl;
*/
}
The ordinary >> as input operator splits the input into "words" separated by whitespace.
To avoid that, consider reading one item per line of input. You can use std::getline from <string> to read a line. Where you need a number you can parse the line with e.g. std::stoi.
If you have no choice but to deal with a string that contains multiple items of input, where at the end there's textual data that can contain spaces, then you can use a std::istringstream to read the items before the text.
Example 1 – reading one item per line:
#include <iostream>
#include <stdlib.h> // exit, EXIT_FAILURE
#include <string> // std::string
using namespace std;
void error( string const& s )
{
cerr << "!" << s << endl;
exit( EXIT_FAILURE );
}
auto get_input_line()
-> string
{
string line;
getline( cin, line );
if( cin.fail() ) { error( "Uh oh, ..." ); }
return line;
}
auto main() -> int
{
cout << "This program adds two numbers A and B." << endl;
cout << "Number A, please? ";
double const a = stod( get_input_line() );
cout << "Number B, please? ";
double const b = stod( get_input_line() );
cout << a << " + " << b << " = " << a + b << "." << endl;
}
Example 2 – parsing a line of text
(IMHO not smart, but perhaps a requirement)
#include <iostream>
#include <stdlib.h> // exit, EXIT_FAILURE
#include <string> // std::string
#include <sstream> // std::istrstream
using namespace std;
void error( string const& s )
{
cerr << "!" << s << endl;
exit( EXIT_FAILURE );
}
auto main() -> int
{
string const data = "152/N 200/E California Ave";
cout << "This program parses the line '" << data << "'." << endl;
cout << endl;
cout << "Result:" << endl;
istringstream stream( data );
char dummy_char;
int latitude;
char latitudeDirection;
int longitude;
char longitudeDirection;
string address;
stream
>> latitude >> dummy_char >> latitudeDirection
>> longitude >> dummy_char >> longitudeDirection;
if( stream.fail() ) { error( "Initial items extraction failed." ); }
while( stream.peek() == ' ' ) { stream.get(); }
getline( stream, address );
cout << "> "
<< latitude << "/" << latitudeDirection << " "
<< longitude << "/" << longitudeDirection << " "
<< "'" << address << "'"
<< "<" << endl;
}
The problem here is that, you are trying to enter strings with space, therefore, as inputs via cin are delimited by a space ' ', you will have to use std::getline which will make the end of input delimeter as newline.
Example Implementation:
#include <iostream>
using namespace std;
int main()
{
string s;
getline(cin,s);
cout<<s;
return 0;
}
with this you can enter string with spaces. (end of string will be after a n enter pressed by user, there for end of string delimiter is newline)

Data doesn't load into c++ vector correctly

I am trying to load a text file and import the contents into a vector of structs.
Here are my definitions
typedef struct
{
string pcName, pcUsername, pcPassword, pcMessage, pcAdvertisement; //I know that
//this is incorrect convention. It was originally a char*
}
ENTRY;
vector<ENTRY> entries;
fstream data;
Here is my display data function
void DisplayData()
{
std::cout << (int)(entries.size() / 5) <<" entries" << endl;
for(int i = 1; i <=(int)entries.size()/5; i++)
{
cout << endl << "Entry " << i << ":" << endl
<< "Name: " << entries[i].pcName << endl
<< "Username: " << entries[i].pcUsername << endl
<< "Password: " << entries[i].pcPassword << endl
<< "Message: " << entries[i].pcMessage << endl
<< "Advertisement: " << entries[i].pcAdvertisement << endl;
}
}
and here is my Load Data function
bool LoadData(const char* filepath)
{
std::string lineData ;
int linenumber = 1 ;
data.open(filepath, ios::in);
ENTRY entry_temp;
if(!data.is_open())
{
cerr << "Error loading file" << endl;
return false;
}
while(getline(data, lineData))
{
if(linenumber==1) {entry_temp.pcName = lineData;}
else if(linenumber==2) {entry_temp.pcUsername = lineData;}
else if(linenumber==3) {entry_temp.pcPassword = lineData;}
else if(linenumber==4) {entry_temp.pcMessage = lineData;}
else if(linenumber==5) {entry_temp.pcAdvertisement = lineData;}
entries.push_back(entry_temp);
if(linenumber == 5)
{
linenumber = 0;
}
linenumber++;
}
data.close();
puts("Database Loaded");
return true;
}
Here is the text file I am loading:
Name1
Username1
Password1
Message1
Ad1
And here is the result of the display data function after calling load data:
1 entries
Entry 1:
Name: Name1
Username Username1
Password:
Message:
Advertisement:
As you can see, the first two load but the last three don't. When I did this with an array instead of a vector, it worked fine so I don't know what I'm doing wrong. Thanks.
I suggest that you read each line directly into the data field where it goes:
getline(data, entry_temp.pcName);
getline(data, entry_temp.pcUsername);
getline(data, entry_temp.pcPassword);
getline(data, entry_temp.pcMessage);
getline(data, entry_temp.pcAdvertisement);
entries.push_back(entry_temp);
This makes your intent much clearer than your current while loop. It also creates a single entry for all 4 input lines rather than one for each input line (with the other three blank). Now you can read several "entries" by using a while loop that checks if you have reached the end of the file.
Doing this will also make printing out the data much easier since the vector will have exactly the number of entries rather than five times as many as you expect (which also eats up a lot more memory than you need to).
Your DisplayData function is a little weird, and so is your LoadData.
Your LoadData pushes back a new copy of the current ENTRIES entry with every line. Your DisplayData starts at 1 (which is not the beginning of any vector or array), and iterates only up to the 1/5th entry of the entire vector.
This needs a heavy rework.
First, the size() member of any standard container returns the number of elements that it contains, and will not take the number of fields in a contained struct into account.
For future reference, you'll want to post your question in a complete, standalone example that we can immediately compile to help. (see http://sscce.org/)
Try this modified data, which runs correctly, and see if you can tell what is being done differently:
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
using namespace std;
typedef struct
{
string pcName, pcUsername, pcPassword, pcMessage, pcAdvertisement;
}
ENTRY;
vector<ENTRY> entries;
fstream data;
bool LoadData(const char* filepath)
{
std::string lineData ;
int linenumber = 1 ;
data.open(filepath, ios::in);
ENTRY entry_temp;
if(!data.is_open())
{
cerr << "Error loading file" << endl;
return false;
}
while(getline(data, lineData))
{
if(linenumber==1) {entry_temp.pcName = lineData;}
else if(linenumber==2) {entry_temp.pcUsername = lineData;}
else if(linenumber==3) {entry_temp.pcPassword = lineData;}
else if(linenumber==4) {entry_temp.pcMessage = lineData;}
else if(linenumber==5) {entry_temp.pcAdvertisement = lineData;}
if(linenumber == 5)
{
entries.push_back(entry_temp);
linenumber = 0;
}
linenumber++;
}
data.close();
puts("Database Loaded");
return true;
}
void DisplayData()
{
std::cout << entries.size() <<" entries" << endl;
for(int i = 0; i < entries.size(); i++)
{
cout << endl << "Entry " << i << ":" << endl
<< "Name: " << entries[i].pcName << endl
<< "Username: " << entries[i].pcUsername << endl
<< "Password: " << entries[i].pcPassword << endl
<< "Message: " << entries[i].pcMessage << endl
<< "Advertisement: " << entries[i].pcAdvertisement << endl;
}
}
int main()
{
LoadData("/tmp/testdata");
DisplayData();
return (0);
}
While I think #code-guru has the right idea, I'd take the same idea just a little further, and make your code work a little more closely with the standard library. I'd do that by reading a data item with a stream extractor, and displaying it with stream inserter. So, the extractor would look something like this:
std::istream &operator>>(std::istream &is, ENTRY &e) {
getline(is, e.pcName);
getline(is, e.pcUsername);
getline(is, e.pcPassword);
getline(is, e.pcMessage);
getline(is, e.pcAdvertisement);
return is;
}
..and the inserter would look something like this:
std::ostream &operator<<(std::ostream &os, ENTRY const &e) {
os << e.pcName << "\n";
os << e.pcUsername << "\n";
os << e.pcPassword << "\n";
os << e.pcMessage << "\n";
os << e.pcAdvertisement << "\n";
return os;
}
With those in place, loading and displaying the data becomes fairly straightforward.
Load the data:
std::ifstream in("yourfile.txt");
std::vector<ENTRY> data((std::istream_iterator<ENTRY>(in)),
std::istream_iterator<ENTRY>());
Display the data:
for (auto const & e: data)
std::cout << e << "\n";
For the moment, I haven't tried to duplicate the format you were using to display the data -- presumably the modifications for that should be fairly obvious.