C++ - Parsing a .txt-file with mulltiple delimiters, display string - c++

everyone!
I'm new to C++, alas I make silly mistakes.
This is a snippet of a .txt-file's content:
<tag attr1="value1" attr2="value2" ... >
What I'm trying to accomplish is parsing through the .txt-file, generating the following output:
Tag: tag
name: attr1
value: value1
name: attr2
value: value2
What I've done so far didn't work (my problem is the delimiters):
#include<iostream>
#include <sstream>
#include <string>
#include <vector>
#include <fstream>
using namespace std;
struct tagline{
string tag;
string attributeN;
string attributeV;
};
int main(){
vector<tagline> information;
string line;
tagline t;
ifstream readFile("file.txt");
while(getline(readFile,line)){
stringstream in(line);
getline(in,t.tag);
getline(in,t.attributeN,'=');
getline(in,t.attributeV,'"');
information.push_back(t);
}
vector<tagline>::iterator it = information.begin();
for(; it != information.end(); it++){
cout << "Tag: " << (*it).tag << " \n"
<< "name: " << (*it).attributeN << " \n"
<< "value: " << (*it).attributeV << " \n";
}
return 0;
}
All I get is a plain display of the snippet as it's formatted in the .txt-file:
<tag attr1="value1" attr2="value2" ... >
I would be happy if someone could help. Thank you!

This would be better handled using an HTML/XML parser (depending on what your file actually contains).
That being said, you are not parsing the lines correctly.
Your first call to getline(in,t.tag); is not specifying a delimiter, so it reads the entire line, not just the first word. You would have to use getline(in, t.tag, ' '); instead.
Also, your tags can have multiple attributes, but you are only reading and storing the first attribute, ignoring the rest. You need a loop to read all of them, and a std::vector to store them all into.
Try something more like this instead:
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <fstream>
using namespace std;
struct tagattribute {
string name;
string value;
};
struct tagline {
string tag;
vector<tagattribute> attributes;
};
int main() {
vector<tagline> information;
string line;
ifstream readFile("file.txt");
while (getline(readFile, line)) {
istringstream in(line);
tagline t;
tagattribute attr;
in >> ws;
char ch = in.get();
if (ch != '<')
continue;
if (!(in >> t.tag))
continue;
do
{
in >> ws;
ch = in.peek();
if (ch == '>')
break;
if (getline(in, attr.name, '=') &&
in.ignore() &&
getline(in, attr.value, '"'))
{
t.attributes.push_back(attr);
}
else
break;
}
while (true);
information.push_back(t);
}
vector<tagline>::iterator it = information.begin();
for(; it != information.end(); ++it) {
cout << "Tag: " << it->tag << "\n";
vector<tagattribute>::iterator it2 = it->attributes.begin();
for(; it2 != it->attributes.end(); ++it2) {
cout << "name: " << it2->name << "\n"
<< "value: " << it2->value << "\n";
}
cout << "\n";
}
return 0;
}
Live demo
Alternatively, consider writing some custom operator>> to help with the parsing, eg:
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <fstream>
using namespace std;
struct tagattribute {
string name;
string value;
};
istream& operator>>(istream &in, tagattribute &attr)
{
getline(in, attr.name, '=');
in.ignore();
getline(in, attr.value, '"');
return in;
}
struct tagline {
string tag;
vector<tagattribute> attributes;
};
istream& operator>>(istream &in, tagline &t)
{
tagattribute attr;
in >> ws;
char ch = in.get();
if (ch != '<')
{
in.setstate(ios_base::failbit);
return in;
}
if (!(in >> t.tag))
return in;
do
{
in >> ws;
ch = in.peek();
if (ch == '>')
{
in.ignore();
break;
}
if (!(in >> attr))
break;
t.attributes.push_back(attr);
}
while (true);
return in;
}
int main() {
vector<tagline> information;
string line;
ifstream readFile("file.txt");
while (getline(readFile, line)) {
istringstream in(line);
tagline t;
if (in >> t)
information.push_back(t);
}
vector<tagline>::iterator it = information.begin();
for(; it != information.end(); ++it) {
cout << "Tag: " << it->tag << "\n";
vector<tagattribute>::iterator it2 = it->attributes.begin();
for(; it2 != it->attributes.end(); ++it2) {
cout << "name: " << it2->name << "\n"
<< "value: " << it2->value << "\n";
}
cout << "\n";
}
return 0;
}
Live demo

