How to Initialize Integers + String Arrays

closed account (9h5X216C)
Hello, I'm new to C++ and need help! I'm currently learning how to do 2D arrays or just 1D arrays.

I have a text file with the following contents shown below, and I'm basically trying to store them into an array or more depending? Is it true that I can have just 1 Array if I use Struct, or 5 individual Arrays based on the contents in the file below?

I believe that the [1, 2] can be formed into a 2D Array, but how do I go about implementing it?

Below are my codes for using a struct, and I'm not sure if I did it right?

Please help!

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
=========================================================
Sample Contents in 'HelloWorld.txt' File
=========================================================
[1, 2]-3-4-Hello_World
[5, 6]-7-8-World_Hello
[9, 1]-2-3-Welcome_Back
[4, 5]-6-7-World_Bye
[8, 9]-1-2-Bye_World

=========================================================
My Sample Codes
=========================================================
struct Example()
{
	fstream file;
	file.open("HelloWorld.txt");
				
	vector<string> abc;	
	string line;
	
	while(getline(file, line, '-'))
	{
		abc.push_back(line);

		int** a = new int*[abc.size()];
    		for(int i = 0; i < abc.size(); i++)

       		cout << abc[i] << endl;
		
		delete [] a;
	}
}


My main objective is to be able to store all the 4 integers + 1 string into arrays. Please advise!

Thank you!
2-d array of what?
I would do it like this, perhaps:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct hw_data  //this is a simple user-defined type that puts the 
//different data types together into one wrapper type. 
{
     vector<int> fournums(4); //an array of the 4 integets
     string worldtext; //the string data
};

.. main
{
    vector<hw_data>Hello_Data(100);  //now you can make an 'array' of these, 
//how about 100 for a starting value?  take your best guess on how many, 
//you can resize it later if you need more. 
     x = Hello_Data[0].fournums[2]; //access the third (0,1,2 <---  ) number of the first [0] item.       
}


