C++ container splitting - c++

I have an unknown number of int variables in a text file, all i know is that the number of variables will be a multiple of 6.
I want to read these files in to a container, and split them down into smaller containers, where each container takes 6 values.
For example, if there is 30 variables in the text file, I want 5 containers, each containing 6 of the variables.And they need to be in the order that they are in the file, i.e., the first container holds the first six values.
I have read the files into a list, and vector for the moment, and was wandering which one is more suited. I've read about the split function, but after a look around, I haven't been able to successfully apply it to one of these attempts, mainly because I don't know how many smaller containers I am going to need
This is my code so far:
Vector Method
std::vector<int> Ticket;
std::ifstream fin (username + "Ticket.txt");
while (!fin.eof())
{
fin >> num;
Ticket.push_back(num);
}
fin.close();
Ticket.shrink_to_fit();
List method
std::list<int> Ticket1;
std::ifstream fin (username + "Ticket.txt");
while (!fin.eof())
{
fin >> num;
Ticket1.push_back(num);
}
fin.close();
Alternatively, if I could somehow read 6 values from the file straight into smaller containers, and keep doing this till the end of the file, hence skipping the big container that would be awesome.

You can use a vector of vectors.
std::vector< std::vector<int> > Ticket;
std::vector<int> newVector;
std::ifstream fin (username + "Ticket.txt");
while ( fin >> num )
{
newVector.push_back( num );
// if the vector is full, then insert it and start afresh
if ( newVector.size() == 6 ) {
Ticket.push_back( newVector );
newVector.clear();
}
}
fin.close();
At the end, the Ticket vector shall contain all the required vectors.
To print each vector to console, use two nested loops:
for ( int i = 0; i < Ticket.size(); i++ ) { // outer vector
for ( int j = 0; j < Ticket[i].size(); j++ ) { // inner vectors
std::cout << Ticket[i][j] << " ";
}
std::cout << "\n";
}

If you create a temporary accumulator, and your ultimate destination:
vector<int> temp ;
vector<vector<int>> package ;
Then just fill up temp, until its full, and push onto the end of package:
while ( fin >> num )
{
temp.push_back( num) ;
if ( temp.size() == MyDesiredSize ) { package.push_back( temp) ; temp.clear() ; }
}
When you push_back on package it will make a copy, that's why you can clear temp afterwards.

Do you need the values within the container to be contiguous as they are within a c-array, or do you need to be able to have more control over the memory allocation using the reserve member function of std::vector? If not, then consider deque or list over vector.
http://www.cplusplus.com/reference/deque/deque/
You also use a fixed size array if you have a C++ 11 compiler that supports it.
http://www.cplusplus.com/reference/array/array/
For instance, you could setup a container like this to setup a list of arrays.
std::list<std::array<int, 6>> containersOfSix;
On the other hand, you might be able to use one of the inserter functions if you would prefer both parts of the container to be dynamic.
http://www.cplusplus.com/reference/iterator/istream_iterator/
http://www.cplusplus.com/reference/iterator/insert_iterator/
Consider reading through that material, and toying around with some of those options. That might lead you to search for other threads related to those, which will help you find more specific examples.

i would use the C function Fscanf()
You can use a format and read the 6 numbers at once.
fscanf(FILE,"%i %i %i %i %i %i",&var1,&var2,&var3,&var4,&var5,&var6)
Then you just need to handle the container, creating one before and reading directly to it or by coping the values if they are what you expect.

Related

2D Vector - Remove Rows by search