Well, I would try to do something like this using this wonderful answer:
struct xml_skipper : std::ctype<char> {
xml_skipper() : ctype(make_table()) { }
private:
static mask* make_table() {
const mask* classic = classic_table();
static std::vector<mask> v(classic, classic + table_size);
v[','] |= space;
v['"'] |= space;
v['='] |= space;
v['<'] |= space;
v['>'] |= space;
return &v[0];
}
};
Then, what you can do is just keep reading:
ifstream readFile("file.txt");
while(getline(readFile,line)){
istringstream in(line);
in.imbue(std::locale(in.getloc(), new xml_skipper));
in >> t.tag >> t.attributeN >> t.attributeV;
information.push_back(t);
}
//...
Do note that this will break if values or attribute names have whitespaces.
If you want something more serious, you will need to write lexer, syntax tree builder and semantics tree builder.
Full code
#include<iostream>
#include <sstream>
#include <string>
#include <vector>
#include <fstream>
#include <sstream>
using namespace std;
struct tagline{
string tag;
string attributeN;
string attributeV;
};
struct xml_skipper : std::ctype<char> {
xml_skipper() : ctype(make_table()) { }
private:
static mask* make_table() {
const mask* classic = classic_table();
static std::vector<mask> v(classic, classic + table_size);
v[','] |= space;
v['"'] |= space;
v['='] |= space;
v['<'] |= space;
v['>'] |= space;
return &v[0];
}
};
int main(){
vector<tagline> information;
string line;
tagline t;
std::istringstream readFile{"<tag attr1=\"value1\" attr2=\"value2\" ... >"};
while(getline(readFile,line)){
istringstream in(line);
in.imbue(std::locale(in.getloc(), new xml_skipper));
in >> t.tag >> t.attributeN >> t.attributeV;
information.push_back(t);
}
vector<tagline>::iterator it = information.begin();
for(; it != information.end(); it++){
cout << "Tag: " << (*it).tag << " \n"
<< "name: " << (*it).attributeN << " \n"
<< "value: " << (*it).attributeV << " \n";
}
}
Live on Wandbox.

If your input may vary within the boundaries of the xml specification, an XML-parser might be a better approach than parsing the string "manually".
Just to show how this could look like, see the following code. It is based on tinyxml2, which just requires to include a single .cpp / .h-file in your project. You could - of course - also use any other xml libraries; this is just for demonstration purpose:
#include <iostream>
#include "tinyxml2.h"
using namespace tinyxml2;
int main()
{
const char* test = "<tag attr1='value1' attr2 = \"value2\"/>";
XMLDocument doc;
doc.Parse(test);
XMLElement *root = doc.RootElement();
if (root) {
cout << "Tag: " << root->Name() << endl;
const XMLAttribute *attrib = root->FirstAttribute();
while (attrib) {
cout << "name: " << attrib->Name() << endl;
cout << "value : " << attrib->Value() << endl;
attrib = attrib->Next();
}
}
}

Related

how do i read a csvfile faster in c++

