I have a list of cities that I'm formatting like this:
{town, ...},
{...},
...
Reading and building each town and creating town1, town2,.... works
The problem is when I output it, 1st line works {town, ...}, but the second line crashes.
Any idea why?
I have [region] [town] (excel table).
So each region repeats by how many towns are in it.
Each file has 1 region/town per line.
judete contains each region repeated 1 time.
AB
SD
PC
....
orase contains the towns list.
town1
town2
....
orase-index contains the region of each town
AB
AB
AB
AB
SD
SD
SD
PC
PC
...
I want an output like this {"town1", "town2", ...} and each row (row 5) contains the town that belong to the region from judete at the same row (row 5).
Here's my code:
#include<stdio.h>
#include<string.h>
char judet[100][100];
char orase[50][900000];
char oras[100], ceva[100];
void main ()
{
int i=0, nr;
FILE *judete, *index, *ORASE, *output;
judete = fopen("judete.txt", "rt");
index = fopen("orase-index.txt", "rt");
ORASE = fopen("orase.txt", "rt");
output = fopen("output.txt", "wt");
while( !feof(judete) )
{
fgets(judet[i], 100, judete);
i++;
}
nr = i;
char tmp[100];
int where=0;
for(i=0;i<nr;i++)
strcpy(orase[i],"");
while( !feof(index) )
{
fgets(tmp, 100, index);
for(i=0;i<nr;i++)
{
if( strstr(judet[i], tmp) )
{
fgets(oras, 100, ORASE);
strcat(ceva, "\"");
oras[strlen(oras)-1]='\0';
strcat(ceva, oras);
strcat(ceva, "\", ");
strcat(orase[i], ceva);
break;
}
}
}
char out[900000];
for(i=0;i<nr;i++)
{
strcpy(out, "");
strcat(out, "{");
strcat(out, orase[i]); //fails here
fprintf(output, "%s},\n", out);
}
}
The result I get from running the code is:
Unhandled exception at 0x00D4F7A9 (msvcr110d.dll) in orase-judete.exe: 0xC0000005: Access violation writing location 0x00A90000.
You don't clear orase array, beacause your loop
for(i-0;i<nr;i++)
strcpy(orase[i],"");
by mistake ('-' instead of '=') executes 0 times.
I think you need to start by making up your mind whether you're writing C or C++. You've tagged this with both, but the code looks like it's pure C. While a C++ compiler will accept most C, the result isn't what most would think of as ideal C++.
Since you have tagged it as C++, I'm going to assume you actually want (or all right with) C++ code. Well written C++ code is going to be enough different from your current C code that it's probably easier to start over than try to rewrite the code line by line or anything like that.
The immediate problem I see with doing that, however, is that you haven't really specified what you want as your output. For the moment I'm going to assume you want each line of output to be something like this: "{" <town> "," <town> "}".
If that's the case, I'd start by noting that the output doesn't seem to depend on your judete file at all. The orase and orase-index seem to be entirely adequate. For that, our code can look something like this:
#include <iostream>
#include <string>
#include <iterator>
#include <fstream>
#include <vector>
// a class that overloads `operator>>` to read a line at a time:
class line {
std::string data;
public:
friend std::istream &operator>>(std::istream &is, line &l) {
return std::getline(is, l.data);
}
operator std::string() const { return data; }
};
int main() {
// open the input files:
std::ifstream town_input("orase.txt");
std::ifstream region_input("orase-index.txt");
// create istream_iterator's to read from the input files. Note
// that these iterate over `line`s, (i.e., objects of the type
// above, so they use its `operator>>` to read each data item).
//
std::istream_iterator<line> regions(region_input),
towns(town_input),
end;
// read in the lists of towns and regions:
std::vector<std::string> town_list {towns, end};
std::vector<std::string> region_list {regions, end};
// write out the file of town-name, region-name:
std::ofstream result("output.txt");
for (int i=0; i<town_list.size(); i++)
result << "{" << town_list[i] << "," << region_list[i] << "}\n";
}
Noe that since this is C++, you'll typically need to save the source as something.cpp instead of something.c for the compiler to recognize it correctly.
Edit: Based on the new requirements you've given in the comments, you apparently want something closer to this:
#include <iostream>
#include <string>
#include <iterator>
#include <fstream>
#include <vector>
#include <map>
// a class that overloads `operator>>` to read a line at a time:
class line {
std::string data;
public:
friend std::istream &operator>>(std::istream &is, line &l) {
return std::getline(is, l.data);
}
operator std::string() const { return data; }
};
int main() {
// open the input files:
std::ifstream town_input("orase.txt");
std::ifstream region_input("orase-index.txt");
// create istream_iterator's to read from the input files. Note
// that these iterate over `line`s, (i.e., objects of the type
// above, so they use its `operator>>` to read each data item).
//
std::istream_iterator<line> regions(region_input),
towns(town_input),
end;
// read in the lists of towns and regions:
std::vector<std::string> town_list (towns, end);
std::vector<std::string> region_list (regions, end);
// consolidate towns per region:
std::map<std::string, std::vector<std::string> > consolidated;
for (int i = 0; i < town_list.size(); i++)
consolidated[region_list[i]].push_back(town_list[i]);
// write out towns by region
std::ofstream output("output.txt");
for (auto pos = consolidated.begin(); pos != consolidated.end(); ++pos) {
std::cout << pos->first << ": ";
std::copy(pos->second.begin(), pos->second.end(),
std::ostream_iterator<std::string>(output, "\t"));
std::cout << "\n";
}
}
Notice that ceva is never initialized.
Instead of using strcpy to initialize strings, I would recommend using static initialization:
char ceva[100]="";
Related
I'm looking to read from std::in with a syntax as below (it is always int, int, int, char[]/str). What would be the fastest way to parse the data into an int array[3] and either a string or char array.
#NumberOfLines(i.e.10000000)
1,2,2,'abc'
2,2,2,'abcd'
1,2,3,'ab'
...1M+ to 10M+ more lines, always in the form of (int,int,int,str)
At the moment, I'm doing something along the lines of.
//unsync stdio
std::ios_base::sync_with_stdio (false);
std::cin.tie(NULL);
//read from cin
for(i in amount of lines in stdin){
getline(cin,str);
if(i<3){
int commaindex = str.find(',');
string substring = str.substr(0,commaindex);
array[i]=atoi(substring.c_str());
str.erase(0,commaindex+1)
}else{
label = str;
}
//assign array and label to other stuff and do other stuff, repeat
}
I'm quite new to C++ and recently learned profiling with Visual Studio however not the best at interpreting it. IO takes up 68.2% and kernel takes 15.8% of CPU usage. getline() covers 35.66% of the elapsed inclusive time.
Is there any way I can do something similar to reading large chunks at once to avoid calling getline() as much? I've been told fgets() is much faster, however, I'm unsure of how to use it when I cannot predict the number of characters to specify.
I've attempted to use scanf as follows, however it was slower than getline method. Also have used `stringstreams, but that was incredibly slow.
scanf("%i,%i,%i,%s",&array[0],&array[1],&array[2],str);
Also if it matters, it is run on a server with low memory available. I think reading the entire input to buffer would not be viable?
Thanks!
Update: Using #ted-lyngmo approach, gathered the results below.
time wc datafile
real 4m53.506s
user 4m14.219s
sys 0m36.781s
time ./a.out < datafile
real 2m50.657s
user 1m55.469s
sys 0m54.422s
time ./a.out datafile
real 2m40.367s
user 1m53.523s
sys 0m53.234s
You could use std::from_chars (and reserve() the approximate amount of lines you have in the file, if you store the values in a vector for example). I also suggest adding support for reading directly from the file. Reading from a file opened by the program is (at least for me) faster than reading from std::cin (even with sync_with_stdio(false)).
Example:
#include <algorithm> // std::for_each
#include <cctype> // std::isspace
#include <charconv> // std::from_chars
#include <cstdio> // std::perror
#include <fstream>
#include <iostream>
#include <iterator> // std::istream_iterator
#include <limits> // std::numeric_limits
struct foo {
int a[3];
std::string s;
};
std::istream& operator>>(std::istream& is, foo& f) {
if(std::getline(is, f.s)) {
std::from_chars_result fcr{f.s.data(), {}};
const char* end = f.s.data() + f.s.size();
// extract the numbers
for(unsigned i = 0; i < 3 && fcr.ptr < end; ++i) {
fcr = std::from_chars(fcr.ptr, end, f.a[i]);
if(fcr.ec != std::errc{}) {
is.setstate(std::ios::failbit);
return is;
}
// find next non-whitespace
do ++fcr.ptr;
while(fcr.ptr < end &&
std::isspace(static_cast<unsigned char>(*fcr.ptr)));
}
// extract the string
if(++fcr.ptr < end)
f.s = std::string(fcr.ptr, end - 1);
else
is.setstate(std::ios::failbit);
}
return is;
}
std::ostream& operator<<(std::ostream& os, const foo& f) {
for(int i = 0; i < 3; ++i) {
os << f.a[i] << ',';
}
return os << '\'' << f.s << "'\n";
}
int main(int argc, char* argv[]) {
std::ifstream ifs;
if(argc >= 2) {
ifs.open(argv[1]); // if a filename is given as argument
if(!ifs) {
std::perror(argv[1]);
return 1;
}
} else {
std::ios_base::sync_with_stdio(false);
std::cin.tie(nullptr);
}
std::istream& is = argc >= 2 ? ifs : std::cin;
// ignore the first line - it's of no use in this demo
is.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
// read all `foo`s from the stream
std::uintmax_t co = 0;
std::for_each(std::istream_iterator<foo>(is), std::istream_iterator<foo>(),
[&co](const foo& f) {
// Process each foo here
// Just counting them for demo purposes:
++co;
});
std::cout << co << '\n';
}
My test runs on a file with 1'000'000'000 lines with content looking like below:
2,2,2,'abcd'
2, 2,2,'abcd'
2, 2, 2,'abcd'
2, 2, 2, 'abcd'
Unix time wc datafile
1000000000 2500000000 14500000000 datafile
real 1m53.440s
user 1m48.001s
sys 0m3.215s
time ./my_from_chars_prog datafile
1000000000
real 1m43.471s
user 1m28.247s
sys 0m5.622s
From this comparison I think one can see that my_from_chars_prog is able to successfully parse all entries pretty fast. It was consistently faster at doing so than wc - a standard unix tool whos only purpose is to count lines, words and characters.
I am writing a dictionary program, the input is specified by a file and parsed as such:
std::string savedDictionary(std::istreambuf_iterator<char>(std::ifstream(DICTIONARY_SAVE_FILE)), {});
// entire file loaded into savedDictionary
for (size_t end = 0; ;)
{
size_t term = savedDictionary.find("|TERM|", end);
size_t definition = savedDictionary.find("|DEFINITION|", term);
if ((end = savedDictionary.find("|END|", definition)) == std::string::npos) break;
// store term and definition here...
}
This throws std::bad_alloc on some of my third world users' machines that don't have enough RAM to store the dictionary string + the dictionary as it's held inside my program.
If I could do this:
std::string term;
for (std::ifstream file(DICTIONARY_SAVE_FILE); file; std::getline(file, term, "|END|")
{
// same as above
}
then it would be great, but std::getline doesn't support a string as delimiter.
So, what's the most idiomatic way to read the file until I find "|END|" without allocating a crap ton of memory up front?
We can achieve the requested functionality by using a very simple proxy class. With that it is easy to use all std::algorithms and all std::iterators as usual.
So, we define a smal proxy class called LineUntilEnd. This can be used in conjunction with all streams like a std::ifstream or whatever you like. You can especially simply use the extractor operator to extract a value from the input stream and put it into the desired variable.
// Here we will store the lines until |END|
LineUntilEnd lue;
// Simply read the line until |END|
while (testInput >> lue) {
It works as expected.
An if we have such a string, we can parse it afterwords with easy regex operations.
I added a small example and put the resulting value in a std::multimap to build a demo dictionary.
Please see the following code
#include <iostream>
#include <string>
#include <iterator>
#include <regex>
#include <map>
#include <sstream>
#include <iterator>
// Ultra simple proxy class to read data until given word is found
struct LineUntilEnd
{
// Overload the extractor operator
friend std::istream& operator >>(std::istream& is, LineUntilEnd& lue);
// Intermediate storage for result
std::string data{};
};
// Read stream until "|END|" symbol has been found
std::istream& operator >>(std::istream& is, LineUntilEnd& lue)
{
// Clear destination string
lue.data.clear();
// We will count, how many bytes of the search string have been matched
size_t matchCounter{ 0U };
// Read characters from stream
char c{'\0'};
while (is.get(c))
{
// Add character to resulting string
lue.data += c;
// CHeck for a match. All characters must be matched
if (c == "|END|"[matchCounter]) {
// Check next matching character
++matchCounter;
// If there is a match for all characters in the searchstring
if (matchCounter >= (sizeof "|END|" -1)) {
// The stop reading
break;
}
}
else {
// Not all charcters could be matched. Start from the begining
matchCounter = 0U;
}
}
return is;
}
// Input Test Data
std::istringstream testInput{ "|TERM|bonjour|TERM|hola|TERM|hi|DEFINITION|hello|END||TERM|Adios|TERM|Ciao|DEFINITION|bye|END|" };
// Regex defintions. Used to build up a dictionary
std::regex reTerm(R"(\|TERM\|(\w+))");
std::regex reDefinition(R"(\|DEFINITION\|(\w+)\|END\|)");
// Test code
int main()
{
// We will store the found values in a dictionay
std::multimap<std::string, std::string> dictionary{};
// Here we will store the lines until |END|
LineUntilEnd lue;
// Simply read the line until |END|
while (testInput >> lue) {
// Search for the defintion string
std::smatch sm{};
if (std::regex_search(lue.data, sm, reDefinition)) {
// Definition string found
// Iterate over all terms
std::sregex_token_iterator tokenIter(lue.data.begin(), lue.data.end(), reTerm, 1);
while (tokenIter != std::sregex_token_iterator()) {
// STore values in dictionary
dictionary.insert({ sm[1],*tokenIter++ });
}
}
}
// And show some result to the user
for (const auto& d : dictionary) {
std::cout << d.first << " --> " << d.second << "\n";
}
return 0;
}
For those in the future, this is what I ended up writing:
std::optional<std::string> ReadEntry(std::istream& stream)
{
for (struct { char ch; int matched; std::string entry; } i{}; stream.get(i.ch); i.entry.push_back(i.ch))
if (i.ch == "|END|"[i.matched++]);
else if (i.matched == sizeof("|END|")) return i.entry;
else i.matched = 0;
return {};
}
I've been trying to read in characters from an external file to be put into a two-dimensional vector with type char. The elements must be able to be compared to certain values in order to navigate a maze given in "MazeSample.txt".
While I haven't been able to get characters be put into the vector, I was able to read and output the characters with the get and cout functions.
The following code is an attempt to read the vectors in the correct format, but provides an error in the end:
//MazeSample.txt
SWWOW
OOOOW
WWWOW
WEOOW
//source.cpp
vector<vector<char>> maze;
ifstream mazeFile;
char token;
mazeFile.open("MazeSample.txt");
while (!mazeFile.eof()) {
mazeFile.get(token); //reads a single character, goes to next char after loop
for (int row = 0; row < maze.size(); row++) {
for (int column = 0; column < maze.at(row).size(); row++) {
maze.push_back(token);
}
}
//cout << token;
}
mazeFile.close();
For the maze provided in "MazeSample.txt", I'd expect the maze vector to read each character row by row, mimicking the format of the maze sample.
In the above code, am provided with an error at maze.push_back(token):
"no instance of overloaded function "std::vector<_Ty, _Alloc>::push_back..." matches the argument list"
"argument types are: (char)"
"object type is: std::vector>, std::allocator>>>"
You are inserting char to vector<vector<char>>. You should create a vector<char>, insert the values of type char into that and then insert the vector<char> to vector<vector<char>> maze;. Here is the corrected version of your program. It can be written in simple ways but for your understanding, I have made made corrections on top of your program.
vector<vector<char>> maze;
ifstream mazeFile;
string token;
mazeFile.open("MazeSample.txt");
while (!mazeFile.eof()) {
std::getline(mazeFile, token); //reads an entire line
//Copy characters in entire row to vector of char
vector<char> vecRow;
vecRow.assign(token.begin(), token.end());
//Push entire row of characters in a vector
maze.push_back(vecRow);
}
mazeFile.close();
The reason for your problem is that you try to put a char into a std::vector of std vector. So you put a wrong type.
maze.at(row).push_back(token); would do it, but then no row exists. You also need to push_back and empty row, before you can write data to it.
That is your syntax error.
Then, your code could be drastically shortened by using C++ algorithms. See:
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
#include <sstream>
std::istringstream testDataFile(
R"#(00000
11111
22222
33333
44444
)#");
// This is a proxy to read a complete line with the extractor operator
struct CompleteLineAsVectorOfChar {
// Overloaded Extractor Operator
friend std::istream& operator>>(std::istream& is, CompleteLineAsVectorOfChar& cl) {
std::string s{}; cl.completeLine.clear(); std::getline(is, s);
std::copy(s.begin(), s.end(), std::back_inserter(cl.completeLine));
return is; }
operator std::vector<char>() const { return completeLine; } // Type cast operator for expected value
std::vector<char> completeLine{};
};
int main()
{
// Read complete source file into maze, by simply defining the variable and using the range constructor
std::vector<std::vector<char>> maze { std::istream_iterator<CompleteLineAsVectorOfChar>(testDataFile), std::istream_iterator<CompleteLineAsVectorOfChar>() };
// Debug output: Copy all data to std::cout
std::for_each(maze.begin(), maze.end(), [](const std::vector<char> & l) {std::copy(l.begin(), l.end(), std::ostream_iterator<char>(std::cout, " ")); std::cout << '\n'; });
return 0;
}
But this is not the end. A std::vector<char>has no advantage over a string. You can work nearly have all the same functionality as a std::vector<char>. That is an improvement in design. The code would then look more like this:
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
#include <sstream>
std::istringstream testDataFile(
R"#(00000
11111
22222
33333
44444
)#");
int main()
{
// Read complete source file into maze, by simply defining the variable and using the range constructor
std::vector<std::string> maze{ std::istream_iterator<std::string>(testDataFile), std::istream_iterator<std::string>() };
// Debug output: Copy all data to std::cout
std::copy(maze.begin(), maze.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
return 0;
}
This is the by far more simpler solution. And it will serve your needs as well.
Please note: I used a istringstream for reading data, becuase I do not have a file on SO. But it is of cause the same as using any other stream (like an ifstream).
EDIT
The first solution read the source and put it directly into a std::vector<std::vector<char>>:
The 2nd solution put everything in the a std::vector<std::vector<std::string>> which is the most efficient solution. Also a std::string is nearly a std::vector<std::vector<char>>.
The OP requested a 3rd solution where we use the 2nd solution and then copy the std::vector<std::vector<std::string>> into a std::vector<std::vector<char>>.
Please see below
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
#include <sstream>
std::istringstream testDataFile(
R"#(00000
11111
22222
33333
44444
)#");
int main()
{
// Read complete source file into maze, by simply defining the variable and using the range constructor
std::vector<std::string> maze{ std::istream_iterator<std::string>(testDataFile), std::istream_iterator<std::string>() };
// Debug output: Copy all data to std::cout
std::copy(maze.begin(), maze.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
// Edit: Copy into a std::vector<std::vector<char> -------------------------------------------------------
std::cout << "\n\n\nSolution 3:\n\n";
// Define the new variable with number of lines from the first maze
std::vector<std::vector<char>> mazeChar(maze.size());
// Copy the data from the original maze
std::transform(
maze.begin(), // Source
maze.end(),
mazeChar.begin(), // Destination
[](const std::string & s) {
std::vector<char>vc; // Copy columns
std::copy(s.begin(), s.end(), std::back_inserter(vc));
return vc;
}
);
// Debug Output
std::for_each(
mazeChar.begin(),
mazeChar.end(),
[](const std::vector<char> & vc) {
std::copy(vc.begin(), vc.end(), std::ostream_iterator<char>(std::cout));
std::cout << '\n';
}
);
return 0;
}
Hope this helps . . .
I posted one question, which was related to faster reading of a file, by skipping specific lines but that does not seem to go well with standard c++ api's.
I researched more and got to know what memory mapped files could come handy for these kinds of cases. Details about memory mapped files are here.
All in all,
Suppose, the file(file.txt) is like this:
A quick brown fox
// Blah blah
// Blah blah
jumps over the little lazy dog
And then in code, opened file, Read that as memory mapped file and then iterate over the contents of the char* pointer, skipping the file pointers itself. Wanted to give it a try before reaching to an conclusion on it. Skeleton of my code looks like this:
struct stat filestat;
FILE *file = fopen("file.txt", "r");
if (-1 == fstat(fileno(file), &filestat)) {
std::cout << "FAILED with fstat" << std::endl;
return FALSE;
} else {
char* data = (char*)mmap(0, filestat.st_size, PROT_READ, MAP_PRIVATE, fileno(file), 0);
if (data == 0) {
std::cout << "FAILED " << std::endl;
return FALSE;
}
// Filter out 'data'
// for (unsigned int i = 0; i < filestat.st_size; ++i) {
// Do something here..
// }
munmap(data, filestat.st_size);
return TRUE;
}
In this case, I would want to capture lines which does not start with //. Since this file(file.txt) is already memory mapped, I could go over the data pointer and filter out the lines. Am I correct in doing so?
If so, what is the efficient way to parse the lines?
Reading selected lines from wherever and copy them to whatever can be done with the C++ algorithms.
You can use std::copy_if. This will copy data from any source to any destination, if the predicate is true.
I show you a simple example that copies data from a file and skips all lines starting with "//". The result will be put in a vector.
This is one statement with calling one function. So, a classical one liner.
For debugging purposes, I print the result to the console.
#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
#include <string>
#include <fstream>
using LineBasedTextFile = std::vector<std::string>;
class CompleteLine { // Proxy for the input Iterator
public:
// Overload extractor. Read a complete line
friend std::istream& operator>>(std::istream& is, CompleteLine& cl) { std::getline(is, cl.completeLine); return is; }
// Cast the type 'CompleteLine' to std::string
operator std::string() const { return completeLine; }
protected:
// Temporary to hold the read string
std::string completeLine{};
};
int main()
{
// Open the input file
std::ifstream inputFile("r:\\input.txt");
if (inputFile)
{
// This vector will hold all lines of the file
LineBasedTextFile lineBasedTextFile{};
// Read the file and copy all lines that fullfill the required condition, into the vector of lines
std::copy_if(std::istream_iterator<CompleteLine>(inputFile), std::istream_iterator<CompleteLine>(), std::back_inserter(lineBasedTextFile), [](const std::string & s) {return s.find("//") != 0; });
// Print vector of lines
std::copy(lineBasedTextFile.begin(), lineBasedTextFile.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
}
return 0;
}
I hope this helps
The Input file:
1 4 red
2 0 blue
3 1 white
4 2 green
5 2 black
what I want to do is take every row and store it into 2D array.
for example:
array[0][0] = 1
array[0][1] = 4
array[0][2] = red
array[1][0] = 2
array[1][1] = 0
array[1][2] = blue
etc..
code Iam working on it:
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <vector>
using namespace std;
int convert_str_to_int(const string& str) {
int val;
stringstream ss;
ss << str;
ss >> val;
return val;
}
string getid(string str){
istringstream iss(str);
string pid;
iss >> pid;
return pid;
}
string getnumberofcolors(string str){
istringstream iss(str);
string pid,c;
iss >> pid>>c;
return c;
}
int main() {
string lineinfile ;
vector<string> lines;
ifstream infile("myinputfile.txt");
if ( infile ) {
while ( getline( infile , lineinfile ) ) {
lines.push_back(lineinfile);
}
}
//first line - number of items
int numofitems = convert_str_to_int(lines[0]);
//lopps items info
string ar[numofitems ][3];
int i = 1;
while(i<=numofitems ){
ar[i][0] = getid(lines[i]);
i++;
}
while(i<=numofitems ){
ar[i][1] = getarrivel(lines[i]);
i++;
}
infile.close( ) ;
return 0 ;
}
when I add the second while loop my program stopped working for some reason!
is there any other way to to this or a solution to my program to fix it.
It's better to show you how to do it much better:
#include <fstream>
#include <string>
#include <vector>
using namespace std;
int main() {
ifstream infile("myinputfile.txt"); // Streams skip spaces and line breaks
//first line - number of items
size_t numofitems;
infile >> numofitems;
//lopps items info
vector<pair<int, pair<int, string>> ar(numofitems); // Or use std::tuple
for(size_t i = 0; i < numofitems; ++i){
infile >> ar[i].first >> ar[i].second.first >> ar[i].second.second;
}
// infile.close( ) ; // Not needed -- closed automatically
return 0 ;
}
You are probably solving some kind of simple algorithmic task. Take a look at std::pair and std::tuple, which are useful not only as container for two elements, but because of their natural comparison operators.
The answer given is indeed a much better solution than your's. I figured i should point out some of your design flaws and give some tips too improve it.
You redefined a function that already exists in the standard, which is
std::stoi() to convert a string to an integer. Remember, if a function
exists already, it's OK to reuse it, don't think you have to reinvent what's
already been invented. If you're not sure search your favorite c++ reference guide.
The solution stores the data "as is" while you store it as a full string. This doesn't really make sense. You know what the data is beforehand, use that to your advantage. Plus, when you store a line of data like that it must be parsed, converted, and then constructed before it can be used in any way, whereas in the solution the data is constructed once and only once.
Because the format of the data is known beforehand an even better way to load the information is by defining a structure, along with input/output operators. This would look something like this:
struct MyData
{
int num1;
int num2;
std::string color;
friend std::ostream& operator << (std::ostream& os, const MyData& d);
friend std::istream& operator >> (std::istream& os, const MyData& d);
};
Then you could simply do something like this:
...
MyData tmp;
outfile << tmp;
vData.push_back(tmp);
...
Their is no question of intent, we are obviously reading a data type from a stream and storing it in a container. If anything, it's clearer as to what you are doing than either your original solution or the provided one.