3

I am trying to read inputs from a file that contain strings such as "Jane 30", "Chris 40", and so on, line by line. I then need to store each name with it's corresponding number in the same index of different arrays, so "Jane" in one array with index 0, and 30 in an integer array with index 0.

This is the code I have so far, but I am struggling to figure out how to extract the integers into a separate array, and the characters into another. Please help.

#include stdio.h
#include stdlib.h
#include DarrensInfo.

int main()
{
FILE * coinfile;
coinfile = fopen("coins.txt", "r");
char names[150];
int change[50];
int x, y;

while(!feof(coinfile)){

    fgets(names, 150, coinfile);
    y = 0;

    for(x=0; names[x]; x++){

        if(names[x] <= '0' && names[x] <= '9'){

            change[y] = names[x];
            y++;

        }
    }

}

fclose(coinfile);

return 0;
}
Jabberwocky
  • 48,281
  • 17
  • 65
  • 115
thedafferg
  • 99
  • 7
  • 4
    unrelated: [Why is “while ( !feof (file) )” always wrong?](https://stackoverflow.com/questions/5431941/why-is-while-feof-file-always-wrong) – pmg Mar 04 '20 at 11:56
  • Take a look at the sscanf function to extract strings and numbers from the read lines. Beside of that... doing the own homework by yourself is allways a good idea – mbieren Mar 04 '20 at 12:40

4 Answers4

2
#define COINS_MAX_LINES 150
#define MAX_LINE_LENGTH 100  
#define MAX_NAME_LENGTH 50
int main()
{
    FILE * coinfile;
    coinfile = fopen("coins.txt", "r");
    char line[MAX_LINE_LENGTH];
    char names[COINS_MAX_LINES][MAX_NAME_LENGTH];
    int change[COINS_MAX_LINES];
    int lineno = 0;
    int i = 0;
    while(fgets(line, MAX_LINE_LENGTH, coinfile))
    {
        sscanf(line, "%s %d", names[lineno], &change[lineno]);
        ++lineno;
    }

    fclose(coinfile);

    for (i = 0; i<lineno;++i)
        printf("Name = %s Change = %d\n", names[i], change[i]);

    return 0;

}

After the end of the while loop, names array & change arrays will contain what you want. I have printed it out in the second loop

user93353
  • 13,733
  • 8
  • 60
  • 122
  • 2
    The length of the `change` array should be `COINS_MAX_LINES`, and it's a good idea to check the return value from `scanf` (even in examples). – Hasturkun Mar 04 '20 at 12:40
  • `sscanf(line, "%s ...` without a width limit (and MAX_LINE_LENGTH > MAX_NAME_LENGTH) risks same troubles as `gets()`. Better code avoids buffer overflows. – chux - Reinstate Monica Mar 04 '20 at 15:15
0
  1. You need a array of character arrays. Define the variable names as names[150][30], assuming the length of each name will not exceed more than 30 characters.
  2. Use a combination of fgets and fscanf to parse the line of entry in the file into separate variables as you desired
#include <stdio.h>
#include <stdlib.h>

int main()
{
  FILE * coinfile;
  coinfile = fopen("coins.txt", "r");
  char names[150][30];
  int change[150];
  int x = 0, y, i;

  char buf[100];
  while(!feof(coinfile)){
    if(fgets(buf, 100, coinfile) != NULL) { 
      sscanf(buf, "%s %d", names[x], &change[x]);
      x++;
    }
  }
  fclose(coinfile);

  puts("-------");
  for (int i = 0; i < x; i++)
    printf("%s %d\n", names[i], change[i]);

  return 0;
}
lifebalance
  • 1,846
  • 3
  • 25
  • 57
0

If you're trying to parse names that might look like "King Charles 3", it will be difficult to use scanf. It's not actually that difficult to do this sort of thing without using static sized buffers, and that's a good practice to get used to. You'll want to avoid fgets, since that is difficult to use without a fixed size. Note that growing arrays is notoriously difficult, so I make no claims as to the correctness of the following. One has to do this sort of exercise every few months to be reminded why we don't do this sort of thing in C:

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

FILE * Fopen(const char *, const char *);
void * Realloc(void *, size_t);

/* Remove trailing whitespace */
void trim_white(char *b, char *end)
{
        while( end > b && isspace(*--end)) {
                *end = '\0';
        }
}

int
main(int argc, char **argv)
{
        char *path = argc > 1 ? argv[1] : "stdin";
        FILE *coinfile = argc > 1 ? Fopen(path, "r") : stdin;
        size_t asize = 16;
        size_t bsize = 0;
        char *buf = NULL;
        char **names = Realloc(NULL, asize * sizeof *names);
        long *change = Realloc(NULL, asize * sizeof *change);
        unsigned line_number = 0;
        ssize_t char_read;
        while( buf = NULL, (char_read = getline(&buf, &bsize, coinfile)) != -1) {
                char *space;
                char *end;
                trim_white(buf, buf + char_read);
                space = strrchr(buf, ' ');
                if(space == NULL) {
                        fprintf(stderr, "Invalid input on line %d (no space)\n", line_number + 1);
                        exit(EXIT_FAILURE);
                }
                change[line_number] = strtol(space + 1, &end, 10);
                if(*end != '\0') {
                        fprintf(stderr, "Invalid input on line %d at '%s'\n", line_number + 1, space + 1);
                        exit(EXIT_FAILURE);
                }
                *space = '\0';
                names[line_number] = buf;

                if(++line_number == asize) {
                        asize *= 2;
                        names = Realloc(names, asize * sizeof *names);
                        change = Realloc(change, asize * sizeof *change);
                }
        }

        return EXIT_SUCCESS;
}

FILE *
Fopen(const char *path, const char *mode) {
        FILE *fp = fopen(path, mode);
        if( fp == NULL ) { perror(path); exit(EXIT_FAILURE); }
        return fp;
}

void *
Realloc(void *buf, size_t s)
{
        buf = realloc( buf, s );
        if( buf == NULL) { perror("realloc"); exit(EXIT_FAILURE); }
        return buf;
}
William Pursell
  • 204,365
  • 48
  • 270
  • 300
  • "You'll want to avoid fgets" vs. `getline()`. A weakness to `getline()` is that is allows a nefarious user to overwhelm memory resources by forming an excessively long line of input. `fgets()` does provide an upper bound. Each solution has its pros/cons. (Aside: suggest %u with unsigned) – chux - Reinstate Monica Mar 04 '20 at 15:25
0

I am trying to read inputs from a file that contain strings such as "Jane 30", "Chris 40", and so on, line by line

You're reading a file that might contain strings such as "Jane 30", "Chris 40", and so on; but might also contain millions of typing mistakes and/or other errors; and therefore you need to detect errors and clearly inform the user what the error is so that they can easily understand the problem, then find the mistake, then fix it.

For this reason none of the C library functions are ever useful.

Instead build a parser as a finite state machine. For example (untested):

    // State

    int state = 0;
    int column = 0;
    int line = 1;
    char current_name[MAX_NAME_LENGTH];
    int name_length;
    int number;

    // Main loop

    for(;;) {
        int c = fgetc(file);
        column++;
        switch(state) {

        case 0:  /* At start of new line */
            if(c == FEOF) {
                return OK;
            } else if(isdigit(c)) {
                printf("ERROR: Number found at start of line (missing name), on line %d at column %d\n", line, column);
                return NOT_OK;
            } else if(isalpha(c)) {
                name_length = 0;
                current_name[name_length++] = c;
                state = 1;
            } else if(c == '\n') {
                line++
            } else if(isspace(c)) {
            } else {
                printf("ERROR: Bad character at start of line, on line %d at column %d\n", line, column);
                return NOT_OK;
            }
            break;

        case 1:  /* In middle of name */
            if(c == FEOF) {
                printf("ERROR: File ends in the middle of a name, on line %d at column %d\n", line, column);
                return NOT_OK;
            } else if(isdigit(c)) {
                printf("ERROR: No whitespace between name and number, on line %d at column %d\n", line, column);
                return NOT_OK;
            } else if(isalpha(c)) {
                if(name_length >= MAX_NAME_LENGTH) {
                    printf("ERROR: Name too long (max length is %d), on line %d at column %d\n", MAX_NAME_LENGTH, line, column);
                    return NOT_OK;
                }
                current_name[name_length++] = c;
            } else if(c == '\n') {
                printf("ERROR: No number after name, on line %d at column %d\n", line, column);
                return NOT_OK;
            } else if(isspace(c)) {
                state = 2;
            } else {
                printf("ERROR: Bad character in middle of name, on line %d at column %d\n", line, column);
                return NOT_OK;
            }
            break;

        case 2:  /* Between name and number */
            if(c == FEOF) {
                printf("ERROR: File ends after name, on line %d at column %d\n", line, column);
                return NOT_OK;
            } else if(isdigit(c)) {
                number = c - '0';
                state = 3;
            } else if(c == '\n') {
                printf("ERROR: No number after name, on line %d at column %d\n", line, column);
                return NOT_OK;
            } else if(isspace(c)) {
            } else {
                printf("ERROR: Bad character after name, on line %d at column %d\n", line, column);
                return NOT_OK;
            }
            break;

        case 4:  /* In middle of number */
            if(c == FEOF) {
                printf("ERROR: File ends in middle of number, on line %d at column %d\n", line, column);
                return NOT_OK;
            } else if(isdigit(c)) {
                if(number > INT_MAX / 10) {
                    printf("ERROR: Number is too large, on line %d at column %d\n", line, column);
                    return NOT_OK;
                }
                number *= 10;
                if(number > INT_MAX - (c - '0') ) {
                    printf("ERROR: Number is too large, on line %d at column %d\n", line, column);
                    return NOT_OK;
                }
                number += c - '0';
            } else if(c == '\n') {
                create_new_entry(current_name, name_length, number);
                line++
                state = 0;
            } else if(isspace(c)) {
                state = 5;
            } else {
                printf("ERROR: Bad character after number, on line %d at column %d\n", line, column);
                return NOT_OK;
            }
            break;

        case 5:  /* Trailing white space before end of line */
            if(c == FEOF) {
                printf("ERROR: File ends between number and end of line, on line %d at column %d\n", line, column);
                return NOT_OK;
            } else if(c == '\n') {
                line++
                create_new_entry(current_name, name_length, number);
                state = 0;
            } else if(isspace(c)) {
            } else {
                printf("ERROR: Unknown characters between number and end of line, on line %d at column %d\n", line, column);
                return NOT_OK;
            }

        }
    }
Brendan
  • 35,656
  • 2
  • 39
  • 66