This is my current code:
std::vector <OrderBookEntry> entries;
std::ifstream csvFile{ csvFilename };
std::string line;
if (csvFile.is_open()) {
while (std::getline(csvFile, line)) {
try {
std::stringstream ss(line);
OrderBookEntry obe = stringsToOBE(tokenise(line, ','));
entries.push_back(obe);
}
catch (const std::exception& e) {
std::cout << "CSVReader::readCSV bad data" << std::endl;
}
}//end of while loop
}
std::cout << "CSVReader::readCSV read " << entries.size() << " entries" << std::endl;
return entries;
}
OrderBookEntry::OrderBookEntry(double _price,
double _amount,
std::string _timestamp,
std::string _product,
OrderBookType _orderType,
std::string _username)
: price(_price),
amount(_amount),
timestamp(_timestamp),
product(_product),
orderType(_orderType),
username(_username)
{
}
example of csv file:
Hi all, I have a csvfile (1 mill+) rows and i am trying to read and store the entries faster. Currently it takes a bit of time before my program is able to read the data and run.
How do i read the csvFile quicker? Thanks!
Difficult to answer.
Please see below a solution that can create a test file with 1 million records.
I then make only the input buffer bigger, without any further optimization.
Then, all lines of the test file are read and parsed in 2.6seconds.
The question is now: Is this fast or is this slow? Please comment.
#include <iostream>
#include <fstream>
#include <string>
#include <unordered_map>
#include <iomanip>
#include <array>
#include <random>
#include <vector>
#include <chrono>
#include <algorithm>
#include <iterator>
const std::array<std::string, 3> OTString{ "bid","offer","trade" };
enum class OrderType : int { bid, offer, trade, invalid };
const std::unordered_map<std::string, OrderType> OrderTypeMap{ {OTString[0],OrderType::bid}, {OTString[1],OrderType::offer}, {OTString[2],OrderType::trade} };
OrderType getOrderType(std::string& ot) {
if (const auto it = OrderTypeMap.find(ot); it != OrderTypeMap.cend())
return it->second;
else
return OrderType::invalid;
}
const std::string testFileName{ "test.txt" };
void createTestFile() {
unsigned ms{};
unsigned second{};
unsigned minute{};
std::random_device rd; // Will be used to obtain a seed for the random number engine
std::mt19937 gen(rd()); // Standard mersenne_twister_engine seeded with rd()
std::uniform_real_distribution<> dis1(0.01, 1);
std::uniform_real_distribution<> dis2(1.0, 1000.0);
std::string product{"AAAAAAAAAAAA"};
if (std::ofstream ofs{ testFileName }; ofs) {
ofs << std::setfill('0') << std::right;
for (std::size_t row{}; row < 1'000'000; ++row) {
ofs << std::setw(2) << minute << ':' << std::setw(2) << second << ':' << ms << ','
<< product << ',' << OTString[row%3] << ',' << dis1(gen) << ',' << dis2(gen) << '\n';
++product[product.length() - 1];
for (std::size_t i{ product.length() - 1 }; i > 0; --i)
if (product[i] > 'Z') {
product[i] = 'A';
++product[i - 1];
}
else
break;
if (row % 100 == 0) {
++ms;
if (ms > 9) {
ms = 0;
++second;
if (second > 59) {
second = 0;
++minute;
}
}
}
}
}
else
std::cerr << "\n\n*** Error: Could not open file '" << testFileName << "' for writing.\n\n";
}
// For measuring durations
struct Timer {
std::chrono::time_point<std::chrono::high_resolution_clock> startTime{};
long long elapsedTime{};
void start() { startTime = std::chrono::high_resolution_clock::now(); }
void stop() { elapsedTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - startTime).count(); }
friend std::ostream& operator << (std::ostream& os, const Timer& t) { return os << t.elapsedTime << " ms "; }
};
constexpr std::size_t IOBufSize = 5'000'000;
char ioBuf[IOBufSize];
struct OrderBookEntry {
std::string timestamp{};
std::string product{};
OrderType orderType{};
double amount{};
double price{};
friend std::istream& operator >> (std::istream& is, OrderBookEntry& obe) {
std::string ot{}; char c;
std::getline(is >> std::ws, obe.timestamp, ',');
std::getline(is >> std::ws, obe.product, ',');
std::getline(is >> std::ws, ot, ',');
is >> obe.amount >> c >> obe.price;
obe.orderType = getOrderType(ot);
return is;
}
};
int main() {
//createTestFile();
std::vector<OrderBookEntry> obe{};
obe.reserve(1'500'000);
if (std::ifstream ifs{ testFileName }; ifs) {
// To speed up reading of the file, we will set a bigger input buffer
ifs.rdbuf()->pubsetbuf(ioBuf, IOBufSize);
Timer t; t.start();
std::copy(std::istream_iterator<OrderBookEntry>(ifs), {}, std::back_inserter(obe));
std::cout << "\nRecords read: "<< obe.size() << '\n';
t.stop();
std::cout << t << '\n';
}
else
std::cerr << "\n\n*** Error: Could not open file '" << testFileName << "' for reading.\n\n";
}

C++ Only reading last line when reading text from a list

