random CSV file generator

I'm working on a project that will generate a random CSV file given some specifications (in main()) however it's spitting out garbage and I can't figure out why...

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
  #include <iostream>
#include <cstring>
#include <sstream>

using namespace std;

static const char alpha[] = "abcdefghijklmnopqrstuvwxyz";
static const char num[] = "1234567890";

struct LineItem {
    char item[100];

    char genRandomAlpha() {
        return alpha[rand() % 25];
    }
    char genRandomNum() {
        return num[rand() % 9];
    }

    void addStringItem() {
        for (unsigned int i = 0; i < 20; ++i) {
        char letter = genRandomAlpha();
            strcat(item, &letter);

        }
    }

    void addIntItem() {
        for (unsigned int i = 0; i < 20; ++i) {
            char number = genRandomNum();
            strcat(item, &number);

        }
    }

    void addDoubleItem() {
        char doubleVal[20];
        double fMin = 0.0, fMax = 1000.0;
        double f = (double) rand() / RAND_MAX;
        f = fMin + f * (fMax - fMin);

        stringstream converter;
        converter << f;
        converter >> doubleVal;
        strcat(item, doubleVal);

    }

};

int main() {

    int size = 1000;
    LineItem *list = new LineItem[size];
    char comma = ',';

    for (int i = 0; i < size; i++) {
        list[i].addIntItem();
        strcat(list[i].item, &comma);
        list[i].addStringItem();
        strcat(list[i].item, &comma);
        list[i].addDoubleItem();
        strcat(list[i].item, &comma);
        list[i].addDoubleItem();
        strcat(list[i].item, &comma);
    }

    for (int i = 0; i < size; i++) {
        for (int j = 0; list[i].item[j] != '\0'; i++) {
            cout << list[j].item[i];
        }
        cout << endl;
    }
    delete[] list;


    return 0;
}
Hello ToeMater,

After some work I finally did manage to get the program to compile. Now every time I try to run the program I get run time errors that stop the program.

Are you trying to write a C program or a C++ program?

Are you stuck using C style character arrays the a std::string would do a better jub with?

As a full C++ program this would work a lot better.

Hint/Tip: instead of int size = 1000; it would work better as: constexpr int SIZE{ 1000 },, "condstexpr" is from C++11 on, then in the next line it would be: LineItem *list = new LineItem[SIZE];. The capital letters help to remind you that it is defined as a onstant and can not be changed. The fact that it is a constant means that you can not accidentally change it value in the program.

I can not get to the second for loop to see what it is doing.

I am starting to think that the problem is in the "strcat"s in the first for loop. for now do not count on "strcat" to add the null terminator to the end of the string. You may have to do that yourself.

I will keep trying to figure out where the problem is.

Hope that helps,

Andy
To answer your first question, C++, but I was using cstrings out of habit and experience with them,

I don't know why you had such trouble getting it to compile, but I guess not compiling is better than trash results.

Thank you for the tip and all the help, good luck on the problem.
Hello ToeMater,

My VS IDE is set up for C++11 standards and I have a lot of problems with old C code. It took a little work, but I changed it over to a full C++ program and it works fine. In what is line 65 of your code you do not need to put a comma at the end of the line. A new line character will do.

When it was finally working this is part of the output from the program:

976 87136523195131958479,iuaqojspdvmaolppcift,927.274392,103.061007
977 84911621517457415726,nxpsdquldaxggaupxcit,86.153752,595.233009
978 65853342447715661595,pwklfdctcngplonpttan,893.276772,646.046327
979 82456188886418258329,wqoxqoxxuvmkuqiixyws,871.517075,892.483291
980 16885671976875472289,rqichmcyfhtdduxyhcop,670.857875,205.786309
981 27651124556497316628,aevesvggkqwkunawjomq,818.964202,694.296091
982 49516525915959674377,wrrlmfhidmtftvamwmve,20.752586,213.629566
983 67115239585734354216,oixaaupamybcrwpdbjdq,525.955992,81.820124
984 48248753163321541165,uqdqddpmrjvxplbyvkep,356.547746,868.648335
985 73959534994513483538,ntkfvcwabyyjqksqjohb,979.338969,757.316813
986 48394112586447829545,dxkcaoflddsvuyelssxk,938.810389,180.669576
987 13621422287554627664,ieixirybxoaxidiockdl,664.143803,342.112491
988 86293448626828175584,xmctryqggswxvbihwhxg,750.541704,968.718528
989 56641978371745961445,kseuunoawcydjmhwwvkv,120.120853,332.743309
990 21386335385692668398,ixegkywdbtbsaxpxvrmf,210.791345,345.225379
991 47583959921586543742,sjqtrqjekblejfakekan,351.420637,496.688742
992 59837753185112177436,yvivvblkudacfwstnkus,382.335887,718.253121
993 94221567585962822188,ltilsmvrugphxqewotch,996.093631,988.616596
994 62169521711693983173,ivtqehkhhskegaxgfbvx,829.523606,842.371899
995 55122938561351998176,odwejxlvhtxvxftockvk,661.641285,968.657491
996 47622669459285832656,wamnduegepklesfvqbvw,783.104953,949.674978
997 66268912748294733821,yttyaelmtgapqalpdcqw,333.079012,3.997925
998 63251821268884467519,ijecleghqvyjatofllvs,767.754143,491.927854
999 41854749189467974671,hetxyvseysexhnkpxxxr,259.041108,34.730064