I'm quite new to vector and need some additional help with regards to vector manipulation.
I've currently created a global StringArray Vector that is populated by string values from a text file.
typedef std::vector<std::string> StringArray;
std::vector<StringArray> array1;
I've created a function called "Remove" which takes the input from the user and will eventually compare the input against the first value in the array to see whether it's a match. If it is, the entire row will then deleted and all elements beneath the deleted row will be "shuffled up" a position to fill the game.
The populated array looks like this:
Test1 Test2 Test3
Cat1 Cat2 Cat3
Dog1 Dog2 Dog3
And the remove function looks like this:
void remove()
{
string input;
cout << "Enter the search criteria";
cin >> input;
I know that I will need a loop to iterate through the array and compare each element with the input value and check whether it's a match.
I think this will look like:
for (int i = 0; i < array1.size(); i++)
{
for (int j = 0; j < array1[i].size(); j++)
{
if (array1[i] = input)
**//Remove row code goes here**
}
}
But that's as far as I understand. I'm not really sure A) if that loop is correct and B) how I would go about deleting the entire row (not just the element found). Would I need to copy across the array1 to a temp vector, missing out the specified row, and then copying back across to the array1?
I ultimately want the user to input "Cat1" for example, and then my array1 to end up being:
Test1 Test2 Test3
Dog1 Dog2 Dog3
All help is appreciated. Thank you.
So your loop is almost there. You're correct in using one index i to loop through the outer vector and then using another index j to loop through the inner vectors. You need to use j in order to get a string to compare to the input. Also, you need to use == inside your if statement for comparison.
for (int i = 0; i < array1.size(); i++)
{
for (int j = 0; j < array1[i].size(); j++)
{
if (array1[i][j] == input)
**//Remove row code goes here**
}
}
Then, removing a row is the same as removing any vector element, i.e. calling array1.erase(array1.begin() + i); (see How do I erase an element from std::vector<> by index?)
Use std::list<StringArray> array1;
Erasing an item from an std::vector is less efficient as it has to move all the proceeding data.
The list object will allow you to remove an item (a row) from the list without needing to move the remaining rows up. It is a linked list, so it won't allow random access using a [ ] operator.
You can use explicit loops, but you can also use already implemented loops available in the standard library.
void removeTarget(std::vector<StringArray>& data,
const std::string& target) {
data.erase(
std::remove_if(data.begin(), data.end(),
[&](const StringArray& x) {
return std::find(x.begin(), x.end(), target) != x.end();
}),
data.end());
}
std::find implements a loop to search for an element in a sequence (what you need to see if there is a match) and std::remove_if implements a loop to "filter out" elements that match a specific rule.
Before C++11 standard algorithms were basically unusable because there was no easy way to specify custom code parameters (e.g. comparison functions) and you had to code them separately in the exact form needed by the algorithm.
With C++11 lambdas however now algorithms are more usable and you're not forced to create (and give a reasonable name to) an extra global class just to implement a custom rule of matching.

std::Map of int and struct running out of memory (std::Bad_alloc) c++

I have the following in my program
class memModel
{
struct Addrlist
{
vector<string> data;
vector<int> timestamp;
vector<string> client;
}
map<int, Addrlist> AddrMap ; //store based address and list of all accesses
}
In main() I read from a few files and store millions of entries into this stuct
int main()
{
memModel newObj ;
ifstream file1("dataStream");
ifstream file2("timeStampSteam");
ifstream file3("clientStream");
ifstream file4("addrStream") ;
string dataSTR,clientSTR;
int time = 0 ;
int addr;
for(int i=0; i<10000000/*10mil*/ ; i++)
{
getline(file1,dataSTR);
getline(file3,clientSTR);
file2 >> time ;
file4 >> hex >> addr ;
newObj.AddrMap[addr].data.push_back(dataSTR) ;
newObj.AddrMap[addr].time.push_back( time) ;
newObj.AddrMap[addr].client.push_back(clientSTR) ;
}
}
So the problem is I am running out of memory and get the std::Bad_alloc exception. This code works with smaller data sizes.
I am trying to understand where the struct and Map are being stored. Is everything going on the Stack ?
The vectors are dynamically allocated right. Are those going to the heap ?
This is my first time working with large data sets so I would like to understand the concepts better. How can I change this to make sure I am using the heap and I do not run out of memory.
newObj.AddrMap[addr].data[i] = dataSTR ;
newObj.AddrMap[addr].time[i] = time ;
newObj.AddrMap[addr].client[i] = clientSTR ;
This stores three items of data into three vectors, here.
Unfortunately, all of these vectors are empty, and they contain no elements. This results in undefined behavior.
You either have to use push_back(), or resize() these vectors in advance, so they are of sufficient size to store the items you're placing into the vectors, here.
A std::vector's operator[] does not automatically create or resize the array. It merely accesses the existing element in the array.

how to add element into the nested vector

