reading a file into an array of structs

I'm trying to read from a file and create an array of structs to hold the information from the file. I cannot figure out how to do it. I'm not even sure where to start.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  void readFile(string filename, AccessRecord records[500])
{
   ifstream fin(filename);  //open file
   if (fin.fail())          //check for fail
      return 0;

   while (fin >> records[500])  //this is where I run into trouble
   {                            //I know this is all wrong, I just 
      int i = 0;                //don't know what I am supposed to do
      fin >> records[i]         //to get the info into the array,
      i++;                      //or how to break it into the different
   }                            //parts of the struct

   fin.close();             //close file

}
Do you realize that trying to use records[500] is accessing the array out of bounds?

Perhaps something more like:

1
2
3
4
5
6
7
8
9
10
11
12
13

void readFile(const string& filename, AccessRecord records[500])  // Note the changes to the string.
{
   ifstream fin(filename);  //open file

   int i = 0;
   // If the file fails to open correctly this loop will also fail and nothing will be added to the array.  
   // Also no need to manually close the file, let the destructor do it's job of closing the file.
   while (i < 500 && fin >> records[i])  // Make sure you don't access the array out of bounds.
   { 
      i++;
   }
}
A couple of problems:
Line 1: You're passing records by value, so readFile is operating on a copy of the array. Updates made to records by readFile will be lost when readFile exits.

Line 7: You're trying to read into the 501st element of the array, which is out of bounds.
Arrays start at 0, so the valid elements are 0 - 499.

Line 9: You're resetting i to zero every time through the loop. Consequently, line 10 will only ever read into records[0]. BTW, i needs to be outside the loop.

1
2
3
4
5
6
7
8
9
10
void readFile(string filename, AccessRecord * records)
{  int i = 0;
   ifstream fin(filename);  //open file
   i  f (fin.fail())          //check for fail
         return 0;
   while (fin >> records[i]) 
   {   i++;                  
   }                          
   fin.close();             //close file
}
Last edited on
okay, I fixed the string parameter, and added the i < 500, I also realized that I was resetting i to 0 on every loop, so I moved that outside the loop. It still isn't doing what it needs to do though. I think there is something fundamental that I'm missing.
doesn't the array automatically pass by reference?
A couple of problems:
Line 1: You're passing records by value, so readFile is operating on a copy of the array. Updates made to records by readFile will be lost when readFile exits.

Wrong. He's passing the array via a pointer which is how you always pass arrays to functions. All updates made in the function will be reflected in the calling function. By the way that pointer is what is passed by value (a copy) so any modifications made to where the pointer points will be lost when the function returns.

I think there is something fundamental that I'm missing.


Show your current code. Are you still trying to read the file twice?
This is what I currently have. I've always passed arrays this way in the past, and it has always worked. I don't really want to use a pointer if it can be avoided. They confuse me to no end.

1
2
3
4
5
6
7
8
9
10
11
12
void readFile(const string & filename, AccessRecord records[500])
{
   ifstream fin(filename);  //open file
   if (fin.fail())          //check for fail
      return;
   int i = 0;
   while (( i < 500) && (fin >> records[i]))
   {
      i++;
   }
   fin.close();             //close file
}

Last edited on
First learn to use code tags when posting code (use the <> icon to the right of the entry window after highlighting your code).

I don't really want to use a pointer if it can be avoided. They confuse me to no end.

Well arrays are passed by pointer, if you don't want to use pointers then you should be using std::vector instead.

It still isn't doing what it needs to do though.

What does it need to do, and what exactly is it doing?

Are you sure the file is actually opening?
Last edited on
currently when I try to compile it it is spitting out pages worth of errors, and I can't figure out why. I know the problem is somewhere in this function, because it has no problem when it's blocked out. Specifically it's complaining about the while line, but I can't figure out what exactly the problem is.
Last edited on
currently when I try to compile it it is spitting out pages worth of errors, and I can't figure out why.


If you're getting compile error then post those errors, all of them exactly as they appear in your development environment.

I know the problem is somewhere in this function,

How do you know? Are your error messages telling you that the problem lies within this function? Do you know that the error messages should be telling the exact line number where it detects the problem?

because it has no problem when it's blocked out.

This doesn't mean that the problem is definitely inside the function, although it is likely, it depends on what your error messages are actually saying.

Edit: By the way what exactly is an "AccessRecord"? It would be helpful if you posted a small complete program that illustrates the problems.




Last edited on
I tried to post the program, and the error codes, but go figure... it was too long.
basically this program is supposed to read the access records from a file, including a user name, files accessed by that person, and a timestamp of when they were accessed. I need to be able to display just the records that fall between a certain time period. I made a struct called AccessRecords to hold each line of the file, and I need an array to hold all of them. I just don't know how to get them from the file into the array of structs.

I won't post all of the errors, but here is the first bunch of them:

