Reading from file and outputting to console through vector - c++

I'm trying to read from this file file.txt which contains contents of the contestants who participated in the long jump event during the Olympic games.
The file is in the format of [First Name] [Last Name] [Nationality] [Distance]
There are 40 contestants in this file. I'm trying to organize them such that there is a vector of pointers of athletes, 40 to be precise. Then dynamically allocate them to the heap. Each athlete is one object in the vector.
Once each athlete object is entered into the vector, I wish to output all the contents of the vector onto the console through a for loop.
However, as it currently stands, in my code I do have 40 objects allocated to the vector, but its the same one being repeated 40 times. The last object is also being repeated twice for some reason.
Any help would be greatly appreciated! Thanks in advance.
Test.cpp
#include <iostream>
#include<fstream>
#include<vector>
#include<string>
#include "Person.h"
#include "Athlete.h"
using namespace std;
vector<Athlete*> athletesList(40);
//overload the operator << to be used for printing the record Objects
ostream& operator<<(ostream& out, Athlete& a) {
out << a.getFirstName() << " " << a.getLastName() << " " << a.getNationality() << " " << a.getDistance() << "\n";
return out;
}
void readAthletesFromFile() {
fstream athlethesFile;
athlethesFile.open("file.txt");
string tmpFirstName;
string tmpLastName;
string tmpNationality;
string tmpDoubleDistance;
while (!athlethesFile.eof()) {
athlethesFile >> tmpFirstName;
athlethesFile >> tmpLastName;
athlethesFile >> tmpNationality;
athlethesFile >> tmpDoubleDistance;
double tmpDistance = stod(tmpDoubleDistance);
for (int i = 0; i < 40; i++) {
athletesList[i] = new Athlete(tmpFirstName, tmpLastName, tmpNationality, tmpDistance);
}
cout << *athletesList[0];
}
}
int main()
{
readAthletesFromFile();
system("pause");
}
File.txt
Aleksandr Menkov Russia 8.09
Aleksandr Petrov Russia 7.89
Alyn Camara Germany 7.72
Arsen Sargsyan Armenia 7.62
Boleslav Skhirtladze Georgia 7.26
Christian Reif Germany 7.92
Christopher Tomlinson Great_Britain 8.06
Damar Forbes Jamaica 7.79
Eusebio Caceres Spain 7.92
George Kitchens United_States 6.84
Godfrey Khotso-Mokoena South_Africa 8.02
Greg Rutherford Great_Britain 8.08
Henry Frayne Australia 7.95
Ignisious Gaisah Ghana 7.79
Li Jinzhe China 7.77
Lin Ching-Hsuan-Taipei China 7.38
Louis Tsatoumas Greece 7.53
Luis Rivera Mexico 7.42
Marcos Chuva Portugal 7.55
Marquise Goodwin United_States 8.11
Mauro-Vinicius da-Silva Brazil 8.11
Michel Torneus Sweden 8.03
Mitchell Watt Australia 7.99
Mohamed Fathalla-Difallah Egypt 7.08
Mohammad Arzandeh Iran 7.84
Ndiss Kaba-Badji Senegal 7.66
Povilas Mykolaitis Lithuania 7.61
Raymond Higgs Bahamas 7.76
Roman Novotny Czech-Republic 6.96
Salim Sdiri France 7.71
Sebastian Bayer Germany 7.92
Sergey Morgunov Russia 7.87
Stanley Gbagbeke Nigeria 7.59
Stepan Wagner Czech-Republic 7.5
Supanara Sukhasvasti Thailand 7.38
Tyrone Smith Bermuda 7.97
Vardan Pahlevanyan Armenia 6.55
Viktor Kuznyetsov Ukraine 7.5
Will Claye United_States 7.99
Zhang Xiaoyi China 7.25
Expected Output
Ex: *athletesList[0] = Aleksandr Menkov Russia 8.09
*athletesList[10]= Godfrey Khotso-Mokoena South_Africa 8.02
Current Output
Ex: *athletesList[0] =
Aleksandr Menkov Russia 8.09
Aleksandr Petrov Russia 7.89
Alyn Camara Germany 7.72
Arsen Sargsyan Armenia 7.62
Boleslav Skhirtladze Georgia 7.26
Christian Reif Germany 7.92
Christopher Tomlinson Great_Britain 8.06
Damar Forbes Jamaica 7.79
Eusebio Caceres Spain 7.92
George Kitchens United_States 6.84
Godfrey Khotso-Mokoena South_Africa 8.02
Greg Rutherford Great_Britain 8.08
Henry Frayne Australia 7.95
Ignisious Gaisah Ghana 7.79
Li Jinzhe China 7.77
Lin Ching-Hsuan-Taipei China 7.38
Louis Tsatoumas Greece 7.53
Luis Rivera Mexico 7.42
Marcos Chuva Portugal 7.55
Marquise Goodwin United_States 8.11
Mauro-Vinicius da-Silva Brazil 8.11
Michel Torneus Sweden 8.03
Mitchell Watt Australia 7.99
Mohamed Fathalla-Difallah Egypt 7.08
Mohammad Arzandeh Iran 7.84
Ndiss Kaba-Badji Senegal 7.66
Povilas Mykolaitis Lithuania 7.61
Raymond Higgs Bahamas 7.76
Roman Novotny Czech-Republic 6.96
Salim Sdiri France 7.71
Sebastian Bayer Germany 7.92
Sergey Morgunov Russia 7.87
Stanley Gbagbeke Nigeria 7.59
Stepan Wagner Czech-Republic 7.5
Supanara Sukhasvasti Thailand 7.38
Tyrone Smith Bermuda 7.97
Vardan Pahlevanyan Armenia 6.55
Viktor Kuznyetsov Ukraine 7.5
Will Claye United_States 7.99
Zhang Xiaoyi China 7.25
Zhang Xiaoyi China 7.25

