0

I want to read a .txt file.

.txt file will have N-rows and M-cols.

Each word present in the txt file will have varying length.

Sample txt file:

Suppose N = 4 rows

Suppose M = 5 cols

content of txt file:

aa bbb cc dddddddd eeee

aa bbbbbbbbbbbb cc ddddddddddd eeee

aaaaaaaaaa bb cc d e

a b c d eeee

What I have to do:

I have to store these strings into a 2D array of strings such that it looks like this:

arr[4][5] =  

[aa             bbb              cc     dddddddd        eeee]

[aa             bbbbbbbbbbbb     cc     ddddddddddd     eeee]

[aaaaaaaaaa     bb               cc     d               e   ]

[a              b                c      d               eeee]

I know how to create dynamic 2D array of integer and its working fine:

int** arr;
int* temp;

arr = (int**)malloc(row*sizeof(int*));
temp = (int*)malloc(row * col * sizeof(int));
for (int i = 0; i < row; i++)
{
    arr[i] = temp + (i * col);
}
int count = 0;
//setting values in 2-D array
for (int i = 0; i < row; i++)
{
    for (int j = 0; j < col; j++)
    {
        arr[i][j] = count++;
    }
}

But, when I am trying to do the same thing for strings, its crashing.

string** arr;
string* temp;

arr = (string**)malloc(row*sizeof(string*));
temp = (string*)malloc(row * col * sizeof(string));
for (int i = 0; i < row; i++)
{
    arr[i] = temp + (i * col);
}

//setting values in 2-D array
for (int i = 0; i < row; i++)
{
    for (int j = 0; j < col; j++)
    {
        arr[i][j].append("hello"); // CRASH here !!
    }
}

How to store each words in an array??

This is what I have written:

#include "stdafx.h"
#include <cstdlib>
#include <iostream>
#include <vector>
#include <map>
#include <fstream>
#include <string>
#include <algorithm>
#include <assert.h>     /* assert */
using namespace std;

vector<string> readFile(const string file, int& row, int& col)
{
    vector<string> buffer;

    ifstream read(file);
    string line;
    char * writable = NULL;

    if (read.is_open())
    {
        int temp_counter = 0;
        while (!read.eof())
        {
            std::getline(read, line);
            writable = new char[line.size() + 1];
            std::copy(line.begin(), line.end(), writable);
            writable[line.size()] = '\0'; // don't forget the terminating 0
            if (temp_counter == 0)//
            {
                row = std::stoi(line);
                ++temp_counter;
            }
            else if (temp_counter == 1)
            {
                col = std::stoi(line);
                ++temp_counter;
            }
            else
            {
                buffer.push_back(line);
            }       
        }
    }
    // don't forget to free the string after finished using it
    delete[] writable;
    return buffer;
}

void create2DDynamicArray(std::vector<string>&v, int row, int col)
{
    string** arr;
    string* temp;

    arr = (string**)malloc(row*sizeof(string*));
    temp = (string*)malloc(row * col * sizeof(string));
    for (int i = 0; i < row; i++)
    {
        arr[i] = temp + (i * col);
    }


    //setting values in 2-D array
    for (int i = 0; i < row; i++)
    {
        for (int j = 0; j < col; j++)
        {
            arr[i][j].append("hello");
        }
    }
}
int main()
{
    vector<string> myvector;
    int row=0;
    int col=0;

    myvector = readFile("D:\\input.txt", row, col);
    create2DDynamicArray(myvector, row, col);

    getchar();
    return 0;
}

txt file look like:

4

5

aa bbb cc dddddddd eeee

aa bbbbbbbbbbbb cc ddddddddddd eeee

aaaaaaaaaa bb cc d e

a b c d eeee

Jatin
  • 1,857
  • 4
  • 24
  • 37
  • You change int to char. You allocate the space for string and not for int. – Mirakurun Aug 25 '16 at 07:27
  • Add allocate an extra entry for the null-character of each string. – barak manos Aug 25 '16 at 07:28
  • 2
    Why not choose *one of* C and C++? If you are going to use C, they say [you shouldn't cast the result of `malloc()` in C](http://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc). If you are going to use C++, why not use `new[]` instead of `malloc()`? – MikeCAT Aug 25 '16 at 07:30
  • "But, when I am trying to do the same thing for strings, its crashing." How? Why not post a [Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve)? – MikeCAT Aug 25 '16 at 07:30
  • If you are going to use C++, why even care about allocating like this. – mtszkw Aug 25 '16 at 07:31
  • "I know how to create dynamic 2D array of integer" No you don't. See [How do I correctly set up, access, and free a multidimensional array in C?](http://stackoverflow.com/questions/12462615/how-do-i-correctly-set-up-access-and-free-a-multidimensional-array-in-c). – Lundin Aug 25 '16 at 09:20

5 Answers5

4

Don't use malloc in C++. It does not run the constructor of the strings, thus not allocating space for the dynamic char array stored in them. Try the new[] operator or smart pointers instead.

string **arr;
arr = new string*[height];
for (int i = 0; i < height; i++)
    arr[i] = new string[width];

A c++ string is just a kind of wrapper around a dynamic char array, which must be initialized (it should have memory assigned to it). By using malloc you don't call the constructor, resulting in accessing a non allocated memory area.

Adrian Jałoszewski
  • 1,695
  • 3
  • 17
  • 33
2

I suggest to avoid fragmentation and use a real 2d array.

In C, since C99 you can use VLA's (variable length arrays):

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    int rows = 4, cols = 5;
    char *(*arr)[cols];
    int i, j;

    arr = malloc(sizeof(*arr) * rows);
    for (i = 0; i < rows; i++) {
        for (j = 0; j < cols; j++) {
            arr[i][j] = "hello"; /* For read only, to be writable use strdup */
        }
    }
    for (i = 0; i < rows; i++) {
        for (j = 0; j < cols; j++) {
            printf("%s\t", arr[i][j]);
        }
        printf("\n");
    }
    free(arr);
    return 0;
}

