while loops, txt file and arrays

Hi I'm currently working on something for the future where I want to read through a comma separated txt file with a known amount of columns but not rows and I want to take each row and turn it into an array. I then want to do calculations on each array.
I was wondering if there is a method on going through a row with a while loop
to get the array and each time it passes through the loop it changes the values of the array with the next row in the file.

I know you can loop through a endless row file using this code below to stop when the file is finished this is why I am asking

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
using namespace std;

int main()
{
ifstream inputFile;
int num;

inputfile.open(val.txt)

while(inputFile >> num)
{
cout << num << endl;
}
return 0;
}


If the file was basic like

1
2
3
4
5

but my txt file will look like

1.1,1.3,15,18
2,1.5,7.3,3.5
and so on for an unknown amount of rows
This will read the file into an array. The number of rows and cols for each row is determined from reading the file.

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

int main()
{
	std::ifstream ifs("val.txt");

	if (!ifs)
		return (std::cout << "Cannot open file\n"), 1;

	std::vector<std::vector<double>> array;

	for (std::string line; std::getline(ifs, line); ) {
		std::istringstream iss(line);
		std::vector<double> row;

		for (double val; iss >> val; row.emplace_back(val), iss.ignore());
		array.emplace_back(row);
	}

	for (const auto& r : array) {
		for (const auto& c : r)
			std::cout << c << ' ';

		std::cout << '\n';
	}
}



1.1 1.3 15 18
2 1.5 7.3 3.5


array.size() gives the number of rows.
Last edited on
Hi seeplus thanks for another solution but is there a way where you don't need to use vectors ?
there are 2 easy ways.
if you know pointers, you can replace the vectors with those.
if you do not, but know arrays, you can make an array big enough for the expected data (will you need to process 1000 lines? 10,000? more?) and track size (how much of the array you used) yourself as you go.

is there a way where you don't need to use vectors ?


yes - as per jonnin's post above. Also you could only read/process the file row by row.

Why don't you want to use std::vector? Other ways complicate the code.
a comma separated txt file with a known amount of columns
1
2
3
4
for ( std::string line; std::getline(ifs, line); ) {
    std::istringstream iss(line);
    // read the known about of values from iss and do something with them
}


Hi I'm currently working on something for the future

Learning to use the C++ Standard Library (including std::vector) ASAP is a very good investment that leads to profits in future.
Allocation / deallocation method courtesy of @dutch here:
http://www.cplusplus.com/forum/beginner/267829/#msg1152277


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
#include <iostream>
#include <fstream>
#include <string>
using namespace std;


//===================================================================


int countRows( const string &filename )
{
   int num = 0;
   ifstream in( filename );
   for ( string line; getline( in, line ); num++ );
   return num;
}


//===================================================================


void readRows( const string &filename, double **a, int rows, int cols )
{
   ifstream in( filename );
   char comma;
   for ( int i = 0; i < rows; i++ )
   {
      in >> a[i][0];
      for ( int j = 1; j < cols; j++ ) in >> comma >> a[i][j];
   }
}


//===================================================================


void writeData( double **a, int rows, int cols )
{
   for ( int i = 0; i < rows; i++ )
   {
      for ( int j = 0; j < cols; j++ ) cout << a[i][j] << '\t';
      cout << '\n';
   }
}


//===================================================================


template<typename T> T **allocate( int rows, int cols )
{
   T **a = new T*[rows];
   a[0] = new T[rows * cols];
   for (int r = 1; r < rows; r++ ) a[r] = a[r-1] + cols;
   return a;
}


//===================================================================


template<typename T> void deallocate( T **a )
{
   delete [] a[0];
   delete [] a;
}


//===================================================================


int main()
{
   const int cols = 4;
   string filename = "val.txt";
   int rows = countRows( filename );   cout << rows << " rows read\n";

   double **a = allocate<double>( rows, cols );
   readRows( filename, a, rows, cols );
   writeData( a, rows, cols );
   deallocate( a );
}


2 rows read
1.1	1.3	15	18	
2	1.5	7.3	3.5	


Last edited on
Hi seeplus I found your method to work the best thanks for the advice and I am busy studying vectors a bit more they look far simpler to use
They are! :)
seeplus can you maybe explain what your line 20-21 does and how it functions ? I don't understand :))
L20 is a for loop. It repeatedly extracts a col value for the current row from the istringstream iss and adds them to the vector row until the stream extraction fails - when the end of the cols has been found.

L21 simply adds the vector row (which has the col entries for one row) to the vector array which contains one element (another vector for the cols) per row.

>> is the stream extraction

iss.ignore() just ignores the , between values. There's other ways of doing this, but as there's only 1 comma and no spaces, this is perhaps the easiest.
Do you mean the:
1
2
3
std::vector<double> row;
for (double val; iss >> val; row.emplace_back(val), iss.ignore());
array.emplace_back(row);

Lets break it into peaces:
1
2
3
4
5
6
7
8
9
std::vector<double> row;
for (
    double val; // init
    iss >> val; // condition
    row.emplace_back(val), iss.ignore() // increment
  )
  ; // empty statement

  array.emplace_back(row);

A for loop has init, condition, and increment statements and a body that is executed on every iteration.
In this case the body is just ;. It could have been written {}

The init is simple, variable 'val' is created before loop really begins.

The condition has "side-effect". It reads a number from stream 'iss' into 'val'. The actual condition is true, if that read succeeds.

The "increment" has two unrelated statements separated by comma-operator:
1
2
row.emplace_back(val)
iss.ignore()

Neither increments any loop counter. The first adds 'val' to 'row'.

The second used istream::ignore() http://www.cplusplus.com/reference/istream/istream/ignore/
The function call relies on default values of function parameters. Like one would have written: iss.ignore( 1, EOF )
That call reads (and discards) at most one character from 'iss'. Reads the comma.

Finally, the line 21 after the loop adds 'row' to 'array'.
Topic archived. No new replies allowed.