Storing data from an unknown number of files - c++

I have used the following piece of code to read from multiple .dat files and parse them. This code uses 3D vectors to store data after the reading process. However, I would like that the data corresponding to each single file be independent from the others. The issue is that the number of files varies, and is unknown at compile time; hence, the number of vectors varies too. I would like to know if there is any solution for this.
vector<vector<vector<string>>> masterList;
for (int i = 0; i < files.size(); ++i) {
cout << "file name: " << files[i] << endl;
fin.open(files[i].c_str());
if (!fin.is_open()) {
// error occurs!!
// break or exit according to your needs
cout<<"error"<<endl;
}
std::vector<vector<string>> tokens;
int current_line = 0;
std::string line;
while (std::getline(fin, line))
{
cout<<"line number: "<<current_line<<endl;
// Create an empty vector for this line
tokens.push_back(vector<string>());
//copy line into is
std::istringstream is(line);
std::string token;
int n = 0;
//parsing
while (getline(is, token, DELIMITER))
{
tokens[current_line].push_back(token);
cout<<"token["<<current_line<<"]["<<n<<"] = " << token <<endl;
n++;
}
cout<<"\n";
current_line++;
}
fin.clear();
fin.close();
masterList.push_back(tokens);
}
So, the main issue I'm facing is: how to create a variable number of 2D vectors to store the data corresponding to each single file, when I don't know how many files there are at compile time.

