Hey hey. You are 100% right, there were lots of mistakes in there. I am still getting used to zero indexing. It goes to show how good these forums are; I wouldn't have realized my mistakes if no one pointed them out.
Anyway, I believe I have fixed the errors. I also had a crack at some error handling. I used stringstreams to check if the data entries were non-numeric. I also implemented a test to see if all the rows were the same length. I made the program halt if any of these situations occured. This seemed sensible - you can't do much if a proper matrix cant be constructed.
Here is the code:
// Reads data from a file that is tab delimited.
// Data is returned as a 2D array.
#include <string>
#include <vector>
#include <iostream>
#include <fstream>
#include <sstream>
#include <cmath>
#include <cstdlib>
using namespace std;
float** ReadTable(const char* FileName, int& RowNum, int& ColNum) {
string line;
ifstream in(FileName);
// Determine number of rows and columns in file.
// Program halts if rows are not of same length.
while(getline(in,line,'\n')) {
string segments;
int ColsPerRow = 0; // Initialize counter.
stringstream ss;
ss << line;
while (getline(ss,segments,'\t')) {
ColsPerRow++;
}
if (RowNum == 0) {
// Define number of columns in file as the number
// of columns in the first row.
ColNum = ColsPerRow;
} else {
if (ColsPerRow != ColNum) {
cerr << "Row " << RowNum << " is not the same length "
"as row 0." << endl;
exit(0);
}
}
RowNum++;
}
// Declare arrays for storing and accessing
// data from the file.
float** pa2d = new float*[RowNum];
float* parr = new float[RowNum*ColNum];
// Reposition to start of stream buffer.
in.clear();
in.seekg(0, ios::beg);
// Write data to array:
for (int i = 0; i < (RowNum*ColNum); i++) {
// Declarations.
float in_float;
char in_char;
string in_s;
stringstream in_ss;
// Insert data entry into stringstream.
in >> in_s;
in_ss << in_s;
// Convert data entry to float.
in_ss >> in_float;
if (!in_ss) {
// Check if stream is in error state.
// Halt program if it is.
float r = floor(i/ColNum); float c = i - (r*ColNum);
// (Convert 1D array position to 2D array position.)
cerr << "Bad input: Entry [" << r << "," << c
<< "] put stream into error state." << endl;
exit(0);
} else {
if (in_ss >> in_char) {
// The attempt to extract a character is a test
// to see if there is anything left in the stream
// after we extract a float. Program is halted if
// if a non-numeric entry is found.
float r = floor(i/ColNum); float c = i - (r*ColNum);
// (Convert 1D array position to 2D array position.)
cerr << "Bad input: Entry [" << r << "," << c
<< "] is not numeric." << endl;
exit(0);
} else {
// Everything is OK, build array.
parr[i] = in_float;
}
}
}
// Prepare 2D array to be returned.
// Define pointer position at start of each row.
for (int i = 0; i < RowNum; i++) {
pa2d[i] = parr + (i*ColNum);
}
return pa2d;
}
void PrintMatrix(float** matrix, const int RowNum, const int ColNum) {
// Print "matrix" to console.
cout << '\n';
for (int i=0; i < RowNum; i++) {
for(int j=0; j < ColNum; j++) {
cout << (j?"\t":"") << matrix[i][j];
// Don't insert tab before first element of each row.
}
cout << '\n';
}
}
int main() {
// Define filename.
const char* FileName = "floats_with_errors.txt";
// Initialize row and column counters.
int RowNum = 0;
int ColNum = 0;
// Perform reading of data file into 2D array.
float** data = ReadTable(FileName,RowNum,ColNum);
// Print contents of 2D array to console.
PrintMatrix(data,RowNum,ColNum);
return(0);
}
The code implements the following error handling.
1. Entries consisting of a mixture of numbers and characters are deemed as non-numeric.
2. Blank entries put the stream in an error state. This is identified and the program is halted.
3. Missing values result in varying row lengths. The code detects this and halts the program.
4. Mistakes, such as having two minus signs, put the stream in an error state. Again, this is identified.
Once again, thanks for the help.
I would be happy to hear any suggestions on how to improve the code.
Have a great Christmas!
Ben.