Ive written a loop function that compares a type String to another String, like such
double robotComplexity() {
double complexity;
if(newChar == "A") {
cout << "Character: " << newChar;
} else {
cout << "Program isnt working! :(";
}
if(complexity > 100) {
complexity = 100;
}
cout << "\nThe Robot Complexity is: " << complexity << endl;
return complexity;
}
However when the program runs, the latter of the if statement returns "Program does not work :("
However within the text file there is in fact a record that contains a character of A
A:Head:1:2:15.
B:Torso:0:6:5.
C:Leg:0:4:6.
D:Arm:0:4:8.
E:Tail:0:6:2.
I am unsure is why the loop function doesnt recongize it. The expected output for would return "A" in string form, as the program loops for the records of the file until it locates the character A linked to a variable partCode. As of now it does not do this.
My full program is as follows
#include <iostream>
#include <string>
#include <conio.h>
#include <stdio.h>
#include "driver.h"
#include <sstream>
#include <fstream>
#include <fstream>
#include <vector>
using namespace std;
//declaration of customer varialbes
std::string customerName;
std::string projectName;
std::string partNumber;;
//declaration of parts variables
char partCode;
std::string partName;
int maximum;
int minimum;
int complexity;
//declaration of builder variables
std::string name;
int ability;
int variability;
std::vector<string> builderVector;
std::vector<string> partsVector;
std::vector<string> customerVector;
std::ifstream buildersList("Builders.txt");
std::ifstream partsList("Parts.txt");
std::ifstream customerList("Customers.txt");
std::string outputFile = "output.txt";
std::string input;
std::string newChar;
std::stringstream convertChar;
void readFile() //function to read Builders, Customers and Parts text file
{
std::string line;
while (std::getline(partsList, line)) {
line.pop_back(); //removing '.' at end of line
std::string token;
std::istringstream ss(line);
convertChar << partCode;
convertChar >> newChar;
// then read each element by delimiter
int counter = 0;//number of elements you read
while (std::getline(ss, token, ':')) { //spilt into different records
switch (counter) {//put into appropriate value-field according to element-count
case 0: newChar = token; //convert partCode from a char to a string
break;
case 1: partName = token; break;
case 2: maximum = stoi(token); break;
case 3: minimum = stoi(token); break;
case 4: complexity = stoi(token); break;
default: break;
}
counter++; //increasing counter
}
//cout << "Part Code: " << newChar << endl;
// cout << "Part Name: "<< partName << endl;
// cout << "Maximum: "<< maximum << endl;
// cout << "Minimum: "<< minimum << endl;
// cout << "Complexity: "<< complexity << endl;
}
while (std::getline(customerList, line)) {
line.pop_back();//removing '.' at end of line
std::string token;
std::istringstream ss(line);
// then read each element by delimiter
int counter = 0;//number of elements you read
while (std::getline(ss, token, ':')) {//spilt into different records
switch (counter) {//put into appropriate value-field according to element-count
case 0: customerName = token; break;
case 1: projectName = token; break;
case 2: partNumber = token; break;
default: break;
}
counter++;//increasing counter
}
// cout << "Customers name: " << customerName << endl;
// cout << "Project name: "<< projectName << endl;
// cout << "Part number: "<< partNumber << endl;
}
}
double robotComplexity() {
double complexity;
while(partsList.is_open) {
if(newChar == "A") {
cout << "Character: " << newChar;
} else {
cout << "Program isnt working! :(";
}
}
if(complexity > 100) {
complexity = 100;
}
cout << "\nThe Robot Complexity is: " << complexity << endl;
return complexity;
}
double robotVariability() {
double variability;
cout << "\nThe Robot Variability is: " << variability << endl;
return variability;
}
void writeFile() //writes to a file output.txt the end calculations.
{
}
Main file
#include <iostream>
#include <string>
#include <conio.h>
#include <stdio.h>
#include "driver.h"
#include "implementation.cpp"
#include <fstream>
#include <vector>
using namespace std;
int main() {
readFile();
writeFile();
robotComplexity();
getch();
return 0;
}
If anyone can provide a solution that would be great.
I have just rearranged your code, as it should look like, and it works properly.
In coding, having a messy code means having bugs you can not trace. Split your code to the smallest logical components, and write the code so it will be readable (no need for comments in code!). I have done nothing fancy other than reorganizing the parsing code.
Also, notice that you have a dot (.) in the end of each line, you should either remove it from the input, or handle it in code.
// these are the streams used:
#include <istream>
#include <sstream>
std::vector<std::string> parse_stream(std::istream& stream)
{
std::vector<std::string> res{};
std::string line;
while(std::getline(stream, line))
{
res.push_back(line);
}
return res;
}
std::vector<std::string> split_line(std::istream& stream, char delimiter)
{
std::vector<std::string> res{};
std::string token;
while(std::getline(stream, token, delimiter))
{
res.push_back(token);
}
return res;
}
void print_vector(const std::vector<std::string>& vec)
{
for (const std::string& str: vec)
{
std::cout << str << ", ";
}
std::cout << std::endl;
}
int main()
{
std::string in_str = "A:Head:1:2:15\nB:Torso:0:6:5\nC:Leg:0:4:6\nD:Arm:0:4:8\nE:Tail:0:6:2";
std::istringstream ss{in_str};
auto parsed_stream = parse_stream(ss);
print_vector(parsed_stream);
for (const auto& line : parsed_stream)
{
std::istringstream line_ss{line};
auto splited_line = split_line(line_ss, ':');
print_vector(splited_line);
}
}
The output is:
A:Head:1:2:15, B:Torso:0:6:5, C:Leg:0:4:6, D:Arm:0:4:8, E:Tail:0:6:2,
A, Head, 1, 2, 15,
B, Torso, 0, 6, 5,
C, Leg, 0, 4, 6,
D, Arm, 0, 4, 8,
E, Tail, 0, 6, 2,

