Function to copy names from file to char * array

As part of my program, I need to copy individual names from a file into an array defined as char *Names[NumOfNames]

, where NumOfNames

is an integer containing the total number of names in the file. I'm new to array pointers and this seems to be my problem.

The file is written in the following format:

JohnFrankJamesPeter

(i.e. every name starts with an uppercase letter and there are no spaces between names)

Here is my unfinished function:

void LoadNamesIntoArray()
{
    char ch;
    int NumOfNames = 0;

    FILE *fpn = fopen(NamesFilePath, "r+");

    if (fpn == NULL)
    {
        printf("Cannot open %s for reading. \n", NamesFilePath);
        printf("Set up Names file at %s and restart. \n", NamesFilePath);
        perror("Error opening Names file");
    }

    do{
        ch = fgetc(fpn);
        if(isupper(ch)){
            NumOfNames++;
        }
    }while(ch != EOF);

    char *Names[NumOfNames];
    ...
    ...
    ...
}

      

I tried several ways to copy each name to each element of the array using a function fgets

and a function islower()

to find out when to go to the next element of the Names array.

I would like the array to be such that

printf("%s", Names[0])

      

will print "John", etc. Is it possible? Any help or help as to where I am going wrong would be greatly appreciated.

EDIT

Now I'm confused trying to copy each name to a temporary array and then to each element char *Names[NumOfNames}

. However, to check if the file was copied to the temporary array correctly, I tried to print it, but it doesn't print correctly. Here's a block of code:

do{
    ch = fgetc(fpn);
    TempName[i] = ch;
    i++;
}while(ch != EOF);

for(i = 0; i<15; i++){
    printf("%c", TempName[i]);
}

      

I know this will print the first 15 characters, not one name, but my problem is that it prints all the weird characters and not the actual letters.

+3


source to share


4 answers


A good first step is finding the number of names.

Add the definition of maximum length to it.

size_t MaxLength = 0;
size_t CurentLength = 0;
while ((ch = fgetc(fpn)) != EOF) {
  if(isupper(ch)){
    CurrentLength = 0; 
    NumOfNames++;
  }
  CurentLength++;
  if (CurrentLength > MaxLength) {
    MaxLength = CurrentLength; 
  }
} 

      

Rewind the file, select buffers.

rewind(fpn);
char *Buffer = malloc(MaxLength + 1);
// +1 here to deal with files that do not begin with  A-Z
char **Names = malloc((NumOfNames + 1) * sizeof *Names);

      

Then repeat reading the names and allocate space for each name using strdup()

. strdup()

is not standard C, but is standard in POSIX and therefore generally available - see below.



size_t i = 0;
size_t name_index = 0;
for(;;)  {
  ch = fgetc(fpn);
  if (ch == EOF || isupper(ch)) {
    buffer[i] = '\0';
    if (i > 0) Names[name_index++] = strdup(buffer);
    if (ch == EOF) break;
    i = 0;
  }
  buffer[i++] = ch;
}

      


[change]

An example strdup()

.

char *strdup(const char *str) {
  size_t len = strlen(str) + 1;
  char *copy = malloc(len);
  if (copy) {
    memcpy(copy, str, len);
  }
  return copy;
}

      

+2


source


If you know that the maximum size of each name is 15, you can do something like this:

char buf[15];
int i = 0;
do {
    if ((ch = fgetc(fpn)) == EOF) break;
    buf[i++] = ch;
    if (isupper(ch)) {
        Names[NumOfNames] = malloc(i * sizeof(char));
        memcpy(Names[NumOfNames]), buf, i -1);
        i = 0;
        NumOfNames++;
    }
} while(ch != EOF);

      



Otherwise, you have to use realloc, not buf [15].

+2


source


First, these are just strange symbols. The string is the letters of the name and nul byte '\ 0' so as not to be confused with NULL. This means that the name John requires 5 char positions [0.3] for letters and "\ 0" for 4.

By no means are you stamping the end of the array with a nul byte.

The best way to accomplish this whole task is to measure the file, create an array, rewind the file, and then process it by putting the letters in the array, sealing "\ 0" at the end of each.

The best way to print the first 15 lines is

printf("%15s",tmpname) ;

      

0


source


Reading each character and checking if it is a cap allows name separation. A quick approach would look something like this:

#include <stdio.h>

#define MAXNM 256
#define MAXCH 32

int main (int argc, char **argv) {

    char names[MAXNM][MAXCH] = {{0}};
    FILE *fp = NULL;
    size_t idx = 0;
    size_t i = 0;
    int c = 0;

    if (argc < 2 ) {
        fprintf (stderr, "error: insufficient input, usage: %s filename\n", argv[0]);
        return 1;
    }

    if (!(fp = fopen (argv[1], "r"))) {
        fprintf (stderr, "error: file open failed '%s'.\n", argv[i]);
        return 1;
    }

    /* read each char in file */
    for (;;) {
        while ((c = getc(fp)) != '\n' && c != EOF)
        {
            if (c >= 'A' && c <= 'Z') {     /* if c is Cap,         */
                if (i) names[idx++][i] = 0; /* null-term & new name */
                i = 0;                      /* reset i = 0          */
                if (idx == MAXNM) break;    /* if MAXNM, break      */
            }
            names[idx][i++] = c;            /* add c to name[idx]   */
            if (i == MAXCH) {               /* if MAXCH             */
                names[idx++][i-1] = 0;      /* null-term/trucate    */
                while ((c = getc(fp)) < 'A' || c > 'Z') /* next Cap */
                    {}
                ungetc (c, fp);             /* put it back          */
                i = 0;
                continue;
            }
        }
        if (c == EOF) {
            names[idx++][i] = 0;            /* null-terminate last  */
            break;
        }
        if (idx >= MAXNM) break;
    }

    fclose (fp);                            /* close input file     */

    for (c = 0; c < idx; c++)               /* output names         */
        printf ("names[%2d] : %s\n", c, names[c]);

    return 0;
}

      

Output

$ ./bin/split_on_caps dat/Capnames.txt
names[ 0] : John
names[ 1] : Frank
names[ 2] : James
names[ 3] : Peter

      

The only thing that is unclear about your question is whether you prefer to allocate dynamically for each line, or if a static declaration is sufficient. You can clearly allocate a specific number of pointers (for example char *names[MAXNM] = calloc (MAXNM, sizeof *names[0]);

), or you can statically allocate the array as above. The only difference is that you have the option to reallocate more name pointers in case you end up.

0


source







All Articles