The first three numbers and space I added just to have a line number to see what was happening.

If you are more familiar with the C code and you do not have any problems compiling it then by all means use it. You might have to use "printf" for your output, but I believe that "iostream" should handle the output with no problem.

Just had a thought. In the "addDoubleItem" function you ar trying to use "stringstream" to convert a number into a C style character array. This may work, but it may not put the terminating null character at the end of the string. Then in the "strcat" "doubleVal" has no idea when to stop and just continues until the boundary of some array is reached and the program crashes. I will have to look into that more.

Hope that helps,

Andy
Hello ToeMater,

I meant to mention lines 7 and 8 of your original code so not need "static" these are global variables the are there for the life of the program. Using "static" is just redundant. The "const' is still needed because they are global variables.

Andy
Hello ToeMater,

I did manage to fix your original code and make it work. I started with a visit to https://en.cppreference.com and then to the page https://en.cppreference.com/w/c/string/byte/strcat

After reading about "strcat" and seeing what it need I realized that the destination variable and the source variable were not null terminated (\0) and that was causing the problem.

This is what I did to fix the program:

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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#include <iostream>
#include <iomanip>
#include <string>
#include <cstring>
#include <ctime>
#include <sstream>

//#pragma warning(disable : 4996) // <--- Something I needed to make some of the old C code work.

//using namespace std;  // <--- Best not to use.

const char alpha[] = "abcdefghijklmnopqrstuvwxyz";
const char num[] = "1234567890";

const size_t MAXSIZE = 100;  // <--- Better to do it this way so there is only one place to make changes.
                             // Also easier to use in the program and better to understand what it is.
const int MAXWIDTH = 3;  // <--- Size of setw for 3 digit number. Increase by 1 for every digit above 3.

struct LineItem
{
	char item[MAXSIZE];

	char genRandomAlpha()
        {
		return alpha[rand() % 25];
        }

	char genRandomNum()
        {
		return static_cast<char> (num[rand() % 9]);
        }

	void addStringItem()
	{
		for (unsigned int i = 0; i < 20; ++i)
		{
			char letter[2]; // <--- Needs to be this way so it can be null terminated to work with "strcat()".
			letter[0] = genRandomAlpha();
			letter[1] = '\0';
			strcat(item, letter);
		}
	}

	void addIntItem()
	{
		for (unsigned int i = 0; i < 20; ++i)
		{
			char number[2]; // <--- Needs to be this way so it can be null terminated to work with "strcat()".
			number[0] = genRandomNum();
			number[1] = '\0';
			strcat(item, number);
			//std::cout << std::endl;
		}
	}

	void addDoubleItem()
	{
		char doubleVal[20];
		std::string sDoubleVal;
		double fMin = 0.0, fMax = 1000.0;
		double f = (double)rand() / RAND_MAX;
		f = fMin + f * (fMax - fMin);

		
		// <--- Using the "stringstream" work well with a "std::string", but not so well with a C string character
		// array.
		// <--- The "sDoubleVal" works with the "stringstream", but needs the string member function "c_str()" to
		// change the "std::string" into a C string to fit into the C character array.
		std::stringstream converter;
		converter << f;
		converter >> sDoubleVal;
		strcat(item, sDoubleVal.c_str());
		//std::cout << std::endl;
	}
};

int main()
{
	const int SIZE = 1000;

	LineItem *list = new LineItem[SIZE];
	char comma[3] = ",\0";  //<--- Needed this way for the "strcatt" function.

	srand(static_cast<size_t>(time(NULL)));  // <--- Needed here to seed the RNG. Only needs done once.Requires the
	                                         // "ctime" header for "time".
	
	for (int i = 0; i < SIZE; i++) 
	{
		list[i].item[0] = '\0';  // <--- Once you have the right "list" from the array you need to set item element
		                         // zero to null (\0) for the "strcat"s to work.

		list[i].addIntItem();
		strcat(list[i].item, comma);
		list[i].addStringItem();
		strcat(list[i].item, comma);
		list[i].addDoubleItem();
		strcat(list[i].item, comma);
		list[i].addDoubleItem();
		//strcat(list[i].item, comma);  // <--- Do not need a comma at the end of the line. Just a new line (\n).
	}

	for (int i = 0; i < SIZE; i++)  // <--- Only need one for loop for this.
		std::cout << std::setw(MAXWIDTH) << i << ". " << list[i].item << std::endl;

	delete[] list;

	// <--- Used mostly for testing in Debug mode. Removed if compiled for release.
	// <--- Used to keep the console window open in Visual Studio Debug mode.
	// The next line may not be needid. 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;
}

