Displaying data from file

So i have a dat. file that holds 500+ user data information. I want to be able to sort the data into arrays and display it if the user selects that option.

the data reads as:

room age acre money
1 0 123456 123456
2 5 12345 12345
1 3 123 1234
2 22 123 123
...

here is the code i have so far, it wont display properly. I would like to use eof to sort the data because it is a large file and doesn’t know how many lines it is.
for the room number it is only 1 or 2 as well.
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

#include <cstdlib>
#include <iostream>
#include <iomanip>
#include <fstream>
#include <string>

using namespace std;



int main() {

  string scoreDataFilename = "";


  int room [i];
  int age [i];
  int acre [i];
  int money [i]
  
  int i =0 ;

  ifstream dataIn;

  cout << "Enter filename to process: " << datafile.dat;
  getline(cin, datafile.dat);


  dataIn.open('datafile.dat');

  if (dataIn) {

 
while (!dataIn.eof())
  {
    dataIn >> room[i];
    dataIn >> age[i];
    dataIn >> acre[i];
    dataIn >> money[i];
    ++i;

  }

    dataIn.close();

cout <<"What would you like to do? \n"
       << "  " << AVG     << ". average\n"
       << "  " << list   << ". list\n"
       << "  " << QUIT     << ". quit\n"
       << "Enter your selection: ";

  // Read initial shape number from user
  cin >> com1;
while (com1 != QUIT) {
     
    switch (com1== average)
      {

      case AVG:  // code not relevant 

     } 
    
    switch (com1 == list)
      {
  cout << "Room"<< setw(10) << "Age" << setw(10) << "Acre" << setw(10) << "Money" << setw(10) << endl;


cout << room<< setw(10) << age << setw(10) << acre << setw(10) << money << setw(10) << endl;
}


If your data is always going to be arranged in groups of 4 consider creating a struct instead of 4 separate variables.
https://www.learncpp.com/cpp-tutorial/introduction-to-structs-members-and-member-selection/

You can then create an array with the struct type, reading each line of data into the struct's members.

You are going to have problems with your arrays as currently coded, the sizes need to be compile-time sized. When dealing with unknown amounts of data, from a file or the user, I would suggest using a std::vector instead of a regular array.
https://www.learncpp.com/cpp-tutorial/an-introduction-to-stdvector/

And lastly, if you have a compiler that can use C++20 there is std::format that can be used to easily create formatted table output.
https://en.cppreference.com/w/cpp/utility/format/format
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
#include <format>

int main()
{
   std::cout << "This program calculates n! and the sum of the integers "
             << "up to n for values 1 to limit.\n";
   std::cout << "What upper limit for n would you like? ";

   unsigned int limit {};
   std::cin >> limit;

   // the format string for all rows of the table
   const auto table_format { "{:>8} {:>8} {:>20}\n" };

   // output column headings
   std::cout << std::format(table_format, "integer", "sum", "factorial");

   for (unsigned long long n { 1 }, sum { 1 }, factorial { 1 }; n <= limit;
        ++n, sum += n, factorial *= n)
   {
      std::cout << std::format(table_format, n, sum, factorial);
   }
}
This program calculates n! and the sum of the integers up to n for values 1 to limit.
What upper limit for n would you like? 15
 integer      sum            factorial
       1        1                    1
       2        3                    2
       3        6                    6
       4       10                   24
       5       15                  120
       6       21                  720
       7       28                 5040
       8       36                40320
       9       45               362880
      10       55              3628800
      11       66             39916800
      12       78            479001600
      13       91           6227020800
      14      105          87178291200
      15      120        1307674368000
You are going to have trouble with that dataIn.eof() too...

For 500 elements a std::vector is fine, but if you have a massive, unknown amount of data prefer a std::deque. It isn’t O(1) lookup any more, but it is still almost as good.

The advice to use a struct/class is correct:

1
2
3
4
struct house
{
  int room, age, acre, money;
};

Now we write a stream extraction operator to read a “house”:

1
2
3
4
5
6
std::istream & operator >> ( std::istream & stream, house & h )
{
  stream >> h.room >> h.age >> h.acre >> h.money;
  stream.ignore( std::numeric_limits <std::streamsize> ::max(), "\n" );  // read to eol
  return stream;
}

And finally we can write a function to load the file:

1
2
3
4
5
6
7
8
9
std::vector <house> read_houses( const std::string & filename )
{
  decltype(read_houses("")) hs;
  std::ifstream f( filename );
  f.ignore( std::numeric_limits <std::streamsize> ::max(), "\n" ); // skip first line
  house h;
  while (f >> h) hs.push_back( h );
  return hs;
}

Now life is extra-super easy:

1
2
3
auto houses = read_houses( "datafile.dat" );

...


BTW, when you write code, you should regularly check that it actually compiles with the highest warning levels you can manage, and just work your way through the list of errors, one at a time, until it does compile.

For example, C++ uses " for strings, not ' . The compiler should have warned you about line 30.

Compile options I typically use:

    MSVC cl /EHsc /W4 /Ox /std:c++17 main.cpp 
    Clang clang++ -Wall -Wextra -pedantic-errors -O3 -std=c++17 main.cpp

(GCC is the same as Clang, just use g++ instead of clang++.)

Hope this helps.
[/code]
@OP

It depends on a lot of factors whether the structs, vectors et al, which make life much simpler, are allowed. If you are resticted to parallel arrays which you code revolves around then reading the data in and setting up the arrays is fairly easy.

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
#include <iostream>
#include <iomanip>
#include <fstream>
#include <string>

using namespace std;

int main()
{
    string scoreDataFilename = "datafile.dat";
    ifstream dataIn;
    dataIn.open(scoreDataFilename);
    
    if ( !dataIn.is_open() )
    {
        cout << "No file\n";
        exit(1);
    }
    
    int NO_ITEMS{0};
    string line;
    
    // COUNT UP ITEMS
    while(getline(dataIn, line) )
    {
        NO_ITEMS++;
    }
    cout << "No of items: " << NO_ITEMS << '\n';
    
    // DYNAMICALLY ALLOCATE PARALLEL ARRAYS
    int* room = new int[NO_ITEMS];
    int* age = new int[NO_ITEMS];
    int* acre = new int[NO_ITEMS];
    int* money = new int[NO_ITEMS];
    
    // REWIND FILE
    dataIn.clear();
    dataIn.seekg(0);
    
    // POPULATE ARRAYS
    int i{0};
    while ( dataIn >> room[i] >> age[i] >> acre[i] >> money[i] )
    {
        ++i;
    }
    dataIn.close();
    
    // DISPLAY DATA
    cout
    << "      Room       Age      Acre     Money\n"
    << "----------------------------------------\n";
    
    for(int i = 0; i < NO_ITEMS; i++)
    {
        cout
        << setw(10) << room[i] << setw(10) << age[i] << setw(10)
        << acre[i] << setw(10) << money[i] << '\n';
    }
    
    return 0;
}


That gets all your arrays loaded up ready for processing, including sorting which is where the use of arrays is a pain, but not impossible.

I am not going to code it because - I'm lazy, and more importantly you might be able to use the containers mentioned.

However, the essence of the sort is ...
1. Choose the attribute(s) for the sort.
2. Copy the relevant data array(s) - could even be a compound array
3. Create a same-size array for index values
4. Sort the copy and record the associated index
Last edited on
I do not recommend using parallel arrays if you can avoid it. It gets annoying, fast, and requires a lot of extra memory and hoop-jumping to do stuff that is extra simple.

For comparison, let’s sort based on the ages array:

Idiomatic C++

1
2
3
4
5
6
7
auto houses = read_houses( "datafile.dat" );

// sort by .age
std::sort( houses.begin(), houses.end(), []( auto a, auto b )
{
  return (a.age < b.age);
} );


C++ with parallel arrays

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
void sort_parallels_by( int rooms[], int ages[], int acres[], int moneys[], int size, int sorts[] )
{
  // create a (no-leak) list of indices into the arrays
  std::shared_ptr <int[]> indices( new int[size] );
  std::iota( &indices[0], &indices[size], 0 );
  
  // sort the indices
  std::sort( &indices[0], &indices[size], [&sorts]( int a, int b )
  {
    return sorts[a] < sorts[b];
  } );
  
  // rearrange the parallel arrays to match the new indices
  std::shared_ptr <int[]> copy_of_source_array( new int[size] );
  auto reorder = [&]( int target_array[] )
  {
    std::copy_n( &target_array[0], size, &copy_of_source_array[0] );
    for (int n = 0;  n < size;  n += 1) 
      target_array[n] = copy_of_source_array[indices[n]];
  };
  reorder( rooms );
  reorder( ages );
  reorder( acres );
  reorder( moneys );
}
1
2
3
// ...skipped all the stuff to read the parallel arrays... //

sort_parallels_by( rooms, ages, acres, moneys, NUM_ITEMS, ages );
Last edited on
There are even opportunities with functions:

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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#include <iostream>
#include <iomanip>
#include <fstream>
#include <string>

using namespace std;

int main()
{
    string scoreDataFilename = "datafile.dat";
    ifstream dataIn;
    dataIn.open(scoreDataFilename);
    
    if ( !dataIn.is_open() )
    {
        cout << "No file\n";
        exit(1);
    }
    
    int NO_ITEMS{0};
    string line;
    
    // COUNT UP ITEMS
    while(getline(dataIn, line) )
    {
        NO_ITEMS++;
    }
    cout << "No of items: " << NO_ITEMS << '\n';
    
    // DYNAMICALLY ALLOCATE PARALLEL ARRAYS
    int* room = new int[NO_ITEMS];
    int* age = new int[NO_ITEMS];
    int* acre = new int[NO_ITEMS];
    int* money = new int[NO_ITEMS];
    
    // REWIND FILE
    dataIn.clear();
    dataIn.seekg(0);
    
    // POPULATE ARRAYS
    int k{0};
    while ( dataIn >> room[k] >> age[k] >> acre[k] >> money[k] )
    {
        ++k;
    }
    dataIn.close();
    
    // DISPLAY DATA
    cout
    << "      Room       Age      Acre     Money\n"
    << "----------------------------------------\n";
    
    for(int i = 0; i < NO_ITEMS; i++)
    {
        cout
        << setw(10) << room[i] << setw(10) << age[i] << setw(10)
        << acre[i] << setw(10) << money[i] << '\n';
    }
    cout << '\n';
    
    // COPY SORT ARRAY (E.G. age)
    int* copy = new int[NO_ITEMS];
    for(int i = 0; i < NO_ITEMS; i++)
    {
        // copy[i] = age[i] * room[i]; // A COMPOUND INDEX
         copy[i] = age[i]; // A SIMPLE INDEX
    }
    
    // AND CREATE INDEX ARRAY
    int* index = new int[NO_ITEMS];
    for(int i = 0; i < NO_ITEMS; i++)
    {
        index[i] = i;
    }
    
    // BUBBLE SORT
    bool swapped;
    for (int i = 0; i < NO_ITEMS - 1; i++)
    {
        swapped = false;
        for (int j = 0; j < NO_ITEMS - i - 1; j++)
        {
            if (copy[j] > copy[j+1])
            {
                swap(copy[j], copy[j+1]);
                swap(index[j], index[j+1]);
                swapped = true;
            }
        }

        // BREAK INNER LOOP IF NONE SWAPPED
        if (swapped == false)
            break;
    }
    
    
    // DISPLAY SORTED DATA
    cout
    << "      Room       Age      Acre     Money\n"
    << "----------------------------------------\n";
    int sorted_index{0};
    for(int i = 0; i < NO_ITEMS; i++)
    {
        sorted_index = index[i];
        
        cout
        << setw(10) << room[sorted_index]
        << setw(10) << age[sorted_index]
        << setw(10) << acre[sorted_index]
        << setw(10) << money[sorted_index] << '\n';
    }
    
    return 0;
}


No of items: 8
      Room       Age      Acre     Money
----------------------------------------
         1         0        16         5
         2         5        12         7
         1         3         3        18
         2        22        20        12
         3        11        31        45
         7        12        34        44
         2        99        12        18
         5        81        12        15

      Room       Age      Acre     Money
----------------------------------------
         1         0        16         5
         1         3         3        18
         2         5        12         7
         3        11        31        45
         7        12        34        44
         2        22        20        12
         5        81        12        15
         2        99        12        18
Program ended with exit code: 0


I wouldn’t call any of this parallel array stuff an “opportunity”. 😉
I wouldn’t call any of this parallel array stuff an “opportunity”. 😉


The opportunity I'm talking about is the usual opportunity that arises with first passes, to simplify by using functions. The interesting thing for me is, once I decided to proceed is how easy it is to implement the 4 steps I alluded to in the sort part.

2000 random entries complete with displaying before and after simple and/or compound sorting is processed in a very reasonable time. 100,000 is still rapid with 1 million still being a 10-20 second job. Not bad considering.

Of course none of this means struct's etc aren't worthwhile. But who knows what stage @OP is at.
There is definitely an "opportunity" to learn. For example, that there are many possible approaches. That the "idiomatic C++" version is deliciously compact.

The againtry version has an interesting feature: sort without actually touching the data -- indirection. Rather than rearranging the data to new order, a set of indices is created. The "sorted" is just a "view" to data via those indices. There can be data that is expensive to copy/move and then these "views" make sense.

Now, "sorting the indices" could be done with "idiomatic C++" -- without "copy of ages" -- with a bit different lambda closure.
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
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <optional>
#include <algorithm>
#include <iomanip>

struct House {
	int room {}, age {}, acre {}, money {};
};

std::istream& operator>>(std::istream& is, House& h) {
	return is >> h.room >> h.age >> h.acre >> h.money;
}

using TLamd = bool (*)(const House&, const House&);

const TLamd crit[] {[](const auto& a, const auto& b) {return a.money < b.money; },
	[](const auto& a, const auto& b) {return a.acre < b.acre; },
	[](const auto& a, const auto& b) {return a.age < b.age; },
	[](const auto& a, const auto& b) {return a.room < b.room; }};

std::optional<std::vector<House>> readHouses() {
	std::string datafile, first;

	std::cout << "Enter filename to process: ";
	std::getline(std::cin, datafile);

	std::ifstream ifs(datafile);

	if (!ifs)
		return std::nullopt;

	std::vector<House> houses;

	std::getline(ifs, first);
	for (House h; ifs >> h; houses.push_back(std::move(h)));

	return houses;
}

int main() {
	auto read {readHouses()};

	if (!read.has_value())
		return (std::cout << "Cannot open file"), 1;

	auto& houses {*read};

	if (houses.empty())
		return (std::cout << "No data read\n"), 2;

	std::cout << houses.size() << " records read\n";

	unsigned opt {};

	do {
		do {
			std::cout << "\nSort by:\n" <<
				"1 money\n" <<
				"2 acre\n" <<
				"3 age\n" <<
				"4 room\n" <<
				"0 exit\n\n" <<
				"Enter option: ";

		} while ((std::cin >> opt) && opt > std::size(crit) && (std::cout << "Invalid option\n"));

		if (opt) {
			std::ranges::sort(houses, crit[opt - 1]);

			std::cout
				<< "\n      Room       Age      Acre     Money\n"
				<< "----------------------------------------\n";

			for (const auto& h : houses)
				std::cout
				<< std::setw(10) << h.room << std::setw(10) << h.age << std::setw(10)
				<< h.acre << std::setw(10) << h.money << '\n';
		}
	} while (opt);
}

Last edited on
A vector of tuples provides yet another means to arriving at a workable solution.
A vector of tuples is a common functional programming idiom. Along with that are free functions to access each element. Or, as C++ lets you do, structured binding constructs:

1
2
3
4
5
6
7
8
9
10
11
using house = std::tuple <int, int, int, int> ;

int house_to_room ( const house & h ) { return std::get<0>(h); }
int house_to_age  ( const house & h ) { return std::get<1>(h); }
int house_to_acre ( const house & h ) { return std::get<2>(h); }
int house_to_money( const house & h ) { return std::get<3>(h); }

void example_using_structured_binding( const house & h )
{
  auto [room, age, acre, money] = h;
}
And, in that vein, here is a solution looking for a problem using tuples and vectors:
std::tuple<int,double,std::vector<std::tuple<int,std::string,char>>> v1;
Topic archived. No new replies allowed.