user3653480
user3653480

Reputation: 1

Read from standard input into a C array

The input is

name number number

like the example

bank 1 10

I need to read input into a matrix n lines by 3 columns like the next example

{ bank, 1, 10,  
  bank2, 2, 15,  
  bank3, 3, 20  }  

My main difficulty is reading the name and storing into the matrix. The name can vary from 1 to 41 characters.

I tried doing it the following way but I don't know a way to ignore the blank spaces in the input and how to make the for loop only count until the word is read.

for (b=0; b<41;b++) {
    scanf("%s ",&nome[i]);
}
scanf("%d %d",&rating,&ref);
n[i][0] = *nome;
n[i][1] = rating;
n[i][2] = ref;

I just started learning programming in C so I can't use advanced things in my code.

Upvotes: 0

Views: 2460

Answers (3)

David C. Rankin
David C. Rankin

Reputation: 84569

Since you are working with a collection of different types of information (e.g. char*, int, int), the proper way to collect this information is in a structure (or struct). You have two choices on how to create space for any array or array of struct, (1) statically allocate on stack, or (2) dynamically allocate on heap. Given your question requirement, static allocation is the most basic. However, it is not as flexible as dynamically allocating the data, and you are limited to the initial size you choose.

Just as with storing the data, you have choices on how you read the data from stdin. As a general proposition, when reading from stdin, the preferred way is to read a line-at-a-time into a buffer and then parse the buffer for the desired content. Just as above, line input is more flexible than squeezing the data into a scanf format string, but is a bit more involved. For purposes here, we will use scanf, but know that line-input with getline or fgets provide certain advantages.

Next, you can just declare the number of structs you need, or you can take the time to initialize all the values in each of the structs. (this has advantages as you will see below). Aside for allowing some iteration tricks, the primary reason you initialize all your variables is to prevent the possibility of reading from them uninitialized. Reading an uninitialized variable results in Undefined Behavior (bad). So take the time to learn how to initialize each of your variable types.

With that said, you can tell there are a number of valid ways to approach any problem. How you do it is up to you, as long as you do it correctly. Here is another approach to meet your requirements. Note that the maximum name length MAXNM and maximum number of lines to read MAXLN are defined at the top of the code. This allows you to easily adjust the values later on. Let me know if you have any questions:

#include <stdio.h>

#define MAXNM 41
#define MAXLN 100

typedef struct mix {
    char name[MAXNM + 1];
    int num1;
    int num2;
} mix;

int main (void) {

    /* initialize array of structs & variables */
    mix array[MAXLN] = {{ {0}, 0, 0 }};
    size_t i = 0;
    size_t read = 0;

    /* read array of struct contents from stdin */
    while (scanf ("%s %d %d", array[i].name, &array[i].num1, &array[i].num2) == 3) {
        i++;
        /* check if lines > MAXLN allowed */
        if (i >= MAXLN) {
            fprintf (stderr, "warning: lines read from stdin exceed MAXLN.\n");
            break;
        }
    }

    /* set the number of elements read to i */
    read = i;

    /* iterate over elements using 'read' */
    printf ("\nIterating array using 'while (i < read)'\n\n");
    i = 0;
    while (i < read) {
        printf (" array[%zu]  %-41s  %4d  %4d\n", i, array[i].name, array[i].num1, array[i].num2);
        i++;
    }

    /* iterate over array by virtue of initization of name to 0/null */
    i = 0;
    printf ("\nIterating array using 'while (array[i].name[0])'\n\n");
    while (array[i].name[0]) {
        printf (" array[%zu]  %-41s  %4d  %4d\n", i, array[i].name, array[i].num1, array[i].num2);
        i++;
    }

    printf ("\n");

    return 0;
}

Input

$ cat dat/staticstruct.txt
TheNamesofVaryingWidth 123 456
SomeOtherName 234 567
Bank_1 12 34
Bank_2 23 45
Bank_3 34 56
OneLastNameThatHasCloseToTheMaximumChars 777 9999

Output

$ ./bin/ptrarraystatic < dat/staticstruct.txt

Iterating array using 'while (i < read)'

 array[0]  TheNamesofVaryingWidth                      123   456
 array[1]  SomeOtherName                               234   567
 array[2]  Bank_1                                       12    34
 array[3]  Bank_2                                       23    45
 array[4]  Bank_3                                       34    56
 array[5]  OneLastNameThatHasCloseToTheMaximumChars    777  9999

