Reading file into 2D array, separated by comma

Hi, so my assignment is to create a French to English translator. My first task is to take a file and put the 1000 English and French words into a 2D array. The file looks like this:

French 1000
bonjour,hello
oui,yes
non,no
merci,thanks

I've tried using getline to help me separate the two, but I'm not sure its actually storing anything into the array, because I will need to sort the array after I create it. When I try to cout the array it gives me a memory address, so I'm confused on what I need to do.

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
 void Language::run()
{
    string fileName, language, line;
    ifstream inFile;
    int num;
    
    //ask user input for file and open file
    
    //cout << "Name of language file to be read in: ";
    //cin >> fileName;
    inFile.open( "french.txt");
    //inFile.open( fileName.c_str() );
    cout << "File successfully processed!" << endl;
    
    //take Language info and number of entries from top of .txt file
    inFile >> language >> num;
    
    //create a 2D array for the languages in size of number of entries
    string langArray[2][num];
    for(int i = 0; i < 2; i++)
    {
        for(int j = 0; j < num; j++)
        {
            if(getline(inFile,line, ','))
            {
                
                langArray[i][j] = line;
                cout << line << " ";
            }
        }
    }
}
Last edited on
Line 16 attempts to extract two whitespace-delimited tokens, but your example only shows 1000, and then each "langArray" line.

The other issue is that you don't have a continuous streak of comma-delimited tokens. Your actual sequence, after the 1000, is { word comma word newline word comma word newline ... }

So you need to call getline(inFile, line, ',') the first time to get the french word, and then call getline(inFile, line) (without the comma delimiter) to get the english word.

Also, VLAs (Variable-Length Arrays, your line 19) are not standard C++; prefer a vector if the size of the array is not known at compile-time.
Last edited on
Hello simulated sushi,

This is not valid in C++ string langArray[2][num];. "num" needs to be defined as a constant or just use 1000. The only way to use this is with a dynamic array. Also it should be string langArray[1000][2];.

I tried to figure out your for loops, but they are wrong and I need some more time to work on it.

In the inner for loop you are using the if statement to read the file, but you only read the first part of a line, but not the second part. Reading the second part is like the first read, but with out the 3rd parameter in the getline.

I will load this into something I can test and let you know.

Andy
@Handy Andy

I would really appreciate the help, I met with my tutor and he was saying it was alright but its obviously not. I'm very new to c++ and this is a data structures and algorithms class thats kicking me right now. I've been looking things up for this and I don't get it.
Maybe your tutor has different ideas about how C++ should be taught and also what should be used.
Since you are at his mercy stick to his advice and what he wants.
You can learn things properly later.
Hello simulated sushi,

Since I did not have enough of your code I created a full program to test the code. Most of what is in "main" can be copied to your function, but some may have to reside outside the function.

The comments tell you what I added or changed. I think I got everything.

This is based on what you started with:
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
#include <iostream>
#include <iomanip>
#include <string>
#include <limits>  // <--- Added.

#include <fstream>