Am I building a correct way of organizing characters so I can count them?

The code is supposed to be able to count the characters in total then count each time they appear within a text file. I tried building a struct that makes an array which is an integer and char array at the same time so that I can have the counting in the same place as my array. But now I am stuck. I've looked online a lot but cannot find what I need to help me. Anyone got some advice? Also in the code if you see anything that should be changed I appreciate the tips! I am newer at c++ so go easy on me please.
Structs, multiple arrays, searching internet for answers
#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>
struct letters {
int count;
char letter;
};
constexpr int SIZE = 10000;
std::string fileName, word;
int count = 0, charCount = 0;
int Alphabet[26];
letters chars[];
void getFileName(std::string& fileName);
int countWords(int& count, std::string& fileName, std::string word);
int countChar(int& charCount, std::string& fileName, letters chars[]);
void sortChars(letters chars[SIZE], int SIZE);
int main()
{
getFileName(fileName);
countWords(count, fileName, word);
countChar(charCount, fileName, chars);
sortChars(chars, SIZE);
return 0;
}
void getFileName(std::string& fileName)
{
std::cout << "Please enter the name of the file followed by the type (ex: text.txt) : " << std::endl;
std::getline(std::cin, fileName);
}
int countWords(int& count, std::string& fileName, std::string word)
{
count = 0;
std::ifstream infile(fileName);
while (infile >> word) {
count++;
}
std::cout << count << std::endl;
return count;
}
int countChar(int& charCount, std::string& fileName, letters chars[])
{
std::ifstream infile(fileName);
while (infile >> chars->letter) {
count++;
}
std::cout << charCount;
return charCount;
}
void sortChars(letters chars[SIZE], int SIZE)
{
int i = 0;
std::ifstream infile(fileName);
while (infile >> chars[i].letter) {
for (int i = 0; i <= chars->count; i++) {
if (infile == chars[i].letter) {
chars[i].count++;
}
}
}
}
void printCount()
{
std::cout << count << std::endl;
std::cout << charCount << std::endl;
std::ifstream infile(fileName);
}
The struct should count the number of times 'A' or 'a', should be able to convert to one case, but I can do this after it counts one or the other. My tester file is in all lowercase so that would be a good place to start.
Even bigger hint, use a std::unordered_map to count the characters:
#include <cstdlib> // EXIT_FAILURE
#include <cctype> // std::isupper(), std::tolower()
#include <string> // std::string<>, std::getline()
#include <unordered_map> // std::unordered_map<>
#include <iostream> // std::ifstream
#include <fstream> // std::cout, std::cerr
int main()
{
std::string file_name;
if (!std::getline(std::cin, file_name)) {
std::cerr << "Input error. :(\n\n";
return EXIT_FAILURE;
}
std::ifstream is{ file_name };
if (!is.is_open()) {
std::cerr << "Couldn't open \"" << file_name << "\" for reading. :(\n\n";
return EXIT_FAILURE;
}
std::size_t num_words = 0;
std::unordered_map<char, std::size_t> char_counts;
for (std::string word; is >> word; ++num_words) {
for (auto ch : word) {
if (std::isupper(ch))
ch = std::tolower(ch);
++char_counts[ch];
}
}
for (auto const &count : char_counts)
std::cout << "'" << count.first << "': " << count.second << '\n';
std::cout << "Number of words: " << num_words << "\n\n";
} // when control reaches the end of main() without encountering a return-statement
// it has the same effect as return 0;
If you insist on splitting that few lines of code up into functions:
#include <cstdlib> // EXIT_FAILURE
#include <cctype> // std::isupper(), std::tolower()
#include <string> // std::string<>, std::getline()
#include <unordered_map> // std::unordered_map<>
#include <iostream> // std::ifstream
#include <fstream> // std::cout, std::cerr
std::string get_file_name()
{
std::cout << "Filename: ";
std::string file_name;
if (!std::getline(std::cin, file_name))
std::cerr << "Input error. :(\n\n";
return file_name;
}
std::ifstream open_file(std::string file_name)
{
std::ifstream file{ file_name };
if (!file.is_open())
std::cerr << "Couldn't open \"" << file_name << "\" for reading. :(\n\n";
return file;
}
std::size_t get_file_stats(std::istream &is, std::unordered_map<char, std::size_t> &char_counts)
{
std::size_t num_words = 0;
for (std::string word; is >> word; ++num_words) {
for (auto ch : word) {
if (std::isupper(ch))
ch = std::tolower(ch);
++char_counts[ch];
}
}
return num_words;
}
int main()
{
std::string file_name{ get_file_name() };
if (!file_name.length())
return EXIT_FAILURE;
std::ifstream is{ open_file(file_name) };
if (!is.is_open())
return EXIT_FAILURE;
std::unordered_map<char, std::size_t> counts;
std::cout << "Number of words: " << get_file_stats(is, counts) << "\n\n";
for (auto const &count : counts)
std::cout << "'" << count.first << "': " << count.second << '\n';
}