structs cannot have loops (they can have functions that have loops inside them but we don't need that right now) and you probably don't really want the file variable in with the actual data (you can, but its a weird design, esp if you make an array of them which one is the real open file?)

Also don't bother with pointers. use vectors. If you must, use a double vector for 2-d, but I don't think you need 2-d here, I think you need 1-d of custom item. Pointers should be avoided unless you really, really need one. These days, they more often use the address of something else (eg int* ip = &foo;) for easy access than new/delete mechanics.

Let me know if that helps. Your code.. cannot be saved. See if you can do it now with the struct syntax I gave you.

Get it working with this approach, and if you want after that, we can show you how to do the struct with a method so that the data handling function is inside the user type rather than external. But for now, just put that code in main and loop over your array and get the data into the container from the file, that is enough for one session.
Last edited on
Hello justagreenie,

Looking at the code you have posetd you are going about the program in all the wrong ways.

First a struct should hold data not an entire program.

The "fstream" and the open should be in main or another function not inside a struct. Defining a file stream as a "fstream" is OK, but in the open statement you must tell the statement if the stream is for input or output. As in file.open("HelloWorld.txt", std::ios::in); or out. Following the open statement you need to check if the file stream is open. If the file did not open properly the program will continue, but you will not be able to read anything.

Next you have used a vector. A good approach, I like vectors over arrays any day, but, I would make it a vector of structs where the struct would likely hold each number in its own variable and the string in its one variable.

Inside the while loop you read part of the line and put it into the vector, but only print it out. Kind of a wast of time when you should be extracking the numbers and the string into say a struct.

If you are not use to using pointers, and it looks like you are not, I would avoid creating the variable "a", which you are doing wrong, and stick with a traditional array. First you are creating a 2D array the wrong way, only creating the first dimension, and then deleting this memory at the end of the while loop only to do it again on the next iteration of the loop.

The for loop looks OK, but I would suggest a better name for the vector other than "abc". A name that describes what the variable is used for works best. Right now this seems easy, but in the future when you look back at this program and waste time trying to figure out the variable names you will wish you had learned to use better names earlier. Now is the time to start.

Do not be afraid to use comments in your program to let your-self and others know what is going on.

I do have a couple of questions.

Do the []s have any special meaning or use?

In the strings is there a reason for the _? As a string you can use a space.

Are you stuck with the layout of the input file or can it be changed?

This looks like some fun for the day. I will work up some code and see what I can come up with. A first thought is:
1
2
3
4
5
6
7
8
struct Info
{
	int num1{};
	int num2{};
	int num3{};
	int num4{};
	std::string sentence;
};

Not knowing what each number is used for I did not know what to call the ints for now. Inside main I would use std::vector<Info> info;.

I have found this code useful when first learning about files
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
std::string iFileName{ "" };  // <--- File name goes here.

std::ifstream inFile;

inFile.open(iFileName);

if (inFile.is_open())
{
	std::cout << "\n File " << iFileName << " is open" << std::endl;
	std::this_thread::sleep_for(std::chrono::seconds(2));  // <--- Needs header files chrono" and "thread".
}
else
{
	std::cout << "\n File " << iFileName << " did not open" << std::endl;
	std::this_thread::sleep_for(std::chrono::seconds(3));  // <--- Needs header files chrono" and "thread".
	exit(1);
}

This is used with the header file "<fstream>" which includes "istream" and "ostream".

I realize that the above code is a bit much, but it can be shortened when you have a better understanding of using file streams..

If you have any questions let me know,

Hope that helps,

Andy
> I'm currently learning how to do 2D arrays or just 1D arrays.

1D array: have a struct which contains a one dimensional array of four numbers.

2D array: have a vector of these structs. https://cal-linux.com/tutorials/vectors.html

It would be richly rewarding to get familiar with regular expressions.

For instance:
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
83
#include <iostream>
#include <string>
#include <regex>
#include <vector>
#include <fstream>
#include <iomanip>

struct info
{
    static constexpr std::size_t N = 4 ;
    int numbers[N] ;
    std::string text ;
};

bool parse_line( const std::string& line, info& inf )
{
     // https://en.cppreference.com/w/cpp/regex
     // ^ - beginning of string
     // \s -  white space character
     // \d+ - one or more decimal digits
     // .*$ - all remaining characters up to end of string
     // note: the five fields of interest are captured with ()
     // raw string literal: http://www.stroustrup.com/C++11FAQ.html#raw-strings
     static const std::regex info_re( R"(^\[(\d+)\,\s(\d+)\]\-(\d+)\-(\d+)\-(.*)$)" ) ;

     // https://en.cppreference.com/w/cpp/regex/regex_match
     std::smatch results ;
     if( std::regex_match( line, results, info_re ) )
     {
          for( std::size_t i = 0 ; i < info::N ; ++i ) inf.numbers[i] = std::stoi(results[i+1]) ;
          inf.text = results[5] ;
          return true ;
     }
     else
     {
          inf = {} ;
          return false ; // parse failed
     }
}

std::vector<info> parse_stream( std::istream& stm )
{
    std::vector<info> result ;

    std::string line ;
    info inf ;
    while( std::getline( stm, line ) )
    {
        if( parse_line( line, inf ) ) result.push_back(inf) ;
        // else std::cerr << "badly formed line " << std::quoted(line) << " was ignored.\n" ;
    }

    return result ;
}

std::vector<info> parse_file( const std::string& file_name )
{
    std::ifstream file(file_name) ;
    return parse_stream(file) ;
}

void create_a_test_file( std::string file_name ) // used for testing
{
    std::ofstream(file_name) << "[1, 2]-3-4-Hello_World\n"
                                "[5, 6]-7-8-World Hello  world!\n"
                                "[9, 1]-2-3-Welcome_Back\n"
                                "[4, 5]-6-7-World_Bye\n"
                                "[8, 9]-1-2-Bye_World\n" ;
}

int main()
{
    const std::string file_name = "HelloWorld.txt" ;
    create_a_test_file(file_name) ;

    // range based loop: http://www.stroustrup.com/C++11FAQ.html#for
    for( const info& inf : parse_file(file_name) )
    {
        std::cout << "numbers: " ;
        for( int n : inf.numbers ) std::cout << n << ' ' ;
        std::cout << "    text: " << std::quoted(inf.text) << '\n' ;
    }
}
closed account (9h5X216C)
Hello @jonnin, @Handy Andy,

Thank you so much for your replies! Appreciated!

I'm actually declaring the functions in a Struct in a .h file (e.g. Test.h), and defining those functions from the Struct into a .cpp file (e.g. Test.cpp) and compiling it with another .cpp with the int main() function inside (e.g. TestMainProg.cpp).

In response to @jonnin question and approach:
- I heard that I can use multi-dimensional arrays which allow storing of 2 or more integers? Can I use it in this case?
- Tried running the codes but encountered errors, most probably due to the compiling.

In response to @Andy questions and approach:
- operator delete[] is a regular function. Its stated that way in my notes so I followed it.
- There's no reason for the _ in the string and yes can indeed use a space, but I am indeed stuck with the layout of the input file.
- I did not learn about "chrono" and "thread", so I'm not exactly sure what 'std::this_thread::sleep_for(std::chrono::seconds(2));' does? Could you explain a bit more in regards to this? Thank you!


I'm actually learning how to use arrays as I want to create a simple grid map using array.

Like below illustration as an EXAMPLE: (values taken from a .text file)

1
2
3
4
5
6
7
8
  # # # # # #
5 #  1      #
4 #         #
3 #     1   #
2 #         #
1 #         #
0 # # # # # #
  0 1 2 3 4 5


Once again, thank you so much!
Last edited on
Hello justagreenie,

Sorry for the delay. It is not easy for me to sit at the computer and type.

- operator delete[] is a regular function. Its stated that way in my notes so I followed it.

I understand the operator "delete" my point was more of where it is used. Overlooking what is between "new" and "delete" the while loop would look like this:
1
2
3
4
5
6
while (getline(file, line, '-'))
{
	int** a = new int*[abc.size()];

	delete[] a;
}

As you can see you create memory on the heap and then delete it just to do it again for the next read until the while loop fails. You are using "delete" , but in the wrong place. The "delete" statement should be out side the while loop somewhere else.

[quote- There's no reason for the _ in the string and yes can indeed use a space, but I am indeed stuck with the layout of the input file.][/quote]
I figured that. You could use something like what JLBorges has done using "regex" from the regex header file or there is a member function of "std::string" that will replace the "-" with a space.

- I did not learn about "chrono" and "thread", so I'm not exactly sure what 'std::this_thread::sleep_for(std::chrono::seconds(2));' does? Could you explain a bit more in regards to this?

One of the nice things about C++ is that you do not have to understand everything about how something works to use it. Just how to use it. The only part of that statement to worry about is "seconds" and the number in the ()s. As you would think "seconds" is the number of seconds for the pause and the number in the ()s is the actual amount of time in whole seconds. For added flexibility "seconds" can be changed to "milliseconds" making one second equal to 1000 so 1500 would be 1.5 seconds and anything less than 1000 would be less than a second.

If you are going to number your output This would be better:
  1 2 3 4 5 6
1 # # # # # #
2 # 1       #
4 #         #
3 #         #
3 #         #
4 #     1   #
5 #         #
6 # # # # # #
As most people think of rows and columns starting at 1 not zero. After that the program needs to adjust the row and column numbers to be one less to work with the array. And the second part is that the number 1 inside your grid needs to line up with the "#" of the column. Putting the "1" between the "#"s would imply that the number of columns in the array is larger than what you are implying to account for every place on the display, i.e., the spaces.

Without seeing all of your code I would consider something like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
// Other header files

constexpr size_t MAXROW{ 5 };
constexpr size_t MAXCOL{ 5 };

int main()
{
	int grid[MAXROW][MAXCOL]{};

	// pass "grid" to the functions that need it.

	return 0;
}

Think of "size_t" as another name for "unsigned int". Not the best way to explain it, but the simplest. And since the subscripts of an array need to be positive numbers it helps.

I like the idea of breaking up the program into separate files. For now I have not seen what you have done. If you need to use pointers than I need to see all of what you have done so I can focus on what to do.

Hope that helps,

Andy
Hello justagreenie,

I worked on this yesterday and finished it up today. This may not all be useful to you, but parts and the concept are worth looking at. JLBorges's use of "regex" may make some of this easier or not.

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

struct Info
{
	int num1{};
	int num2{};
	int num3{};
	int num4{};
	std::string sentence;
};

int main()
{

	Info temp;
	std::vector<Info> info;
	std::string line;
	std::string::size_type found;  // <--- Used to find the "_" in the string.

	//const std::string PATH{ "C:/..." };  // <--- If you need to use a path to the file.
	std::string iFileName{ "InputFile.txt" };

	std::ifstream inFile;

	inFile.open(iFileName);  //<--- Could be written as: inFile.open(PATH + iFileName);

	if (inFile.is_open())  // <--- Checks to see if the stream is working. Can be simplified.
	{
		std::cout << "\n File " << iFileName << " is open" << std::endl;
		std::this_thread::sleep_for(std::chrono::seconds(0));  // <--- Needs header files chrono" and "thread".
		// <---  Comment out above line or change 2 to zero when you see it is working. Cuts down on the wait time every time the program runs.
	}
	else
	{
		std::cout << "\n File " << iFileName << " did not open" << std::endl;
		std::this_thread::sleep_for(std::chrono::seconds(5));  // <--- Needs header files chrono" and "thread".
		exit(1);  // <--- Because there is no need to continue.
	}

	while (std::getline(inFile, line, '-'))
	{
		temp.num1 = std::stoi(line.substr(1, 1));
		temp.num2 = std::stoi(line.substr(4, 1));
		std::getline(inFile, line, '-');
		temp.num3 = std::stoi(line);
		std::getline(inFile, line, '-');
		temp.num4 = std::stoi(line);
		std::getline(inFile, line);
		temp.sentence = line;
		found = temp.sentence.find("_", 0);
		temp.sentence.replace(found, 1, " ");
		info.emplace_back(temp);
	}

		std::cout << std::endl;  // <--- Originally used for testing. Now used as a blank line before the for loop.

		for (auto item : info)
		{
			std::cout << ' ' << item.num1 << ' ' << item.num2 << ' ' << item.num3 << ' ' << item.num4 << ' ' << item.sentence << std::endl;
		}

	// 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;
}


Hope this helps,

Andy
@justagreenie , you still didn't properly explain the file format --> what does each token stand for? If you have full control over this format, I'd suggest you change it so that every token is space-separated, since by default various streams separate on whitespace.

suppose you had the format
rows columns
row column value
row column value
...

actual file example of this
8 10
1 2 x
2 0 y


From this information, you could construct an 8 rows x 10 columns 2D array, and then populate (1, 2) with "x" and (2, 0) with "y".

In fact, this is an example output from my possible implementation of borders with '#' from your previous thread http://www.cplusplus.com/forum/beginner/240277/
Topic archived. No new replies allowed.