I think the comments in the program should explain every thing. If not let me know.

And if you are curious this is the C++ version:
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
84
85
86
87
88
#include <iostream>
#include <string>
#include <ctime>
#include <sstream>

#pragma warning(disable : 4996)

//using namespace std;

const std::string alpha = "abcdefghijklmnopqrstuvwxyz";
const std::string num = "1234567890";

struct LineItem
{
	std::string item;

	char genRandomAlpha()
		return alpha[rand() % 25];

	char genRandomNum()
		return static_cast<char> (num[rand() % 9]);

	void addStringItem()
	{
		for (unsigned int i = 0; i < 20; ++i)
		{
			char letter = genRandomAlpha();
			item += letter;
		}
	}

	void addIntItem()
	{
		for (unsigned int i = 0; i < 20; ++i)
		{
			char number = genRandomNum();
			item += number;
		}
	}

	void addDoubleItem()
	{
		std::string doubleVal;
		double fMin = 0.0, fMax = 1000.0;
		double f = (double)rand() / RAND_MAX;
		f = fMin + f * (fMax - fMin);

		doubleVal = std::to_string(f);

		//std::stringstream converter(f);
		////converter << f;
		//converter >> doubleVal;
		item += doubleVal;

	}

};

int main()
{
	constexpr int SIZE = 1000;
	LineItem list[SIZE];
	char comma = ',';

	srand(static_cast<size_t>(time(NULL)));

	for (int i = 0; i < SIZE; i++)
	{
		list[i].addIntItem();
		list[i].item += comma;
		list[i].addStringItem();
		list[i].item += comma;
		list[i].addDoubleItem();
		list[i].item += comma;
		list[i].addDoubleItem();
		//list[i].item += comma;
	}

	for (int i = 0; i < SIZE; i++)
		std::cout << i << ' ' << list[i].item << std::endl;

	// <--- Used to keep the console window open in Visual Studio Debug mode.
	// The next line may not be needid. 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;
}

You can see that the C++ version is much easier to write and use. Just a hint of what you could do.

Hope that helps,

Andy
Style Question: Why didn't you just use namespace std? I don't think you use any other namespace?
Handy Andy wrote:
I meant to mention lines 7 and 8 of your original code so not need "static" these are global variables the are there for the life of the program. Using "static" is just redundant.

You don't seem to understand the use of static here. In this context, it restricts the scope of these variables to this file only; it means they are not global variables.

The "const' is still needed because they are global variables.

What? Whether or not a variable is const, has nothing to do with whether or not they're global. You can have const global variables, and non-const global variables. What are you trying to say here?



Last edited on
@MikeBoy,

For the first part my understanding of static was not complete. I have always used "static" in a totally different context. Thank you for the input. Now I will have to go read up on the use of static and see what I can learn.

For the second part it was late and I was not thinking clearly. I should have put it something more like: It is best to not use global variables, but a "const" global variable is slightly better because you can not accidentally change the variable in the program since the whole file has access to the variable. If possible it is better to put these "const" variables in "main" or the function that needs it.

I hope this works better. I see now I will have to work on it until I get it right then I can just copy and paste it when needed.

Any input you have will be helpful. You can send me a PM if you want.

Andy
Somewhat off-topic, but in C++, anonymous namespaces are an alternative to using static functions (and allow for local-only classes as well).
https://stackoverflow.com/questions/154469/unnamed-anonymous-namespaces-vs-static-functions
I have never really used the feature, though, but just thought I'd let people know.
For the first part my understanding of static was not complete. I have always used "static" in a totally different context. Thank you for the input. Now I will have to go read up on the use of static and see what I can learn.

To be fair, it's not something you see done much in C++. Class variables and anonymous namespaces cover most of the use cases for it. And then...

For the second part it was late and I was not thinking clearly. I should have put it something more like: It is best to not use global variables, but a "const" global variable is slightly better because you can not accidentally change the variable in the program since the whole file has access to the variable.


Fun fact: In C++, const "global" variables have internal linkage only. Which means they're not actually global - they have file scope only. In other words, you were right originally (if for the wrong reasons) - the static in the OP's code is redundant, as even without it, those variables still have file scope.

(This means that it's OK to define a const "global" variable in a header file, without needing to worry about multuple definitions. When you do this, you're not redefining the same global variable in multiple translation units; you're actually defining a different, file-scope variable in each translation unit, so no linker errors. If you tried it in C, or woth non-const globals in C++, you'd get linker errors, and you'd have to start using extern declarations instead.)
Last edited on
Topic archived. No new replies allowed.