How to read data from a text file into a struct

I'm completely new to C++ and currently I'm trying to read very basic text file which look like this:
Dr John Doe
British
2
Soccer
Swimming
and my expected output should look like:
My information
Name: John Doe
Nationality: British
I have 2 hobbies:
1. Soccer
2. Swimming
My header file:
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <cstdlib>
#include <ctime>
#include <vector>
using namespace std;
const int MAX = 80;
const int MAXNO = 5;
enum Title {Miss, Mrs, Mr, Dr, Unknown};
struct Date
{
int day;
int month;
int year;
};
struct MyInfo
{
char name [MAX];
char national [MAX];
int noOfHobbies;
char hobby [MAXNO][MAX];
};
void getMyInfo (fstream& , char[] , MyInfo&);
void displayMyInfo (MyInfo);
My functions:
#include "Lab_1.h"
void getMyInfo (fstream& afile,char fileName[], MyInfo& x) {
afile.open (fileName);
if (!afile)
{
cout << "Binary file " << fileName << " opened for creation failed" << endl;
exit (-1);
}
cout << "\n" << "Begin reading of " << fileName << endl;
string line;
while(getline(afile, line))
{
afile >> x.national;
afile >> x.noOfHobbies;*/
if (afile >> x.name >> x.national >> x.noOfHobbies) {
cout << "Name: " << x.name << ", "
<< "National: " << x.national << ", "
<< "noOfHobbies: " << x.noOfHobbies << ", "
<< endl;
}
}
}
void displayMyInfo (MyInfo x) {
}
My main function:
#include "Lab_1.h"
int main () {
fstream afile;
MyInfo x;
string fileName;
getMyInfo(afile,"textfile.txt",x);
//displayMyInfo(x);
afile.close ();
}
The above code output nothing because I just put everything I understand over the forum with similar question. Since I'm already stuck for 1 day even though I've already done a lot of research but most of them suggest to use vector which I'm not familiar with at this moment, so can someone give me a solution to this problem? Thank you very much for your help in advance.
Random act of madness kindness:
Live On Coliru
#include <fstream>
#include <set>
struct Person {
std::string name;
std::string nationality;
std::set<std::string> hobbies;
friend std::istream& operator>>(std::istream& is, Person& into) {
size_t n = 0;
if (getline(is, into.name) &&
getline(is, into.nationality) &&
is >> n && is.ignore(1024, '\n'))
{
while (n--) {
std::string hobby;
if (getline(is, hobby))
into.hobbies.insert(hobby);
else
is.setstate(std::ios::failbit);
}
}
return is;
}
};
#include <iostream>
int main() {
std::ifstream ifs("input.txt");
Person p;
if (ifs >> p) {
std::cout << "My information\n";
std::cout << p.name << "\n";
std::cout << p.nationality << "\n";
std::cout << "I have " << p.hobbies.size() << " hobbies:\n";
size_t counter = 0;
for(auto const& hobby : p.hobbies) {
std::cout << ++counter << ". " << hobby << "\n";
}
} else {
std::cerr << "Parse failure\n";
}
}