Modify the list of files in the main to adapt the size of your "master data". If the length of file names is variable, then parse it first (or get it one way or another first), and then execute the parsing on the dat files. If the filenames are known at run time only, and asynchronously with that, then add a new element in the list each time you get a new filename (you can use events for that for example, take a look at https://github.com/Sheljohn/siglot).
Note that list elements are independent in memory, and that lists support deletion/insertion in constant time. That way, data corresponding to each file is independent from the other. If you want to retrieve the data specific to a file (knowing the filename), either iterate on the list to find the corresponding file (linear time) or trade the list for an unordered_map (amortized constant time).
#include <string>
#include <list>
#include <vector>
#include <iostream>
#include <sstream>
#include <fstream>
#include <iterator>
#include <algorithm>
using namespace std;
#define AVG_LINES_PER_FILE 100
/**
* [tokenize_string Tokenize input string 'words' and put elements in vector 'tokens'.]
* #param words [Space separated data-string.]
* #param tokens [Vector of strings.]
*/
void tokenize_string( string& words, vector<string>& tokens )
{
unsigned n = count( words.begin(), words.end(), ' ' );
tokens.reserve(n);
istringstream iss(words);
copy(
istream_iterator<string>(iss),
istream_iterator<string>(),
back_inserter<vector<string> >(tokens)
);
}
/**
* Contains data parsed from a single .dat file
*/
class DATFileData
{
public:
typedef vector<string> line_type;
typedef vector<line_type> data_type;
DATFileData( const char* fname = nullptr )
{
m_fdata.reserve(AVG_LINES_PER_FILE);
m_fdata.clear();
if ( fname ) parse_file(fname);
}
// Check if the object contains data
inline operator bool() const { return m_fdata.size(); }
// Parse file
bool parse_file( const char* fname )
{
string line;
m_fdata.clear();
ifstream fin( fname );
if ( fin.is_open() )
{
while ( fin.good() )
{
getline(fin,line);
m_fdata.push_back(line_type());
tokenize_string( line, m_fdata.back() );
}
fin.close();
m_fname = fname;
cout << "Parsed " << m_fdata.size() << " lines in file '" << fname << "'." << endl;
return true;
}
else
{
cerr << "Could not parse file '" << fname << "'!" << endl;
return false;
}
}
// Get data
inline unsigned size() const { return m_fdata.size(); }
inline const char* filename() const { return m_fname.empty() ? nullptr : m_fname.c_str(); }
inline const data_type& data() const { return m_fdata; }
inline const line_type& line( const unsigned& i ) const { return m_fdata.at(i); }
private:
string m_fname;
data_type m_fdata;
};
int main()
{
unsigned fcount = 0;
vector<string> files = {"some/file/path.dat","another/one.dat"};
list<DATFileData> data(files.size());
for ( DATFileData& d: data )
d.parse_file( files[fcount++].c_str() );
cout << endl << files.size() << " files parsed successfully." << endl;
}

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.

C++ Alphabetically Sorting Strings in Vectors of Vectors

nm92,Nate,Matthews,Aetna,1
sc91,Steve,Combs,Cigna,2
ml94,Morgan,Lands,BCBS,3
kb93,Kyle,Borris,Aetna,2
I am trying to take a CSV input file like above, store it, sort it by insurance (col 4), and then write it to diff files based on insurance but in alphabetical order by last name.
So in this program, I have a vector of uniqueInsurances, which in turn have a vector of enrollees. It is this vector of enrollees that I want to sort alphabetically by last name (col 3), so that if uniqueInsurances[0].name is Aetna, then uniqueInsurances[0].enrollees[] will have Kyle Borris listed BEFORE Nate Matthews. Right now I have it stored the other way with Nate Matthews listed before Kyle Borris.
I think it's due to the vector of vectors and nested for loops required for this problem that's getting me mixed up, so I was wondering if someone could help guide me in terms of the best way to sort the enrollee vectors for each uniqueInsurance?
struct enrollee
{
string userid = "";
string fname = "";
string lname = "";
string insurance = "";
string version = "";
};
struct uniqueInsurance
{
string name = "";
int numEnrollees = 0;
vector <enrollee> enrollVector;
};
If your task is just to write to different files sorted names then you do not need the second structure. Just have one std::vector<enrollee> sort it based on insurance and names, then iterate over it. When insurance name changes reopen file accordingly:
std::vector<enrollee> enrollees;
// read them from csv file
std::sort( enrollees.begin(), enrollees.end(), []( const enrollee &e1, const enrollee &e2 ) {
return std::tie( e1.insurance, e1.fname, e1.lname ) < std::tie( e2.insurance, e2.fname, e2.lname );
} );
std::string insurance;
std::ofstream out;
for( const auto &e : enrollees ) {
if( insurance != e.insurance ) {
insurance = e.insurance;
out.open( insurance + ".csv" );
}
out << e.fname << ',' << e.lname << std::endl;
}
This sorts by first name then last name, if you need last name first just swap their order in std::tie()
This is a follow up of your question yesterday:
Original question
I modified my code and added one line for sorting.
Please see:
#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
#include <iterator>
#include <regex>
struct Enrollee
{
// Data
std::string userid{};
std::string fname{};
std::string lname{};
std::string insurance{};
std::string version{};
// Overload Extractor Operator to read data from somewhere
friend std::istream& operator >> (std::istream &is, Enrollee& e) {
std::vector<std::string> wordsInLine{}; // Here we will store all words that we read in onle 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 and split it into parts
std::copy(std::sregex_token_iterator(wholeLine.begin(), wholeLine.end(), separator, -1), std::sregex_token_iterator(), std::back_inserter(wordsInLine));
// If we have read all expted strings, then store them in our struct
if (wordsInLine.size() == 5) {
e.userid = wordsInLine[0];
e.fname = wordsInLine[1];
e.lname = wordsInLine[2];
e.insurance = wordsInLine[3];
e.version = wordsInLine[4];
}
return is;
}
// Overload Inserter operator. Insert data into output stream
friend std::ostream& operator << (std::ostream& os, const Enrollee& e) {
return os << "userid is: " << e.userid << "\nfname is: " << e.fname << "\nlname is: " << e.lname << "\ninsurance is: " << e.insurance << "\nversion is: " << e.version << '\n';
}
};
int main()
{
// Her we will store all Enrollee data in a dynamic growing vector
std::vector<Enrollee> enrollmentData{};
// Define inputFileStream and open the csv
std::ifstream inputFileStream("r:\\input.csv");
// If we could open the file
if (inputFileStream)
{
// Then read all csv data
std::copy(std::istream_iterator<Enrollee>(inputFileStream), std::istream_iterator<Enrollee>(), std::back_inserter(enrollmentData));
// Sort the data
std:sort(enrollmentData.begin(),enrollmentData.end(),[](const Enrollee& left, const Enrollee& right){return left.lname < right.lname;});
// For Debug Purposes: Print all data to cout
std::copy(enrollmentData.begin(), enrollmentData.end(), std::ostream_iterator<Enrollee>(std::cout, "\n"));
}
else {
std::cerr << "Could not open file 'input.csv'\n";
}
}

C++ Data files, arrays, and calculations assignment

I'm new to C++ and I'm running into an issue on one of my assignments. The goal is to load data from a data file that looks like this.
item number date quantity cost per each
1000 6/1/2018 2 2.18
1001 6/2/2018 3 4.44
1002 6/3/2018 1 15.37
1001 6/4/2018 1 4.18
1003 6/5/2018 7 25.2
Basically I need to do calculations the average item number used for each date using arrays and do some other calculations with the cost. I'm getting really hung up with loading the data from the file and manipulating it for equations. This is what I have so far.
#include <cmath> //for math operations
#include <iostream> //for cout
#include <cstdlib> //for compatibility
#include <fstream>
#include <string>
using namespace std;
int main()
{
string date;
int EOQ, rp;
int count;
int itemnum[][];
double quantity[][];
double cost[][];
ifstream myfile;
string filename;
cout << "Data File: " << endl;
cin >> filename; // user enters filename
myfile.open(filename.c_str());
if(myfile.is_open())
{
cout << "file opened" << endl;
string head;
while(getline(myfile, head))
{
break; // so header won't interfere with data
}
while(!myfile.eof())
{ // do this until reaching the end of file
int x,y;
myfile >> itemnum[x][y] >> date >> quantity[x][y] >> cost[x][y];
cout << "The numbers are:" << endl;
for(count = 0; count < y; count++)
{
cout << itemnum[x][y] << endl;
break;
}
//cout << "Item: Reorder Point: EOQ: " << endl;
//cout << itemnum << " " << rp << " " << EOQ << endl;
break;
}
}
else
{
cout << "" << endl; //in case of user error
cerr << "FILE NOT FOUND" << endl;
}
cout << endl;
cout << "---------------------------------------------" << endl;
cout << " End of Assignment A8" << endl;
cout << "---------------------------------------------" << endl;
cout << endl;
system("pause");
return 0;
I haven't started working with the equations yet since I still can't get the file loaded in a simple array!!!
Thank you!
Link for data file : https://drive.google.com/file/d/1QtAC1bu518PEnk4rXyIXFZw3AYD6OBAv/view?usp=sharing
When working on these kinds of problems I like to break these down into the parts related to parsing. I'm using some of the standard libraries to do some of the work for me. I also created a couple of structures to help keep the information of the data organized. As for your date, I could of left that as a single std::string but I chose to break the date down into three individual types themselves and store them into a data structure just to show the capabilities of one of the functions that is involved with parsing.
What I prefer doing is to get either a single line of data from a file and save that to a string, or get the entire contents of a file and save that either to a large buffer or a vector of strings, unless if I'm handling specific type of code where that is not applicable such as parsing a wav file. Then close the file handle as I'm done reading from it! Then after I have all of the information I need, instead of trying to parse the file directly while it is opened I'd rather parse a string as it is easier to parse. Then after parsing the string we can populate our data types that we need.
I had to modify your data file slightly to accommodate for the extra white spaces so I saved your file as a text file with only a single white space between each data type within a single line of text. I also did not include the first line (header) information as I just omitted it completely. However this should still act as a guide of how to design a good work flow for an application that has good readability, reusability, try to keep it portable and as generic as possible. Now, what you have been waiting for; the demonstration of my version of your code:
#include <string>
#include <sstream>
#include <iostream>
#include <fstream>
#include <exception>
struct Date {
int month;
int day;
int year;
Date() = default;
Date( int monthIn, int dayIn, int yearIn ) :
month( monthIn ),
day( dayIn ),
year( yearIn )
{}
};
struct DataSheetItem {
int itemNumber;
Date date;
int quantity;
double costPerEach;
DataSheetItem() = default;
DataSheetItem( int itemNumberIn, Date& dateIn, int quantityIn, double costPerEachIn ) :
itemNumber( itemNumberIn ),
date( dateIn ),
quantity( quantityIn ),
costPerEach( costPerEachIn )
{}
};
std::vector<std::string> splitString( const std::string& s, char delimiter ) {
std::vector<std::string> tokens;
std::string token;
std::istringstream tokenStream( s );
while( std::getline( tokenStream, token, delimiter ) ) {
tokens.push_back( token );
}
return tokens;
}
void getDataFromFile( const char* filename, std::vector<std::string>& output ) {
std::ifstream file( filename );
if( !file ) {
std::stringstream stream;
stream << "failed to open file " << filename << '\n';
throw std::runtime_error( stream.str() );
}
std::string line;
while( std::getline( file, line ) ) {
if ( line.size() > 0 )
output.push_back( line );
}
file.close();
}
DataSheetItem parseDataSheet( std::string& line ) {
std::vector<std::string> tokens = splitString( line, ' ' ); // First parse with delimeter of a " "
int itemNumber = std::stoi( tokens[0] );
std::vector<std::string> dateInfo = splitString( tokens[1], '/' );
int month = std::stoi( dateInfo[0] );
int day = std::stoi( dateInfo[1] );
int year = std::stoi( dateInfo[2] );
Date date( month, day, year );
int quantity = std::stoi( tokens[2] );
double cost = std::stod( tokens[3] );
return DataSheetItem( itemNumber, date, quantity, cost );
}
void generateDataSheets( std::vector<std::string>& lines, std::vector<DataSheetItem>& dataSheets ) {
for( auto& l : lines ) {
dataSheets.push_back( parseDataSheet( l ) );
}
}
int main() {
try {
std::vector<std::string> fileConents;
getDataSheetItemsFromFile( "test.txt", fileContents );
std::vector<DataSheetItem> data;
generateDataSheets( fileConents, data );
// test to see if info is correct
for( auto& d : data ) {
std::cout << "Item #: " << d.itemNumber << " Date: "
<< d.date.month << "/" << d.date.day << "/" << d.date.year
<< " Quantity: " << d.quantity << " Cost: " << d.costPerEach << '\n';
}
} catch( const std::runtime_error& e ) {
std::cerr << e.what() << '\n';
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
NOTE This will not work with how your file currently is; this does not account for the first line of text (header information) and this does not account for any extra white spaces in between the data fields. If you add a single line of text when opening the file and read in a single line and just ignore it, then perform the loop to get all strings to add to vector to return back; your vectors will have the information in it but they will not be at the correct index locations of the vector because of all the extra white spaces. This is something you need to be aware of! Other than that; this is how I would basically design a program or application to parse data. This is by all means not 100% full proof and may not even be 100% bug free, but from a quick glance and running it through my debugger a few times it does appear to be without any noticeable bugs. There could also be some room for improvements for runtime efficiency, etc. but this is just a generalization of basic parsing.

Store input values into array while reading them in, c++

I am pretty new to c++. I am trying to read a file in line by line and store the input into several arrays.
Because I don't know the size of input file, I have this to get the number of lines in the file
while (std::getline(inputFile, line)){
++numOfLines;
std::cout << line << std::endl;
}
Now I want to use the numOfLines as the size of arrays, but i cannot get it run by having this
std::string *firstName= new std::string[numOfLines];
std::string *lastName= new std::string[numOfLines];
for (int i = 0; i < numOfLines; ++i)
{
line >> firstName[i];
}
I guess it is because it has reached the end of the file after the while loop. But I do not know how to solve this problem. Is there a way to scan the input file in and store the value into array at the same time?
If you use std::vector you don't need to know ahead the lines count. You can use vector method push_back to insert new elements into it. Try use something like this:
#include <fstream>
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main()
{
std::vector<std::string> first_names;
std::string line;
ifstream input_file;
while (std::getline(input_file, line)){
first_names.push_back(line);
}
for (size_t i = 0; i < first_names.size(); i++) {
std::cout << first_names[i] << std::endl;
}
return 0;
}
I don't know if you have ever taken a course related to Data Structures & Algorithms,
in which you will learn to use Containers (such as:
vector,
deque,
list, etc.) instead of Primitive Data Structures.
Please notice that although the follow example chooses vector as its container, it could vary according to different contexts. Say you are handling gigantic mount of data, you might want to use list instead`1,2,3.
#include <fstream>
#include <iostream>
#include <vector>
#include <string>
// alias long type
// #see: https://en.cppreference.com/w/cpp/language/type_alias
using NameVector = std::vector<std::string>;
int handleLine(std::string line, NameVector &firstNames)
{
// TODO implement your line handler here
firstNames.push_back(line);
return 0;
}
int handleFile(std::ifstream inputFile, NameVector &firstNames)
{
std::string line;
for (int lineNum = 1;
// invoke `good` to check if there is any error
inputFile.good()
&&
std::getline(inputFile, line);
lineNum++)
{
std::cout << "Current line number : (" << lineNum << ")" << std::endl;
std::cout << "Current line content: (" << line << ")" << std::endl;
handleLine(line, &firstNames);
}
return 0;
}
int main()
{
std::string path; // = R"(HERE GOES YOUR FILE PATH)";
// Using **Raw string**
std::ifstream inputFile { path }; // Initialize `inputFile`
NameVector firstNames;
handleFile(inputFile, firstNames);
for (auto firstName : firstNames)
{
std::cout << firstName << std::endl;
}
return 0;
}

print out the last 10 lines of a file

I want to have the option to print out the last 10 lines of a textfile . with this program I've been able to read the whole textfile, but I can't figure out how to manipulate the array in which the textfile is saved, any help?
// Textfile output
#include<fstream>
#include<iostream>
#include<iomanip>
using namespace std;
int main() {
int i=1;
char zeile[250], file[50];
cout << "filename:" << flush;
cin.get(file,50); ///// (1)
ifstream eingabe(datei , ios::in); /////(2)
if (eingabe.good() ) { /////(3)
eingabe.seekg(0L,ios::end); ////(4)
cout << "file:"<< file << "\t"
<< eingabe.tellg() << " Bytes" ////(5)
<< endl;
for (int j=0; j<80;j++)
cout << "_";
cout << endl;
eingabe.seekg(0L, ios::beg); ////(6)
while (!eingabe.eof() ){ ///(7)
eingabe.getline(zeile,250); ///(8)
cout << setw(2) << i++
<< ":" << zeile << endl;
}
}
else
cout <<"dateifehler oder Datei nicht gefunden!"
<< endl;
return 0;
}
Try this:
#include <list>
#include <string>
#include <iostream>
#include <fstream>
#include <algorithm>
#include <iterator>
// A class that knows how to read a line using operator >>
struct Line
{
std::string theLine;
operator std::string const& () const { return theLine; }
friend std::istream& operator>>(std::istream& stream, Line& l)
{
return std::getline(stream, l.theLine);
}
};
// A circular buffer that only saves the last n lines.
class Buffer
{
public:
Buffer(size_t lc)
: lineCount(lc)
{}
void push_back(std::string const& line)
{
buffer.insert(buffer.end(),line);
if (buffer.size() > lineCount)
{
buffer.erase(buffer.begin());
}
}
typedef std::list<std::string> Cont;
typedef Cont::const_iterator const_iterator;
typedef Cont::const_reference const_reference;
const_iterator begin() const { return buffer.begin(); }
const_iterator end() const { return buffer.end();}
private:
size_t lineCount;
std::list<std::string> buffer;
};
// Main
int main()
{
std::ifstream file("Plop");
Buffer buffer(10);
// Copy the file into the special buffer.
std::copy(std::istream_iterator<Line>(file), std::istream_iterator<Line>(),
std::back_inserter(buffer));
// Copy the buffer (which only has the last 10 lines)
// to std::cout
std::copy(buffer.begin(), buffer.end(),
std::ostream_iterator<std::string>(std::cout, "\n"));
}
Basically, you are not saving the file contents to any array. The following sample will give you a head start:
#include <iostream>
#include <vector>
#include <string>
int main ( int, char ** )
{
// Ask user for path to file.
std::string path;
std::cout << "filename:";
std::getline(std::cin, path);
// Open selected file.
std::ifstream file(path.c_str());
if ( !file.is_open() )
{
std::cerr << "Failed to open '" << path << "'." << std::endl;
return EXIT_FAILURE;
}
// Read lines (note: stores all of it in memory, might not be your best option).
std::vector<std::string> lines;
for ( std::string line; std::getline(file,line); )
{
lines.push_back(line);
}
// Print out (up to) last ten lines.
for ( std::size_t i = std::min(lines.size(), std::size_t(10)); i < lines.size(); ++i )
{
std::cout << lines[i] << std::endl;
}
}
It would probably be wiser to avoid storing the whole file into memory, so you could re-write the last 2 segments this way:
// Read up to 10 lines, accumulating.
std::deque<std::string> lines;
for ( std::string line; lines.size() < 0 && getline(file,line); )
{
lines.push_back(line);
}
// Read the rest of the file, adding one, dumping one.
for ( std::string line; getline(file,line); )
{
lines.pop_front();
lines.push_back(line);
}
// Print out whatever is left (up to 10 lines).
for ( std::size_t i = 0; i < lines.size(); ++i )
{
std::cout << lines[i] << std::endl;
}
The eof() function does not do what you and it seems a million other C++ newbies think it does. It does NOT predict if the next read will work. In C++ as in any other language, you must check the status of each read operation, not the state of the input stream before the read. so the canonical C++ read line loop is:
while ( eingabe.getline(zeile,250) ) {
// do something with zeile
}
Also, you should be reading into a std::string, and get rid of that 250 value.
Do a circular buffer with 10 slots and while reading the file lines, putting them into this buffer. When you finish thr file, do a position++ to go to the first element and print them all.
Pay attention for null values if the file has less than 10 lines.
Have an array of strings with size 10.
Read the first line and store into the array
Continue reading till the array is full
Once the array is full delete the first entry so that you can enter new line
Repeate step 3 and 4 till the file is finished reading.
I investigate proposed approaches here and describe all in my blog post. There is a better solution but you have to jump to the end and persist all needed lines:
std::ifstream hndl(filename, std::ios::in | std::ios::ate);
// and use handler in function which iterate backward
void print_last_lines_using_circular_buffer(std::ifstream& stream, int lines)
{
circular_buffer<std::string> buffer(lines);
std::copy(std::istream_iterator<line>(stream),
std::istream_iterator<line>(),
std::back_inserter(buffer));
std::copy(buffer.begin(), buffer.end(),
std::ostream_iterator<std::string>(std::cout));
}