Avoid calling new and delete explicitly to dynamically allocate memory. You don't need it in combination with STL containers. A container allocates and manages the memory on the heap.
The for loop is not necessary. For each element you read you overwrite all elements with the last read element.
You print the first element from the vector for each element you read:
Change
cout << athletesList[0];
to
cout << athletesList.back();
You can't
while (!athlethesFile.eof()) {
because eof is set after you tried to read and it failed. You have to check if the read was successful after the read. Why is iostream::eof inside a loop condition (i.e. while (!stream.eof())) considered wrong?
Don't include Person.h if you don't use it.
You should avoid system(). Usually it's not portable.
In your current code you read 40 elements in a loop. In each loop iteration you allocate 40 elements for that vector. That means that you allocate memory for 1600 elements. You do not clean that up. That is a serious memory leak.
#include "Athlete.h"
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
using std::cerr;
using std::cout;
using std::fstream;
using std::ostream;
using std::string;
using std::vector;
//overload the operator << to be used for printing the record Objects
ostream& operator<<(ostream& out, Athlete& a) {
out << a.getFirstName() << " " << a.getLastName() << " " << a.getNationality() << " " << a.getDistance() << "\n";
return out;
}
auto readAthletesFromFile() {
vector<Athlete> athletesList;
athletesList.reserve(40);
fstream athlethesFile("file.txt");
if (!athlethesFile) {
cerr << "Could not open file\n";
return athletesList;
}
string tmpFirstName;
string tmpLastName;
string tmpNationality;
string tmpDoubleDistance;
while (true) {
athlethesFile >> tmpFirstName;
athlethesFile >> tmpLastName;
athlethesFile >> tmpNationality;
athlethesFile >> tmpDoubleDistance;
if (!athlethesFile) break;
auto tmpDistance = stod(tmpDoubleDistance);
athletesList.emplace_back(tmpFirstName, tmpLastName, tmpNationality, tmpDistance);
//cout << athletesList.back();
}
return athletesList;
}
int main() {
auto athletesList = readAthletesFromFile();
cout << athletesList[9];
}

I'm going to posit that #Thomas Sablik started out in the right direction, but then at least in my opinion, he kind of dropped the ball, so to speak. In particular, I think this part:
//overload the operator << to be used for printing the record Objects
ostream& operator<<(ostream& out, Athlete& a) {
out << a.getFirstName() << " " << a.getLastName() << " " << a.getNationality() << " " << a.getDistance() << "\n";
return out;
}
...is absolutely great! Even if I really wanted to say bad things about it, just about the worst I could probably come up with would be that the Athlete should be passed by const reference (and being honest, that's a pretty minor quibble).
So we have great code to write an Athlete object. But for some reason, our code for reading Athlete objects isn't the same at all. Why not?
I believe the code for reading should be almost a mirror image of the code for writing.
std::istream &operator>>(std::istream &in, Athlete &a) {
in >> a.firstName >> a.lastName >> a.nationality >> a.distance;
return in;
}
Using that, we can leave almost all the memory management to the standard library. In fact, there's probably no need for the readAthletesFromFile at all. Reading in a file becomes something like this:
std::ifstream input("file.txt");
std::vector<Athlete> list{std::istream_iterator<Athlete>(input), {}};
That's it. That reads in the entire file. Well, it could stop sooner if it encountered bad input, but it'll read as much as it can anyway.
So let's consider an example: we'll read in the data and print out the top 5 distances (and the information for each of those records):
class Athlete {
public:
friend std::ostream& operator<<(std::ostream& out, Athlete const& a) {
out << a.firstName << " " << a.lastName << " " << a.nationality << " " << a.distance << "\n";
return out;
}
friend std::istream &operator>>(std::istream &in, Athlete &a) {
in >> a.firstName >> a.lastName >> a.nationality >> a.distance;
return in;
}
bool operator<(Athlete const &other) const {
return distance < other.distance;
}
private:
std::string firstName, lastName, nationality;
double distance;
};
int main() {
std::ifstream in("file.txt");
std::vector<Athlete> list{std::istream_iterator<Athlete>(in), {}};
std::partial_sort(list.begin(), list.end()-5, list.end());
std::copy(list.end()-5, list.end(), std::ostream_iterator<Athlete>(std::cout, ""));
}
Result:
Mauro-Vinicius da-Silva Brazil 8.11
Marquise Goodwin United_States 8.11
Aleksandr Menkov Russia 8.09
Greg Rutherford Great_Britain 8.08
Christopher Tomlinson Great_Britain 8.06

Related

Regex replace from a string ( smarty) [duplicate]

This question already has an answer here:
Learning Regular Expressions [closed]
(1 answer)
Closed 6 years ago.
I'm trying to replace a string with empty space("") after a specific character(colon) ":"
example:2017 - Alpha Romeo United kingdom : New vehicle (by abc)
I want out put as "2017 - Alpha Romeo United kingdom"
it would be much appreciated if anyone can help me to write regex in smarty
Many Thanks
You could do it using the following regex (using capturing group and positive lookahead) :
input >> 2017 - Alpha Romeo United kingdom : New vehicle (by abc)
regex search >> (?=:):(.*)
replace with >> " "
output >> 2017 - Alpha Romeo United kingdom
see demo / explanation
smarty
{
assign
var = "articleTitle"
value = "2017 - Alpha Romeo United kingdom : New vehicle (by abc)"
} {
$articleTitle | regex_replace: "/(?=:):(.*)/": " "
}
private void Form1_Load(object sender, EventArgs e)
{
string str = "2017 - Alpha Romeo United kingdom : New vehicle (by abc)";
str = Regex.Replace(str, #":+(.*)", "");
MessageBox.Show(str);
}

C++ Reading tab-delimited input and skipping blank fields

I am making a program that will take a long string of tab-delimited metadata pasted into the console by the user and split them into their correct variables. I have completed the code to split the line up by tab, but there are empty fields that should be skipped in order to put the correct metadata into the correct string variable, which I can't get to work.
Here is the code that I have so far:
string dummy;
string FAImport;
cin.ignore(1000, '\n');
cout << "\nPlease copy and paste the information from the finding aid and press Enter: ";
getline(cin, FAImport);
cout << FAImport;
stringstream ss(FAImport);
auto temp = ctype<char>::classic_table();
vector<ctype<char>::mask> bar(temp, temp + ctype<char>::table_size);
bar[' '] ^= ctype_base::space;
ss.imbue(locale(cin.getloc(), new ctype<char>(bar.data())));
ss >> coTitle >> altTitle >> description >> dateSpan >> edition >> publisher >>
physicalDescription >> scale >> extentField >> medium >> dimensions >> arrangement >>
degree >> contributing >> names >> topics >> geoPlaceNames >> genre >> occupations >>
functions >> subject >> langIN >> audience >> condition >> generalNotes >> collection >>
linkToFindingAid >> source >> SIRSI >> callNumber;
checkFAImport(); //shows the values of each variable
cout << "\n\nDone";
With this code, I get this output after inputing the metadata:
coTitle = William Gates photograph with Emiliano Zapata
altTitle = 1915
description = 1915
datespan = Electronic version
edition = 1 photograph : sepia ; 11 x 13 cm
publisher = L. Tom Perry Special Collections, Harold B. Lee Library, Brigham Young University
physicalDescription = Photographs
scale = William Gates papers
extentField = http://findingaid.lib.byu.edu/viewItem/MSS%20279/Series%2011/Subseries%205/Item%20979/box%20128/folder%2012
medium = William Gates photograph with Emiliano Zapata; MSS 279; William Gates papers; L. Tom Perry Special Collections; 20th Century Western & Mormon Manuscripts; 1130 Harold B. Lee Library; Brigham Young University; Provo, Utah 84602; http://sc.lib.byu.edu/
dimensions = MSS 279 Series 11 Subseries 5 Item 979 box 128 folder 12
arrangement =
degree =
contributing =
names =
topics =
geoPlaceNames =
genre =
occupations =
functions =
subject =
langIN =
audience =
condition =
generalNotes =
collection =
linkToFindingAid =
source =
SIRSI =
callNumber =
In this example, fields like altTitle and description should be blank and skipped. Any help would be much appreciated.
You've solved the issue with spaces in the fields in an elegant manner. Unfortunately, operator>> will skip consecutive tabs, as if they were one single separator. So, good bye the empty fields ?
One easy way to do it is to use getline() to read individual string fields:
getline (ss, coTitle, '\t');
getline (ss, altTitle, '\t');
getline (ss, description, '\t');
...
Another way is

Finding repeated strings in a list

I have a list:
Class Name Surname
Math John Johnson
Programming Tom Tomson
Physics John Johnson
I'd like to find out which teacher teaches the most classes and return it as a string. I have written a simple class to save, take, add information.
template <typename tip>
string FindTeacher(List <tip> & A)
{
string module, surname, name, TeacherX;
for (int i = 0; i < A.Length(); i++)
{
tip kk = A.Take(i);
kk.Take(module, surname, name);
//stuck on this part, what to do next?
//how do i find that John Johnson teaches 2 classes, and Tom teaches only 1
}
return TeacherX;
}
I am terribly sorry if it's hard to understand me, english is not my native language.
You can use std::unodered_map, or std::map
with std::pair<std::string, std::string> > as key,
and size_t as data, and every time you get:
kk.Take(module, surname, name);
find by key(surname,name), if no insert with 1 as data, else increase data by one.

Unable to read from text file and store into string

I have a text file
0 Po Tom Mr 123AlphabetStreet Netherlands Wulu 123456 D01 Malaysia SmallAdventure 231112
0 Liu Jack Mr 123AlphabetStreet Italy Spain 123456 D02 Afghanistan TriersAdventure 030214
I am trying to read the txt file:Form.txt, store each line using getline into the variable foo
This is the working program
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int main()
{
fstream afile;
afile.open("Form.txt",ios::in);
string foo;
while (getline(afile,foo,'\n') );
{
cout<<foo
<<endl;
}
}
Nothing gets printed to the console output , I am expecting
0 Po Tom Mr 123AlphabetStreet Netherlands Wulu 123456 D01 Malaysia SmallAdventure 231112
0 Liu Jack Mr 123AlphabetStreet Italy Spain 123456 D02 Afghanistan TriersAdventure 030214
Instead i get
What is wrong with my code ??
You have a semicolon at the end of your while loop:
while (getline(afile, foo, '\n'));
// ^
This causes the extraction to be performed but only the when the loop ends does foo get printed. The last extraction doesn't extract anything which is why foo is empty, hence the empty output.

Formatting a string into multiple lines of a specific length in C/C++

Is there a common C/C++ library (or common technique) for taking a line(s) of input text and splitting the words into separate lines. Where each line of output has a max width and words are not split across lines. Whitespace being collapsed or preserved is ok. Punctuation must be preserved. Small and compact library is preferred.
I could easily spend an afternoon putting something together that works, but would like to know if there is something common out there so I don't re-invent the wheel. Bonus points if the input line can contain a format specifier to indicate an indention level for the output lines.
Example input:
"Shankle drumstick corned beef, chuck turkey chicken pork chop venison beef strip steak cow sausage. Tail short loin shoulder ball tip, jowl drumstick rump. Tail tongue ball tip meatloaf, bresaola short loin tri-tip fatback pork loin sirloin shank flank biltong. Venison short loin andouille.
Example output (target width = 60)
123456789012345678901234567890123456789012345678901234567890 Line added to show where 60 is
Shankle drumstick corned beef, chuck turkey chicken pork
chop venison beef strip steak cow sausage. Tail short loin
shoulder ball tip, jowl drumstick rump. Tail tongue ball tip
meatloaf, bresaola short loin tri-tip fatback pork loin
sirloin shank flank biltong. Venison short loin andouille.
I think what you may be looking for is:
char temp[60];
int cnt, x = 0;
do
{
cnt = 59;
strncpy(temp, src + x, 60); //Assuming the original is stored in src
while(temp[cnt] != ' ') cnt --;
temp[cnt] = (char) 0;
x += cnt + 1;
printf("%s\n", temp);
}while (x < strlen(src));
Here is a small function with which you can do what you want. It returns a list of the lines. You can remove all of the std:: if you want by using namespace std; or better using std::list; using std::string; using std::size_t; but I didn't want to assume you did.
list<string> wraptext(string input, size_t width) {
size_t curpos = 0;
size_t nextpos = 0;
list<string> lines;
string substr = input.substr(curpos, width + 1);
while (substr.length() == width + 1 && (nextpos = substr.rfind(' ')) != input.npos) {
lines.push_back(input.substr(curpos, nextpos));
curpos += nextpos + 1;
substr = input.substr(curpos, width + 1);
}
if (curpos != input.length())
lines.push_back(input.substr(curpos, input.npos));
return lines;
}
This program using that function:
int main() {
string input = "Shankle drumstick corned beef, chuck turkey chicken pork chop venison beef strip steak cow sausage. Tail short loin shoulder ball tip, jowl drumstick rump. Tail tongue ball tip meatloaf, bresaola short loin tri-tip fatback pork loin sirloin shank flank biltong. Venison short loin andouille.";
list<string> l = wraptext(input, 60);
for (auto i = l.begin(); i != l.end(); ++i)
cout << *i << endl;
cin.get();
}
Prints your example text:
Shankle drumstick corned beef, chuck turkey chicken pork
chop venison beef strip steak cow sausage. Tail short loin
shoulder ball tip, jowl drumstick rump. Tail tongue ball tip
meatloaf, bresaola short loin tri-tip fatback pork loin
sirloin shank flank biltong. Venison short loin andouille.
If you want to do the job in C, you could try the w_wrap.c and w_wrap.h that I posted to Fidonet C_ECHO 20 years ago or so.
If you want to do the job in C++, it seems like you could simplify the code a bit:
#include <sstream>
#include <string>
#include <iostream>
void wrap(std::string const &input, size_t width, std::ostream &os, size_t indent = 0)
{
std::istringstream in(input);
os << std::string(indent, ' ');
size_t current = indent;
std::string word;
while (in >> word) {
if (current + word.size() > width) {
os << "\n" << std::string(indent, ' ');
current = indent;
}
os << word << ' ';
current += word.size() + 1;
}
}
#ifdef TEST
int main() {
char *in = "Shankle drumstick corned beef, chuck turkey chicken pork chop"
" venison beef strip steak cow sausage. Tail short loin shoulder"
" ball tip, jowl drumstick rump. Tail tongue ball tip meatloaf,"
" bresaola short loin tri-tip fatback pork loin sirloin shank"
" flank biltong. Venison short loin andouille.";
wrap(in, 60, std::cout);
return 0;
}
#endif
To add indentation, you'd use something like:
wrap(in, 60, std::cout, 5);
Given that you're doing I/O, it probably doesn't matter much in this case, but if you were doing this under other circumstances, you might want to consider a different algorithm. Rather than copy one word at a time until you exceed the specified width, you can go directly to the maximum line width in the input, and walk backwards through the input string from there until you find whitespace. At least given typical word lengths, you'll only walk back somewhere around 3 characters on average, rather than walking forward through an average of (say) 60 characters. This would be particularly relevant using something like C strings, where you were storing a pointer to the beginning of each line, without copying the content.
Here's my approach, it's certainly not the fastest but I tried to make it as readable as possible. The result is the same as your example.
#include <iostream>
#include <string>
std::string splitInLines(std::string source, std::size_t width, std::string whitespace = " \t\r")
{
std::size_t currIndex = width - 1;
std::size_t sizeToElim;
while ( currIndex < source.length() )
{
currIndex = source.find_last_of(whitespace,currIndex + 1);
if (currIndex == std::string::npos)
break;
currIndex = source.find_last_not_of(whitespace,currIndex);
if (currIndex == std::string::npos)
break;
sizeToElim = source.find_first_not_of(whitespace,currIndex + 1) - currIndex - 1;
source.replace( currIndex + 1, sizeToElim , "\n");
currIndex += (width + 1); //due to the recently inserted "\n"
}
return source;
}
int main() {
std::string source = "Shankle drumstick corned beef, chuck turkey chicken pork chop venison beef strip steak cow sausage. Tail short loin shoulder ball tip, jowl drumstick rump. Tail tongue ball tip meatloaf, bresaola short loin tri-tip fatback pork loin sirloin shank flank biltong. Venison short loin andouille.";
std::string result = splitInLines(source , 60);
std::cout << result;
return 0;
}
Ya, load it into a character array, then use strtok, to break it into words, using a space as the word seperator.
take a function for your work like:
void put_multiline(const char *s,int width)
{
int n,i=0;
char t[100];
while( 1==sscanf(s,"%99s%n",t,&n) )
{
if( i+strlen(t)>width ) puts(""),i=0;
printf("%s%s",i?++i," ":"",t);i+=strlen(t);
s+=n;
}
}
strtok will destroy your string, this solution not. This function will also work on all whitespaces not only space/tab.
You could probably use regex substitution: replace /(.*){,60}? +/ with $1\n, advance the string pointer and repeat (note: the ? is supposed to mean non-greedy matching).
If properly implemented, the conversion could be even made in-place.
Here is a regex-based approach. Different from the approaches in other answers, it also handles newlines in the input string gracefully.
#include <regex>
#include <iostream>
#include <string>
int main() {
auto test = std::string{"Shankle drumstick corned beef, chuck turkey chicken pork chop venison beef strip steak cow sausage. Tail short loin shoulder ball tip, jowl drumstick rump. Tail tongue ball tip meatloaf, bresaola short loin tri-tip fatback pork loin sirloin shank flank biltong. Venison short loin andouille."};
// Consume 60 characters that are followed by a space or the end of the input string
auto line_wrap = std::regex{"(.{1,60})(?: +|$)"};
// Replace the space or the end of the input string with a new line
test = regex_replace(test, line_wrap, "$1\n");
// Trim the new line added for the end of the input string
test.resize(test.size() - 1);
std::cout << test << std::endl;
}