Iterating array using 'while (array[i].name[0])'

 array[0]  TheNamesofVaryingWidth                      123   456
 array[1]  SomeOtherName                               234   567
 array[2]  Bank_1                                       12    34
 array[3]  Bank_2                                       23    45
 array[4]  Bank_3                                       34    56
 array[5]  OneLastNameThatHasCloseToTheMaximumChars    777  9999

Upvotes: 0

Weather Vane
Weather Vane

Reputation: 34585

In this example I have interpreted your "matrix" requirement, such that each line of the input fills in a struct having different field types, so we end up with a 1-D array of the struct instead of a 2-D array of differing data types.

And instead of entering the data from the keyboard, which is so boring every time you test the program, I have put your data in a file and read from that - although the technique is quite similar.

Also, because your input format is not consistent, I have skipped any chars that don't form part of the data, see delims.

The program is possibly rather more complicated than you would have liked, but I hope it can help you do what you want. The complexity level rose when I decide not to use a fixed length string in the struct but a pointer to string memory, allocated for a variable length string.

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

typedef struct {
    char *nome;
    int rating;
    int ref;
    } customer;

void fatal(char *msg) {
    printf("%s\n", msg);
    exit (1);
    }

int main()
{
    FILE *fp;                                       // for fopen
    char line[1000];                                // for fgets
    char *delims = ", {}\t\r\n";                    // for strtok
    char *sptr;                                     // for strtok
    customer *mark = NULL;                          // an empty array
    int marks = 0;                                  // number of customers
    int len;                                        // length of input string
    fp = fopen("input.txt", "r");                   // open the file
    if (fp == NULL)                                 // did it open?
        fatal("Cannot open file");
    while (fgets(line, 1000, fp) != NULL) {         // each line of file
        mark = realloc(mark, sizeof(customer) * (marks+1));  // extend
        if (mark == NULL)
            fatal("Cannot extend array");           // failed extend the array

        sptr = strtok(line, delims);                // split first field
        if (sptr == NULL)
            fatal("Cannot get first field");
        len = strlen(sptr);                         // length of bank name
        mark[marks].nome = malloc(len+1);           // memory for the string
        if (mark[marks].nome == NULL)
            fatal("Cannot allocate string array");
        strcpy(mark[marks].nome, sptr);             // copy string to struct

        sptr = strtok(NULL, delims);                // split second field
        if (sptr == NULL)
            fatal("Cannot get second field");
        mark[marks].rating = atoi(sptr);            // extract number

        sptr = strtok(NULL, delims);                // split third field
        if (sptr == NULL)
            fatal("Cannot get third field");
        mark[marks].ref = atoi(sptr);               // extract number

        marks++;                                    // one more record
    }
    fclose (fp);

    // print the results (re-using len for convenience)
    for (len=0; len<marks; len++)
        printf("%s %d %d\n", mark[len].nome, mark[len].rating, mark[len].ref); 

    // release the data array
    for (len=0; len<marks; len++)
        free(mark[len].nome);
    free(mark);

    return 0;
}

Input file:

{ bank, 1, 10,  
  bank2, 2, 15,  
  bank3, 3, 20  }  

Program output:

bank 1 10
bank2 2 15
bank3 3 20

Upvotes: 0

John Bollinger
John Bollinger

Reputation: 180460

You are trying to read a string one character at a time, but using the edit descriptor for reading a whole string. You're trying to make it harder than it needs to be.

Also, the term "matrix" is normally understood to mean a 2-dimensional array of elements all the same type, whereas it sounds like you want a 1-dimensional array of structs containing members of different types. For example:

#define MAX_BANKS 10

struct bank {
    char nome[42];
    int rating;
    int ref;
};

struct bank banks[MAX_BANKS];
int num_banks = 0;

/* ... */

void read_banks(void) {
    while (num_banks < MAX_BANKS) {
        int fields;

        fields = scanf("%41s %d %d", banks[num_banks].nome,
                &banks[num_banks].rating, &banks[num_banks].ref);
        if (fields != 3) {
            /* handle error */
            break;
        } else {
            num_banks += 1;
        }
        /* ... */
    }
}

Upvotes: 1

Related Questions