I'm trying to declare array of objects of length specified in the file. Then i want to fill this array array with data from the same file.
Let's assume my file looks like that:
3 - length of the array
1 2
3 4
5 6
Here's my code
while ( getline (myfile,line) ){
istringstream ss(line);
if(i==0){
ss >> numOfObjects;
Object * array[numOfObjects];
}
if((i>=i) && (i<=1+numOfObjects)){
Object * o=new Object();
ss >> x >> y;
location l;
l.x=x;
l.y=y;
o->setLocation(l);
array[i-2]=o;
}
i++;
}
When i try to run my code it's says that array was not declared in this scope. Is there anything I can do? I know it will work if I declare array outside of while loop but I need it has to be of exact length as specified in file.
Sorry if it is a silly question i'm just a beginner
You could declare it outside and dynamically allocate memory using new after.
Object * array = nullptr;
if (...)
{
....
array = new Object[numOfObjects];
....
}
delete [] array;
As pointed out, you should consider using a vector, which automatically manage their memory
Read the number of things outside the loop, not as a special case inside it.
if (getline(myfile,line))
{
istringstream number(line);
number >> numOfObjects;
Object * array[numOfObjects];
while (i < numOfObjects && getline (myfile,line))
{
// ...
}
}
Related
I have a .txt file that stores student names along with two of their best marks. If a student for some reason, i.e. dropping out of course, fails to pass a course, then no marks are recorded.
My file looks like this
Samuel= 90.5, 95.9
Bill= 25.2, 45.3
Tim
Anthony= 99.9, 12.5
Mark
Rob
Basically, Tim, Mark and Rob failed the course and hence their marks are not stored. Also to differentiate between a failed mark and a pass mark, I have used the = symbol. Basically, I want to store all the names into memory alongside their associated values.
This is my implementation, however it is flawed in the sense that I have declared a double *marks[2] array to store all six marks, when clearly it will only store 3. I am having trouble storing the values into the double array.
This is my code...
istream& operator>> (istream& is, Students& student)
{
student.names = new char*[6];
for (int i=0; i<10; i++)
{
student.names[i] = new char[256];
student.marks[i] = new double[2];
is.getline(student.names[i], sizeof(student.names));
for (int j=0; j < 256; j++)
{
if((student.names[i][j] == '='))
{
int newPos = j + 1;
for (int k = newPos; k < 256; k++)
{
student.names[i][k - newPos] = student.names[k];
}
}
}
}
}
How can I go about storing the values of the students with the valid marks? Please, no use of vectors or stringstreams, just pure C/C++ char arrays
You have a few options, you could use a struct like so
struct Record {
std::string name;
double marks[2];
};
And then stick that into something like std::vector<Record> or an array of them like
Records *r = new Records[1000];
You could also keep three different arrays (either automatically allocated or dynamically allocated or even std::vector), one to hold the name, two to hold the marks.
In each case you would just indicate a fail by some thing like the marks being zero.
Also, you can use
std::string name;
double first, second;
std::cin >> name;
if (name[name.size() - 1] == '=')
std::cin >> first >> second;
And this will parse the input like you want it to for a single line. Once you've done that you can wrap the whole thing in a loop while sticking the values you get into some sort of data structure that I already described.
Hope that gives you a few ideas on where to go!
Here's a strategy:
First of all you need to implement a struct to hold the key-value pair, I suggest the following:
struct Student {
char name[30];
double marks[2];
};
Note that you can give the dimension of the char array inside the struct if you know that the length will never be higher. (which is given here)
Now what you need is to know how many lines are in your ifstream, you could make a loop of is.getline() calls to get there. (don't forget to call is.clear() and is.seekg(0) when finished, to be at the beginning for the real loop)
When you know how many lines are in your ifstream you can use dynamically cast the Array of your struct with the actual length of your file:
Student * students = new Student[lineCount]; // line count of is
As you can see, there's no need to have a std::vector to hold the values. Consider that the getline() loop may be an overkill just to get the line count, alternatively you could give a length to Students at compile-time by making an array with a length that will never be overpassed.
(e.g. Student students[128];)
Now you need to parse the lines, i'd suggest you make a loop like the following (line by line):
// int parseLine ( char* line, char* name, double* marks ) { ...
bool hasMarks=false;
int iLine=0; // Line pos iterator
int iName=0; // Name pos iterator
char mk1Str[4]; // String buffer, Mark 1
char mk2Str[4]; // String buffer, Mark 2
while(line[iLine]!='\0')
{
if(line[iLine]=='=')
{
hasMarks=true;
name[iLine]='\0';
for(int iMark=0;iMark<4;iMark++)
{
mk1Str[iMark]=line[iLine+iMark+2];
mk2Str[iMark]=line[iLine+iMark+8];
// ^^ You can harcode the offsets (2,8) since they don't change
}
break;
}
name[iName++]=line[iLine];
iLine++;
}
Now what you need is to parse the marks to double values, for this you could use the atof() function that works with char*. The bool hasMarks helps you know if a student has defined marks, if not, you could define dummy values like -1 for the mark fields of your struct.
I think this works quite well for your case...
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.
**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.
I'm relatively new to C++ memory management, and I'm getting this weird error of heap corruption (plus an automatic breakpoint in Visual Studio before it). Here is the offending code:
z_world::z_world(char* name)
{
unsigned int i, skip;
char tmp;
//Load data from file
std::string* data = loadString(name);
//Base case if there is no world data
tiles = NULL;
w = 0;
h = 0;
if(data->length() > 0) {
//Set up the 'tiles' array
for(i = 0; i < data->length(); i++) {
if(data->at(i) == '\n')
h++;
if(h == 0)
w++;
}
tiles = new int[data->length()-h];
//Load Data
skip = 0;
for(i = 0; i < data->length(); i++) {
if(data->at(i) == '\n') {
skip++;
printf("\n");
continue;
}
tmp = data->at(i);
tiles[i+skip] = atoi(&tmp);
printf("%i ",tiles[i+skip]);
}
}
delete data;
}
Here's where I load in the string:
std::string* loadString(char* name)
{
ifstream in(name);
std::string* input = new string();
while(in) {
std::string line;
getline(in,line);
input->append(line);
input->append("\n");
}
in.close();
return input;
}
I get the breakpoint and error inside of "delete data;", which makes me think that "data" gets deleted somewhere before that, but I can't find where it would. For reference, this method is to create an object that contains world data for a game in the form of a virtual 2D integer array (for the ID's of the tiles).
Youre problem is probably here:
tiles[i+skip] = atoi(&tmp);
Problem 1:
It should be -skip
tiles[i - skip] =
Problem 2:
The atoi() command is being used incorrectly (tmp does not contain a string). But also I don't think atoi() is the appropriate method. I think what you are looking for is simple assignment. The conversion from char to int is automatic:
tiles[i - skip] = tmp;
Problem 3:
You are not using objects correctly. In this situation there is no need to generate dynamic objects and create a mess with dynamic memory management. It would be simpler to just to create automatic objects and pass those back normally:
std::string* loadString(char* name)
// ^ Don't do this.
std::string loadString(std::string const& name)
// ^^^^^^^ return a string by value.
// The compiler will handle memory management very well.
In general you should not be passing pointers around. In the few situations where you do need pointers they should be held within a smart pointer object or containers (for multiple objects) so that their lifespan is correctly controlled.
atoi(&tmp);
atoi expects a pointer to a null terminated string - not a pointer to a char
There's no need to dynamically allocate the string in the code you've shown. Change the loadString function to
std::string loadString(char* name)
{
ifstream in(name);
std::string input;
// ...
return input;
}
In the caller
std::string data = loadString( name );
Now there's no need to delete the string after you're done.
Instead of
int *tiles = NULL;
tiles = new int[data->length()-h];
use
std::vector<int> tiles;
tiles.resize(data.length() - h);
Also, if you do need to dynamically allocate objects you should be using smart pointers (std::unique_ptr and std::shared_ptr) instead of raw pointers.
There is a bug in
tiles[i+skip] = atoi(&tmp);
For example, for a string
Hello\n
World\n
and for the loop iteration at the point of i == 10, skip is already 1 (since we have encountered the first \n before) and you are writing to tiles[10 + 1], but tiles only has been allocated as an array with 10 elements.
May be the variable input is local to this function. So after returning from this the memory is freed. So, calling later delete on this string tries to free already freed memory.
I need to declare an array of structures on the heap, then transfer data from parallel arrays on the stack and from calculations into each structure. I declared
struct Grades
{
string studentName;
int scores[4];
double average;
};
....
Grades *art1301 = new Grades;
....
(art1301 + i)->studentName = names[i];
for((int i = 0 ; i < 5 ; i++ )
(art1301 + i)->scores[j] = exams[i][j];
(art1301 + i)->average = average;
My program accesses the first record, but it crashes after it accesses the first field of the second record. I don't understand why it works for the first record, but dies in the middle of the second? Am I accessing the structure correctly?
Thank you.
To allocate an array, you need the array form of new, with the square brackets:
Grades *art1301 = new Grades[200];
// ^^^^^
The array size can be a dynamically determined quantity.
You aren't allocating memory for an array, you are allocating only for one element.
As someone said in the comments, the key is in the new Grades instruction
In addition, unless you have another i variable declared before (which is a bad practice), that code doesn't compile because (art1301 + i)->studentName = names[i]; will not find variable i