Hello. I recently finished a name generator that reads, depending on the passed to the script (-f or -m), a txt file with a list of names. It'll then generate a name (first and last) for a fictional male or female character.
I've included the code below and would love some feedback on where I can improve.
Things I learned:
* opening and reading data from a text file and storing the value in arrays.
I do this constantly in other languages, such as Go, and I was happy with the result. I needed to include a hack or two to eliminate a '\n' character that fgets puts in.
The code is below. I'd love feedback and how I can improve it.
If you want to run it, I can provide the text files I used. Otherwise, just make "males.txt", "females.txt" and "surnames.txt" in a directory called "names." In each file, just one name on each line.
Code:
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#define MAXLINES 100000
#define NAMESIZE 200
struct Person {
char* first;
char* last;
};
// utility functions
void print_usage()
{
puts("Usage: namege <-hmf>");
}
void print_error()
{
puts("Error: Enter a male (-m) or female (-f) flag");
}
void print_names(char* names[])
{
int i;
for (i = 0; i < MAXLINES; i++) {
if (names[i] != NULL) {
printf("%s", names[i]);
}
}
return;
}
int get_random_index(char* names[])
{
int i = 0, n, min, max;
while (names[++i] != NULL) {
;
}
max = i - 1;
min = 0;
n = (rand() % (max + 1 - min)) + min;
return n;
}
char** get_names(char* filename)
{
int count;
char** names;
char buffer[BUFSIZ];
FILE* fp;
// allocate N items and multiply by chars
names = malloc(MAXLINES * sizeof(char*));
fp = fopen(filename, "rb");
if (fp == 0) {
fprintf(stderr, "Failed to open file");
exit(1);
}
count = 0;
while (fgets(buffer, MAXLINES, fp)) {
// skip blank new lines
if (buffer[0] == '\n') {
continue;
}
/* replace new line with null terminator */
if (buffer[strlen(buffer) - 1] == '\n') {
buffer[strlen(buffer) - 1] = '\0';
}
// allocate space for each item
names[count] = malloc(10 * sizeof(char*));
// then copy it;
strcpy(names[count], buffer);
// increment
count++;
}
fclose(fp);
return names;
}
struct Person get_random_name(char* filename, struct Person* p, char* s_filename)
{
char **names, **surnames;
int r_first, r_last;
// get list of names
names = get_names(filename);
surnames = get_names(s_filename);
// get random first and last random index
r_first = get_random_index(names);
r_last = get_random_index(surnames);
// assign first and last to Person
p->first = names[r_first];
p->last = surnames[r_last];
return *p;
}
void show_name(struct Person* p)
{
char name[NAMESIZE];
sprintf(name, "%s %s", p->first, p->last);
printf("%s\n", name);
}
int main(int argc, char** argv)
{
// set random seed
srand(time(NULL));
int opt;
char* male_name_file = "./names/males.txt";
char* female_name_file = "./names/females.txt";
char* surnames_file = "./names/surnames.txt";
struct Person person, *p;
p = &person;
if (argc == 1 || isalpha(*argv[1]) != 0 || isdigit(*argv[1]) != 0) {
print_error();
exit(EXIT_FAILURE);
}
while ((opt = getopt(argc, argv, "hmf")) != -1) {
switch (opt) {
case 'h':
print_usage();
exit(EXIT_SUCCESS);
break;
case 'm':
*p = get_random_name(male_name_file, p, surnames_file);
break;
case 'f':
*p = get_random_name(female_name_file, p, surnames_file);
break;
case '?':
print_error();
exit(EXIT_FAILURE);
break;
}
}
show_name(p);
return 0;
}