4

The following code works fine when reading two .txt files containing two 5X5 array.

    #include <iostream>
    #include <string>
    #include <fstream>
    #include <sstream>
    #include <stdio.h>
    #include <vector>
    #include <sstream>

    using namespace std;

    int main()
    {
        string myFile, mysecondFile, mystring;
        string DIR;
        string extension;
        int total = 0;

        int number_of_lines = 0;
        string line;

        extension = ".txt";
        DIR = "H:\\Year2\\EE273\\EE273\\Week6\\";

        cout << "Enter the name of the file: \t";
        cin >> myFile;
        cout << "Enter the name of the second file: \t";
        cin >> mysecondFile;

        myFile = DIR + myFile + extension;
        mysecondFile = DIR + mysecondFile + extension;

        ifstream inFile;
        ifstream inFile2;

    int i=5;
    int j=5;
    int i2=5;
    int j2=5;
    int i3=5;
    int j3=5;
    int k;
    int l;

int Array[5][5];
int Array2[5][5];
int Array3[5][5];
string attempt1,attempt2;
int row = 0;
int col = 0;
int row2 = 0;
int col2 = 0;//i = row
                    //y = column

inFile.open(myFile.c_str());


if (!inFile) {
    cout <<"Error opening file"<<myFile<<endl;
    return -1;
}

while (!inFile.eof())
{
    getline(inFile, attempt1);
    stringstream iss( attempt1 );
    string result;
    col = 0;
    while (getline( iss, result, ','))
    {
        //cout << result << endl;
        Array[row][col] = atoi(result.c_str());
        //j = j + 1;
        col = col + 1;

    }
    row = row + 1;
}
inFile.close();

inFile2.open(mysecondFile.c_str());
if (!inFile2) {
    cout <<"Error opening file"<<mysecondFile<<endl;
    return -1;
}
while (!inFile2.eof())
{
    getline(inFile2, attempt2);
    stringstream iss( attempt2 );
    string result2;
    col2 = 0;
    while (getline( iss, result2, ','))
    {   
        //cout << result2 << endl;
        Array2[row2][col2] = atoi(result2.c_str());
        col2 = col2 + 1;
    }
    row2 = row2 + 1;
}
inFile2.close();

/*for (int i=0;i<5;i++){
    for (int j=0; j<5; j++){
        cout<<Array[i][j]<<endl;}}
for (int i2=0;i2<5;i2++){
    for (int j2=0; j2<5; j2++){
        cout<<Array2[i2][j2]<<endl;
    }}

Here I am carrying out the multiplication between the two matrices and writing the resulting values to a third matrix.

int Total=0;
i=0;
j2=0;
j=0;
j3=0;
for (i3=0; i3<5; i3++) {
    while(j3<5){
            while (j<5){
            for (i2=0;i2<5;i2++){
            Total += Array[i][j]*Array2[i2][j2];
            j++;
            Array3[i3][j3]=Total;

            }}
            j=0;
            j2++;
            j3++;
            Total=0;
            }
    i++;
    j=0;
    j2=0;
    j3=0;
    Total=0;
}

My question is: what is the easiest way to modify the code so that it can read two .txt files containing an array of any size and then carry out the multiplication successfully?

EDIT I have to do this using arrays only, I can't use vectors unfortunately.

Am I correct in thinking the new operator is involved?

Paolo
  • 21,270
  • 6
  • 38
  • 69
  • 6
    Use [`std::vector`](http://en.cppreference.com/w/cpp/container/vector) instead of naked arrays. – Captain Obvlious Dec 15 '14 at 13:46
  • Thing is this is an assessment and they told us to only use arrays – Paolo Dec 15 '14 at 13:47
  • 2
    Gee, don't you think you should have included that constraint in your post? – Captain Obvlious Dec 15 '14 at 13:48
  • @Paolokiller I think you mean assignment... – Borgleader Dec 15 '14 at 13:49
  • 1
    @Paolokiller: So write something that behaves like `std::vector` (reallocating and moving elements when it needs to grow beyond its current capacity), then use that instead of naked arrays. (And yes, that will involve `new` and `delete`. You should also read about the [Rule of Three](http://stackoverflow.com/questions/4172722). Pointer-juggling is tricky, which is why we have friendly library facilities like `vector` to manage it for us.) – Mike Seymour Dec 15 '14 at 13:49
  • @MikeSeymour appreciate the importance of vectors but I have to do this using arrays only. I have edited the op post, sorry for not being clear enough – Paolo Dec 15 '14 at 13:57
  • 1
    @Paolokiller: You mean you can't wrap up the array in a nice class that manages it for you? That's rather a bizarre constraint; but if that really is the case, just do the same thing (allocate a larger array, move the data, and delete the old one) in a grotesque lump of unencapsulated code. – Mike Seymour Dec 15 '14 at 14:00

3 Answers3

3

The "easiest" way would be to do something naive, like reading the file once fully to get the number of rows/cols, then reading the file again to actually store the values in the matrix:

unsigned int rows = 0;
unsigned int cols = 0;

std::string line;
while (std::getline(inFile, line)) {
    rows++;
    std::stringstream ss(line);

    std::string col;
    while (std::getline(ss, col, ',')) {
        cols++;
    }
}

// Now allocate the rows*cols matrix
int** matrix = new int*[rows];
for (int i = 0; i < rows; i++) {
    matrix[i] = new int[cols];
}

// and read your values into the matrix ...
// matrix[m][n] = xxx

It's pretty inefficient to read a file twice; and there are other ways to obtain the size beforehand. For example you could have a convention in your input file to include the matrix width/height before the data:

[infile.txt]
3,3
1,2,3
4,5,6
7,8,9

Now you can read the first line of the file, and you'll know that the rest of this file contains a 3x3 matrix. Allocate your matrix with new (similar to above example), then continue to read the rest of the file into it.

Remember to clean up your dynamically allocated matrices with delete[]. There should be 1 call to delete for every call to new.

for (int i = 0; i < rows; i++) {
    delete[] matrix[i];
}
delete[] matrix;
Julian
  • 1,688
  • 1
  • 12
  • 19
  • This is exactly what I was looking for. As long as the getline keeps finding values then the rows and columns will be incremented. I will try the code later but I am sure it will work perfectly fine. Why do you say it is inefficient? Is it not the most efficient solution if, say, we want to open to .txt files of which the contents are unknown? – Paolo Dec 15 '14 at 16:55
  • 1
    @Paolokiller Doing anything twice is always going to take more time than doing the same thing once. In particular, reading a file from disk is generally a slow operation, so you'll want to avoid doing it redundantly. In my answer, I included one such method of doing so: `include the matrix width/height before the data` :) You could also store the file as binary and determine the size with `seekg()` and `tellg()`, but this comes with other complexities (e.g., not human readable). If it were permitted, `std::vector` would work too. There are always trade-offs between solutions. – Julian Dec 15 '14 at 17:19
1

Use std::vector instead of raw arrays. E.g. you can push_back an item on a vector. And more crucially, you can create it with a size known only at run-time, e.g. from information in a file.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
1

The easiest approach requires the file to contain the size of the matrix as its first entries. With that, you can fallback to using C (C++ does not tolerate matrices of dynamic size) and do the following:

  1. Read the dimension of the matrix into variables width and heigh.

  2. Allocate the matrix using

    int (*dynamicMatrix)[width] = malloc(height*sizeof(*dynamicMatrix));
    
  3. Reuse your code to fill the matrix.

If you can't fall back to C, and can't use std::vector<>, the only thing left to you is to use a double pointer:

int**dynamicMatrix = new int*[height];
for(size_t i = width; i--; ) dynamicMatrix[i] = new int[width];

Again, this is easiest if you can define the first two numbers in a file to contain the width and height of the matrix in the file. If you can't code these two numbers into your file, you have to grow your dynamic arrays as you go:

size_t lines = 0, allocatedLines = 8;
int** dynamicMatrix = new int*[allocatedLines];
while(/* can read a line */) {
    if(lines == allocatedLines) {
        int** temp = new int*[allocatedLines *= 2];
        for(size_t i = lines; i--; ) temp[i] = dynamicMatrix[i];
        delete[] dynamicMatrix;
        dynamicMatrix = temp;
    }

    //add one line
    size_t curLineLength = 0, allocatedLineLength = 8;
    dynamicMatrix[lines++] = new int[allocatedLineLength];

    //fill the line
    ...
}