text file contents to array

I have a text file-> info.txt which contains the follow Id,games,amount
info.txt
1,left4dead,50
2,warcraft3,60
I know how to extract the details using vectors with my codes shown below.
stock.h
#ifndef stock_stock_h
#define stock_stock_h
#include <iostream>
class stock {
public:
stock() {
columnOneText = " ";
columnTwoText = " ";
}
stock(std::string columnOneText,
std::string columnTwoText
);
std::string getColumnOne();
std::string getColumnTwo();
void setItemId(std::string columnOneText);
void setItemDescription(std::string columnTwoText);
private:
std::string columnOneText, columnTwoText;
};
#endif
main.cpp
#include "stock.h"
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <vector>
using namespace std;
stock::stock(string columnOneText,
string columnTwoText)
{
setItemId(columnOneText);
setItemDescription(columnTwoText);
};
string stock::getColumnOne() {
return columnOneText;
}
string stock::getColumnTwo() {
return columnTwoText;
}
void stock::setItemId(string columnOneText) {
this->columnOneText = columnOneText;
}
void stock::setItemDescription(std::string columnTwoText) {
this->columnTwoText = columnTwoText;
}
int main(){
vector<stock> itemDetails;
string line;
string columnOneText;
string columnTwoText;
ifstream readFile("info.txt");
while(getline(readFile,line)) {
stringstream iss(line);
getline(iss, columnOneText,',');
getline(iss, columnTwoText, ',');
//consturctor
stock splitedColumns(columnOneText,
columnTwoText
);
itemDetails.push_back(splitedColumns);
}
readFile.close();
cout << "this is column One in text file" << endl;
for (int i =0; i<itemDetails.size(); i++) {
cout << itemDetails[i].getColumnOne() << " " << endl;
}
cout << "this is column Two in text file" << endl;
for (int i =0; i<itemDetails.size(); i++) {
cout << itemDetails[i].getColumnTwo() << " " << endl;
}
}
what I don't really know is how to extract the details using arrays instead of using vectors.
I have tried this but it doesn't seem to work
string line;
string columnOneText;
string columnTwoText;
string columnOneTextArray[7];
while(getline(readFile,line)) {
stringstream iss(line);
getline(iss, columnOneText,',');
getline(iss, columnTwoText, ',');
for(int i=0; i<8; i++) {
columnOneTextArray[i] = columnOneText;
cout << columnOneTextArray[i];
}
}
You guys might ask why do I want to do it in arrays instead of using vector,
I just curious and exploring how it can be done using arrays.
You problem can be solved in a lot simpler manner by using struct definitions in addition to using vectors (your use of classes is a bit of overkill, IMHO):
#include <fstream>
#include <iostream>
#include <cstdlib>
#include <vector>
#include <sstream>
//this utilizes a more idiomatic naming convention as opposed to `columnX`
typedef struct Stock {
int id;
std::string games;
int amount;
} Stock;
//Syntactic sugar for easier output
std::ostream& operator << (std::ostream& in, const Stock& stock) {
in << stock.id << " " << stock.games << " " << stock.amount;
return in;
}
int main() {
std::string filename = "info.txt";
std::fstream ifile;
std::vector <Stock> inventory;
Stock stock_t;//a temporary Stock variable
std::string temp;
ifile.open(filename.c_str(), std::ios::in);
if (!ifile.is_open()) {
std::cerr << "There was a problem opening the input file: \"" << filename << "\"\n";
exit(1);
}
while (ifile >> temp) {
std::istringstream iss(temp);
int column = 0;
//break each constituent line by using the comment as a delimiter:
while(std::getline(iss, temp, ',')) {
if (column == 0) {
stock_t.id = std::atoi(temp.c_str());
}
else if (column == 1) {
stock_t.games = temp;
}
else if (column == 2) {
stock_t.amount = std::atoi(temp.c_str());
}
++column;
}
inventory.push_back(stock_t);
}
for (int i = 0; i < inventory.size(); ++i) {
std::cout << inventory[i] << "\n";
}
ifile.close();
return 0;
}
Which outputs:
1 left4dead 50
2 warcraft3 60