Hello sorry for the stupid question but I am very beginner in c++. I cannot describe the problem well because of my bad English. I'll add my code here that i have tried so far.
vector< vector<string> >allData;
int main(){
vector<string>test;
for(int i = 0; i<allData.size(); i++){
test = allData[i];
}
int id;
cout<<"enter Id"<<endl;
cin>>id;
if (id == test[2]){
string desc;
cout<<"enter ur description"<<endl;
cin>>description;
allData.push_back(description);
} else {
cout<<"there is no data with the id u have entered"<<endl;
}
}
Above code is just an example code. Lets say that there are 2 vectors inside the vector named allData, so when i enter the ID of a first vector i can add description into the vector which i have chosen by choosing it by its Id. Somehow i cannot do that thing like choosing the first or second vector by inputing their ids then add more datas into chosen 1. So please someone tell me what should i do. I know that above code is incomplete and awful but as i said before i am very beginner in c++
You can add values into nested vector just like a normal vector. So you could do allData[i].push_back(description) or even allData[i][j] = description to overwrite an existing value. However you are trying to push a string into allData which does not contain strings but rather Vectors of strings.
Additionally your first for loop runs through allData and saves each to test, overwriting test each time. When the loop is done test will simply contain the last element of allData. Thus your for loop is the equivalent of doing test = allData.back()
Well, if you need to access your child vectors directly, you need to declare your vector saying how many child vectors it has (without this, you have segmentation fault when trying to access it ie. allData[0] will be unitialised).
Let's say you have 2, so they will be allData[0] and allData[1], just like a normal array.
std::vector<std::vector<std::string>> allData (2);
allData[0].push_back("string0a");
allData[0].push_back("string0b");
allData[1].push_back("string1");
for(auto & a : allData[0])
std::cout << a << "\n";
Of course, you can add vectors dinamically too.
std::vector<std::vector<std::string>> allData;
std::vector<std::string> child0;
child0.push_back("string0");
allData.push_back(child0);
And to check if the id the user input is valid, you can do
if(id >= 0 && id < allData.size()) {
....
Your variable all_data is a vector of vectors representing rows and columns. You can create a scratch vector for a row, push_back() data on that scratch vector, then push_back() that scratch vector onto all_data like so:
#include <vector>
int main() {
std::vector<std::vector<int>> all_data;
for (int ii = 0; ii < 10; ii++) {
std::vector<int> row_data;
for (int jj = 0; jj < 10; jj++) {
row_data.push_back(ii * jj);
}
all_data.push_back(row_data);
}
return (0);
}

grabbing data sets from a file with an arbitrary amount of spaces

**No direct answers or code examples please, this is my homework which i need to learn from. I'm looking for help concerning the algorithm i need to develop.
I seem to be having a logic error in coming up with a solution for a portion of my class work, the program involves multiple files, but here is the only relevant portion:
I have a file PlayerStats that holds the stats for a basketball player in:
rebounds
points
assists
uniform #
my initial reaction would be to create a while loop and read these into a temporary struct that holds these values, then create a merge function that merges the values of the temp struct with the inital array of records, simple enough?
struct Baller
{
//other information on baller
int rebounds;
int assists;
int uniform;
int points;
void merge(Baller tmp); //merge the data with the array of records
}
//in my read function..
Baller tmp;
int j = 0;
inFile << tmp.uniform << tmp.assists << tmp.points << tmp.rebounds
while(inFile){
ArrayRecords[j].merge(tmp);
j++;
//read in from infile again
}
The catch:
The file can have an arbitrary number of spaces between the identifiers, and the information can be in any order(leaving out the uniform number, that is always first). e.g.
PlayerStats could be
11 p3 a12 r5 //uniform 11, 3 points 12 assists 5 rebounds
//other info
OR
11 p 3 r 5 a 12 //same exact values
What I've come up with
can't seem to think of an algorithm to grab these values from the file in the correct order, i was thinking of something along these lines:
inFile << tmp.uniform; //uniform is ALWAYS first
getline(inFile,str); //get the remaining line
int i = 0;
while(str[i] == " ") //keep going until i find something that isnt space
i++;
if(str[i] == 'p') //heres where i get stuck, how do i find that number now?
else if(str[i] == 'a')
eles if(str[i] = 'r'
If you're only going to check one letter, you could use a switch statement instead of if / else, that would make it easier to read.
You know where the number starts at that point, (hint: str[i+1]), so depending on what type your str[] is, you can either use atoi if its a char array, or std::stringstream if it's an std::string.
I'm tempted to give you some code, but you said not too. If you do want some, let me know and I'll edit the answer with some code.
Instead of using a 'merge' function, try using an std::vector so you can just push_back your structure instead of doing any 'merging'. Besides, your merge function is basically a copy assignment operator, which is created by the compiler by default (you don't need to create a 'merge' function), you just need to use = to copy the data across. If you wanted to do something special in your 'merge' function, then you should overload the copy assignment operator instead of a 'merge' function. Simples.
Do something like that:
int readNumber () {
while isdigit (nextchar) -> collect in numberstring or directly build number
return that number;
}
lineEater () {
Read line
skip over spaces
uniform=readNumber ();
haveNum=false;
haveWhat=false;
Loop until eol {
skip over spaces
if (isdigit)
number=readNumber ();
skip over spaces
haveNum=true;
else
char=nextChar;
haveWhat=true;
if (haveChar and haveNum) {
switch (char) {
case 'p' : points=number; break;
...
}
haveNum=false;
haveWhat=false;
}
}
or, if you are more ambitous, write a grammar for your input and use lex/yacc.

Making an array of pointers to structs or objects in C++

So I'm basically just trying to take in some file input, and then take that data and put it into several structs. The only issue I'm having is with the naming of the pointers to the structs. The struct's themselves are supposed to represent students and I wanted to set each pointer as one of their names rather than an arbitrary variable. I tried to do this in a way that I'm assuming is syntactically wrong for it didn't work. In the code below, I increment the for loop with the temp array because each 4th position is a new student. Any ideas on how I could go about this?
#include<iostream>
#include<iomanip>
#include"student.h"
#include"creditcard.h"
#include<fstream>
using namespace std;
int main ()
{
string creditcards[20];
int i;
int x;
int amount;
string temp[20];
ifstream infile;
string filename;
int count;
int numstudents;
string newstring="";
string pointers[20];
cout<<"enter the file name of which you've stored your"<<endl
<<"credit card infomation"<<endl;
getline(cin,filename,'\n');
infile.open(filename.c_str());
count=0;
getline(infile,temp[count],'\n');
while(! infile.eof())
{
count++;
getline(infile,temp[count],'\n');
numstudents= (count/4);
if(numstudents < 1 || count%4 != 0)
{
cout<<"incorrect data file"<<endl;
}
}
cout<<numstudents<<endl;
for(i=0,x=0; i<numstudents;i++,x+4)
{
student *temp[x];
temp[x] = new student;
pointers[i] = temp[x];
}
for(i=0;i<numstudents;i+4)
{
cout<<temp[i]<<endl;
}
return 0;
}
Ok, let's start from the top.
Your code was (before I reformatted it) a mess. Messy code is harder to read and more likely to have bugs.
You have 3 arrays, each containing 20 strings. Why do you need so many?
One of them is named temp; having to use that as a variable name is a good indicator that you're mishandling data somewhere.
You're declaring int count relatively early on, then initializing it to 0 later. While not necessarily a bad thing, that's not the best method (do both at once, when needed).
You can declare local variables more than one in a line, but you don't need to declare them all at the top of the function. That's not necessary in C++.
int main ()
{
string creditcards[20];
int i = 0, x = 0, amount = 0;
(legal, but might not be needed)
It's typically better to declare and initialize a variable at the same time, just before you need it:
int count = 0;
getline(infile, temp[count], '\n');
I remember seeing that reading until you hit eof isn't recommended, although I'm not entirely sure on that. You may want to change this:
while ( !infile.eof() )
{
Now, the first actual mistake I see here is that you read a line, increment count, then read another line before acting. Is that intentional, and if so, why is it necessary? Doing the getline and increment inside the loop would be more readable and potentially more reliable.
count++;
getline(infile, temp[count], '\n');
This line is a bug, I think:
for(i=0,x=0; i<numstudents;i++,x+4)
The last section does i++, x+4. It does not change x.
The next loop after that handles i in the same way this loop uses x, so you can probably combine those two.
Now, on top of all that, massive temp arrays are not the solution to this problem (or any other that I can think of).
To store this kind of data, you'll want to look into a std::map<std::string, student*> or std::vector<student*>. The vector will allow you to push the new student struct to the back when necessary, and the map will allow you to key them based on name and retrieve that later, something like so:
typdef map<string, student*> studentmap;
studentmap students;
studentmap::iterator iter = students.find("Bob");
if ( iter != students.end() )
{
student * bob = iter->second;
// Work with data
}
It's a much better way of handling this, and will take a lot of the guess work out of what you're doing now.
If you want to be able to reference the students by name, consider using a map<string, student> or map<string, student*>.
This will allow you to refer to individual students via students["Jack"] or students["Jill"].