Output:

hello   hello   hello   hello   hello   
hello   hello   hello   hello   hello   
hello   hello   hello   hello   hello   
hello   hello   hello   hello   hello   

arr[i][j].append("hello"); // CRASH here !!

There are not methods in C and this wont compile, why are you mixing C and C++? pick one of them.

David Ranieri
  • 39,972
  • 7
  • 52
  • 94
  • 1
    Even if C99 isn't available, this method works even on ancient C compilers or C++ compilers, as long as rows and cols are compile-time constants. I personally prefer to use the style `arr = malloc(sizeof(char*[rows][cols]));` as that makes the intent clearer, it is self-documenting code. – Lundin Aug 25 '16 at 09:23
  • @Lundin are you sure? with `gcc -std=c89` : `ISO C90 forbids variable length array ‘arr’` – David Ranieri Aug 25 '16 at 09:28
  • The warning disapears when `cols` is defined as `enum {cols = 5};` or `#define cols 5` but is still there using with `int cols = 5;` even using `const`: related question: http://stackoverflow.com/q/436300/1606345 – David Ranieri Aug 25 '16 at 09:54
  • Yes that's what I meant, you have to make it a compile-time constant. Unfortunately, C doesn't treat `const` variables as compile-time constants (but that might work in C++). – Lundin Aug 25 '16 at 09:57
  • @Lundin Ah, ok ;) – David Ranieri Aug 25 '16 at 09:59
  • Anyway, that's of peripheral interest, since nobody should be using C90 for new programs in the year 2016. And C++ programmers wouldn't use VLAs but a std::something container. – Lundin Aug 25 '16 at 10:01
1

Don't use malloc, avoid new/new[] and use RAII containers:

std::vector<std::vector<std::string>> readFile(const std::string& filename)
{
    std::ifstream file(filename);
    int row;
    int col;
    file >> row >> col;

    std::vector<std::vector<std::string>> words(row, std::vector<std::string>(col));

    for (auto& rows : words) {
        for (auto& word : rows) {
            file >> word;
        }
    }
    return words;
}
Jarod42
  • 203,559
  • 14
  • 181
  • 302
1

enter image description here

Vectors are also Dynamic Arrays , but with all the work of keeping track of pointers hidden from the user.

If you decide to use vectors instead then coding a 2D dynamic array is as easy as this:

#include <iostream>
#include <vector>
#include <string>
#include <fstream>       
using namespace std;


int main () {

    cout<<"\nDynamic 2D Array.\n\n";

    // create string vector
    vector<string> vArray;

    // create one line string
    string line;

    // open file for reading
    ifstream fileToRead("d2d.txt");
    while (getline(fileToRead, line)){

        //  fuse (store) line from file in vector
        vArray.push_back(line);
    }
    fileToRead.close();

    // display results
    for (int i=0; i< vArray.size();i++){
        cout<<" [ "<< vArray[i] <<" ] \n";
    }

cout<<"\nPress ANY key to close.\n\n";
cin.ignore(); cin.get();
return 0;
} 
Software_Designer
  • 8,490
  • 3
  • 24
  • 28
0

If you really want to use 2D array with memory allocated with malloc my suggestion is to shift from string ** to string *** type, like:

    ifstream f(your_file_name);

    string*** arr;

    arr = (string***)malloc(row*sizeof(string**));

    for (int i = 0; i < row; i++)
    {
        arr[i] = (string**)malloc(col * sizeof(string*));
    }

    //setting values in 2-D array
    for (int i = 0; i < row; i++)
    {
        for (int j = 0; j < col; j++)
        {
            arr[i][j] = new string();
            f >> *arr[i][j]; // or arr[i][j] -> append("hello");
        }
    }

But if it really C++ project consider using vector<vector<string>> or even use new instead of malloc, like:

    ifstream f("tmp.txt");

    string** arr;

    arr = new string*[row];

    for (int i = 0; i < row; i++)
    {
        arr[i] = new string[col];
    }

    //reading 2-D array from file
    for (int i = 0; i < row; i++)
    {
        for (int j = 0; j < col; j++)
        {
            f >> arr[i][j];
        }
    }
    // show file content
    for (int i = 0; i < row; i++)
    {
        for (int j = 0; j < col; j++)
        {
            cout << arr[i][j] << " ";
        }
        cout << endl;
    }
VolAnd
  • 6,367
  • 3
  • 25
  • 43