int main()
{
    constexpr int MAXROWS{ 5 }, MAXCOLS{ 2 };

    std::string fileName, language, french, english;  // <--- Changed. Added last 2.
    std::ifstream inFile;
    int numRows;

    //ask user input for file and open file

    //cout << "Name of language file to be read in: ";
    //cin >> fileName;
    inFile.open("french.txt");
    //inFile.open( fileName.c_str() );  // <--- Not needed from C++11 on.
    std::cout << "File successfully processed!" << '\n';

    //take Language info and number of entries from top of .txt file
    inFile >> language >> numRows;
    inFile.ignore(std::numeric_limits<std::streamsize>::max(), '\n');  // <--- Requires header file <limits>. Added.

    //create a 2D array for the languages in size of number of entries
    std::string langArray[MAXROWS][MAXCOLS];

    //for (int i = 0, j = 0; i < numRows && inFile; i++, j = 0)
    //{
    //       if (std::getline(inFile, french, ','))
    //        {
    //            langArray[i][j++] = french;

    //            std::getline(inFile, english);

    //            langArray[i][j] = english;

    //            std::cout << i + 1 << ". " << french << " " << english << '\n';
    //        } 
    //}

    for (int i = 0, j = 0; i < numRows && std::getline(inFile, french, ',') && std::getline(inFile, english); i++, j = 0)
    {
        langArray[i][j++] = french;

        langArray[i][j] = english;

        std::cout << i + 1 << ". " << french << " " << english << '\n';
    }

    std::cout << "\n\n  For checking array contents.\n";

    for (int row = 0; row < 4; row++)
    {  
        std::cout << row + 1 << ". ";

        for (int col = 0; col < 2; col++)
        {
            std::cout << langArray[row][col] << "  ";
        }

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

	// <--- Keeps console window open when running in debug mode on Visual Studio. Or a good way to pause the program.
	// The next line may not be needed. If you have to press enter to see the prompt it is not needed.
	//std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');  // <--- Requires header file <limits>.
	std::cout << "\n\n Press Enter to continue: ";
	std::cin.get();

	return 0;  // <--- Not required, but makes a good break point.
}

The for loop was a good start, but you are over complicating the whole. The code above can be done in 1 for loop although it is different from a normal for loop.

The if statement is not needed because the reads are done in the for condition and therefor the loop will end when (eof) is set.

There are 2 different for loops and either will work. I left them both so you can see the difference.

Lines 54 - 74 I used for testing and are not required or needed. Line 68 on may be useful to you in the future.

Andy

Edit:

This produces the output of:

File successfully processed!  // <--- I think you mean opened. If not it is in the wrong place.

1. bonjour hello
2. oui yes
3. non no
4. merci thanks


  For checking array contents.
1. bonjour  hello
2. oui  yes
3. non  no
4. merci  thanks


 Press Enter to continue:
Last edited on
I would recommend using a single dimensional array of a struct/class instead of the 2d array. Something like:
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
#include <iostream>
#include <vector>
#include <fstream>
#include <string>

struct Translation
{
    std::string to_translate;
    std::string translated;
};

int main()
{
    std::string fileName, language, line;
    int num;

    std::ifstream inFile("french.txt");
    if(!inFile)
    {
        std::cerr << "Error opening input file.\n";
        return 1;
    }

    //take Language info and number of entries from top of .txt file
    inFile >> language >> num;
    /*********************************************************************
    {
        // Using an array, not recommended, see below.
        //create an array for the languages in size of number of entries
        Translation *translation = new Translation[num];
        size_t i = 0;

        while(getline(inFile, translation[i].to_translate, ','))
        {

            getline(inFile, translation[i].translated);
            ++i;
        }

        delete[] translation;
    }
    ************************************************************************/
    {
        // Using std::vector:
        std::vector<Translation> translation(num);
        size_t i = 0;

        // Get the first word from the file.
        while(getline(inFile, translation[i].to_translate, ','))
        {
            // Read the second word from the file.
            getline(inFile, translation[i].translated);
            ++i;
        }
    }
    /***********************************************************************
    {
        // Or using dynamic array of two strings.
        std::string(*array)[2] = new std::string[num][2];
        size_t i = 0;

        // Get the first word from the file.
        while(getline(inFile, array[i][0], ','))
        {
            // Read the second word from the file.
            getline(inFile, array[i][1]);
            ++i;
        }

        delete[] array;
    }
    *************************************************************************/
}




 
 string langArray[2][num];


Unfortunately, this is not standard C++ - although some non-conforming compilers allow it. For standard C++, the size of the array has to be known at compile time. If the size is only known at run-time (such as here when reading a file), then using a vector instead is the way to go.

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

struct Translate {
	std::string french;
	std::string english;

	Translate(const std::string& f, std::string& e) : french(f), english(e) {}
};

int main()
{
	std::ifstream inFile ("french.txt");

	if (!inFile.is_open()) {
		std::cout << "File cannot be opened!\n";
		return 1;
	}

	int num;

	inFile >> num;
	inFile.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

	std::vector<Translate> words;
	words.reserve(num);

	for (std::string french, english; std::getline(inFile, french, ',') && std::getline(inFile, english); words.emplace_back(french, english));

	for (const auto& w : words)
		std::cout << w.french << '\t' << w.english << '\n';
}


However, as the aim is to create a translator then at some point you'll need to look up a French word to convert to English. The easiest way to facilitate a look-up like this is to use a std::map. Consider:

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

int main()
{
	std::ifstream inFile ("french.txt");

	if (!inFile.is_open()) {
		std::cout << "File cannot be opened!\n";
		return 1;
	}

	int num;

	inFile >> num;
	inFile.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

	std::map<std::string, std::string> words;

	for (std::string french, english; std::getline(inFile, french, ',') && std::getline(inFile, english); words[french] = english);

	for (const auto& w : words)
		std::cout << w.first << '\t' << w.second << '\n';

	std::cout << "\noui is ";

	const auto w {words.find("oui")};

	if (w != words.end())
		std::cout << w->second << '\n';
	else
		std::cout << "<not found>\n";
}


Topic archived. No new replies allowed.