assign02.cpp: In function ‘void readFile(const string&, AccessRecord*)’:
assign02.cpp:90:26: error: no match for ‘operator>>’ (operand types are ‘std::ifstrea
while (i < 500 && fin >> records[i])
~~~~^~~~~~~~~~~~~
In file included from /usr/include/c++/8/iostream:40,
from assign02.cpp:16:
/usr/include/c++/8/istream:120:7: note: candidate: ‘std::basic_istream<_CharT, _Traitstd::basic_istream<_CharT, _Traits>::__istream_type& (*)(std::basic_istream<_CharT, _traits<char>; std::basic_istream<_CharT, _Traits>::__istream_type = std::basic_istrea
operator>>(__istream_type& (*__pf)(__istream_type&))
^~~~~~~~
/usr/include/c++/8/istream:120:7: note: no known conversion for argument 1 from ‘Acasic_istream<char>::__istream_type&)’ {aka ‘std::basic_istream<char>& (*)(std::basic_
/usr/include/c++/8/istream:124:7: note: candidate: ‘std::basic_istream<_CharT, _Traitstd::basic_istream<_CharT, _Traits>::__ios_type& (*)(std::basic_istream<_CharT, _Traihar>; std::basic_istream<_CharT, _Traits>::__istream_type = std::basic_istream<char>;ar>]’
operator>>(__ios_type& (*__pf)(__ios_type&))
^~~~~~~~
/usr/include/c++/8/istream:124:7: note: no known conversion for argument 1 from ‘Ac_istream<char>::__ios_type&)’ {aka ‘std::basic_ios<char>& (*)(std::basic_ios<char>&)’
/usr/include/c++/8/istream:131:7: note: candidate: ‘std::basic_istream<_CharT, _Traitstd::ios_base& (*)(std::ios_base&)) [with _CharT = char; _Traits = std::char_traits<cic_istream<char>]’
operator>>(ios_base& (*__pf)(ios_base&))

assign02.cpp: In function ‘void readFile(const string&, AccessRecord*)’:
assign02.cpp:90:26: error: no match for ‘operator>>’ (operand types are ‘std::ifstrea
while (i < 500 && fin >> records[i])


Okay so look at the first error, and answer the question I posed above about AccessRecord.

I tried to post the program, and the error codes, but go figure... it was too long.

That is why I said to post a small complete program that illustrates your problem.

You need to post the definition of the AccessRecord type so we can see what that type actually is.



1
2
3
4
5
6
struct AccessRecord
{
   string username;
   string filename;
   long timestamp;
};
Okay do you realize that unless you have overloaded the insertion operator>> the compiler doesn't know how to "read" that structure using the operator?

yeah, I know it doesn't know how... I don't know how either. That's what I'm asking for help with! haha! I know what I have wouldn't work, I just don't know where to go from here
Depending on how the data is stored on the file, the pseudocode for this goes something like this.


open the file
Loop:
    create a temp of type AccessRecord
    read a line from file
    parse line into attributes
    set temp to these attributes
    assign temp to next vacant item in array
Loop until there are no more lines/attributes on file
close the file


You should be able to write and test code sequentially through this to avoid all this back and forth hole digging. Without a psedocode plan you are chewing up a mountain of time.

Just tick off each item in the plan as it develops through testing each component sequentially. Don’t proceed until that bit works.
Silence reigns so this might help seeing the full picture you present to me. Pls not for arrays you pretty much can't get away from pointers. (All you have to do is add '*' to the front of the array name, and specify the array size.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#include <iostream>
#include <string>
#include <fstream>

struct AccessRecord
{
    std::string username{"Unknown"};
    std::string filename{"??"};
    long timestamp{0};
};

// OVERLOADED << OPERATOR (cf cout << )
std::ostream& operator<<(std::ostream& out, AccessRecord aRecord)
{
    out
    << aRecord.username  << ' '
    << aRecord.filename  << ' '
    << aRecord.timestamp << '\n';
    
    return out;
}

void display_records(AccessRecord* aArray, int aSize)
{
    for (int i = 0; i < aSize; i++)
        std::cout
        << aArray[i].username << ' '
        << aArray[i].filename << ' '
        << " Timestamp: " << aArray[i].timestamp << '\n';
    
    return;
}

void read_from_file(std::string file_name, AccessRecord* aArray, int aSize)
{
    std::ifstream myList;
    myList.open(file_name);
    
    if( myList.is_open() )
    {
        std::string uname, fname;
        long stamp{0};
        
        AccessRecord temp;

        int counter{0};
        while(  myList >> uname >> fname >> stamp)
        {
            temp.username = uname;
            temp.filename = fname;
            temp.timestamp = stamp;
            
            aArray[counter] = temp;
            counter++;
        }
    }
    else
    {
        std::cout << "File not opened ...\n";
        return;
    }
    myList.close();

    return;
}

const int LIMIT{500};

int main()
{
    // SETUP SOME TEST DATA WITH JUNK
    // GENERATE AN ARRAY OF 500 RECORDS AND SAVE TO FILE
    AccessRecord dummy[LIMIT];
    AccessRecord temp;
    
    std::ofstream myRecords;
    myRecords.open("access_records.txt");
    
    for(int i = 0; i < LIMIT; i++)
    {
        temp.username = "BLAH" + std::to_string(i);
        temp.filename = "blah" + std::to_string(7*i);
        temp.timestamp = i;
        
        myRecords << temp;
    }
    myRecords.close();
    
    // NOW USE THE TEST DATA
    // SETUP THE WORKING ARRAY BY READIN IN FROM TEST FILE
    AccessRecord records[LIMIT];
    
    // OFF TO A FUNCTION
    read_from_file("access_records.txt", records, LIMIT);
    display_records(records, LIMIT);
    
    return 0;
}
Topic archived. No new replies allowed.