A similar block for reallocating a line would need to go into the loop where you read the elements of a single line. This is tedious; but the only way to get better is to use stuff that you are not allowed to use.


Btw: even the reallocating stuff is easier in C, since it provides the realloc() function:

size_t lines = 0, allocatedLines = 8;
int** dynamicMatrix = malloc(allocatedLines * sizeof(*dynamicMatrix));
while(/* can read a line */) {
    if(lines == allocatedLines) {
        //realloc takes care of copying the data to a new location (if that is necessary):
        allocatedLines *= 2;
        dynamicMatrix = realloc(dynamicMatrix, allocatedLines * sizeof(*dynamicMatrix));
    }

    //add one line
    size_t curLineLength = 0, allocatedLineLength = 8;
    dynamicMatrix[lines++] = malloc(allocatedLineLength * sizeof(**dynamicMatrix));

    //fill the line
    ...
}

Since there is no equivalent to realloc() to work with new/delete, you are required to either use std::vector<> in C++, or to do the copying yourself as above.

cmaster - reinstate monica
  • 38,891
  • 9
  • 62
  • 106
  • 1
    I am writing the program in C++ so I don't think I should be using malloc. My original idea to approach the problem was to use getline to read into the txt files and have one variable for rows and one for columns which increase according to the file being read. That does make sense? I am not sure of how to do so but I think if I can get the program to know how many rows/cols there are in the files I can get the rest working... – Paolo Dec 15 '14 at 14:11
  • 1
    I removed the mention of `std::vector<>` now, and replaced it with a basic description of what you need to do in C++ without it. Hope that helps. – cmaster - reinstate monica Dec 15 '14 at 14:46