1

Have to read file.txt with format as mentioned below: 12AA,abc12 \n 4CCC,cde15 \n

I want to read the file line by line and store comma separated values in separate variables.

fp = fopen("file.txt", "r");

while(fgets(buffer, 255, (FILE*) fp)) 
{
    fscanf(fp, "%s %s", acc_no, package);
    printf("%s\n", acc_no);
    printf("%s\n", package);
}

fclose(fp);

I would like not only just read and print the variable but, also, to store them in separate variables. Suggestion on how to do that?

Leos313
  • 5,152
  • 6
  • 40
  • 69
  • strtok may help. – Biswapriyo Nov 17 '18 at 07:12
  • so, if every line is a variable, you can read before the whole file and, after, you can use `malloc` to allocate all the variable in the *heap*. Bring the pointer back to the beginning of the file and perform the operation of storing the string in the variable you have allocated. That's all, nothing more! (Be careful: if you need a value and not a string, you need to convert them using the function of the C library: `strod(...)` is one of them - https://stackoverflow.com/questions/7951019/how-to-convert-string-to-float) – Leos313 Nov 17 '18 at 07:40
  • This [code smells](https://en.wikipedia.org/wiki/Code_smell): `while(fgets(buffer, 255, (FILE*) fp))` **Why** are you casting something that should already be a `FILE *` to a `FILE *`? – Andrew Henle Nov 17 '18 at 13:47

2 Answers2

3

strchr() can help:

while (fgets(str, sizeof str, fp)) {
    char *arr[2], *ptr;

    arr[0] = str;
    if ((ptr = strchr(str, ','))) {
        arr[1] = ptr + 1;
        *ptr = '\0';
    } else {
        exit(EXIT_FAILURE);
    }
    printf("<%s> <%s>\n", arr[0], arr[1]);
}

Notice that you may need to strip the trailing newline lefted by fgets() or ommit the \n in printf

If you need to store those strings in fresh memory, then there is a bit more of work, you can realloc() or use a linked list (always prefer linked lists when you don't know the number of lines before-hand):

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

struct node {
    void *data;
    struct node *next;
};

/* Returns a pointer to an allocated string */
extern char *strdup(const char *);

/* Insert a node and returns a pointer to the data */
static void *enqueue(struct node **root, void *data)
{
    struct node *node;

    if (root == NULL) {
        return NULL;
    }
    node = malloc(sizeof *node);
    if (node == NULL) {
        return NULL;
    }
    if (*root == NULL) {
        node->next = node;
    } else {
        node->next = (*root)->next;
        (*root)->next = node;
    }
    node->data = data;
    *root = node;
    return data;
}

/* Delete a node and returns a pointer to the data */
static void *dequeue(struct node **root)
{
    struct node *node;
    void *data = NULL;

    if (root == NULL) {
        return NULL;
    }
    node = *root;
    if (node != NULL) {
        node = node->next;
        data = node->data;
        if (*root == node) {
            *root = NULL;
        } else {
            (*root)->next = node->next;
        }
        free(node);
    }
    return data;
}

int main(void)
{
    struct node *head = NULL;
    char str[256];
    char **arr;
    char *ptr;

    /* While we don't hit EOF */
    while (fgets(str, sizeof str, stdin)) {
        /* Reserve space for 2 pointers */
        arr = malloc(sizeof(*arr) * 2);
        if (arr == NULL) {
            perror("malloc");
            exit(EXIT_FAILURE);
        }
        /* If we find a comma */
        if ((ptr = strchr(str, ','))) {
            /* Store the second string */
            arr[1] = strdup(ptr + 1);
            if (arr[1] == NULL) {
                perror("strdup");
                exit(EXIT_FAILURE);
            }
            /* Strip the string */
            *ptr = '\0';
        /* If we don't find a comma */
        } else {
            exit(EXIT_FAILURE);
        }
        /* Store the first string */
        arr[0] = strdup(str);
        if (arr[0] == NULL) {
            perror("strdup");
            exit(EXIT_FAILURE);
        }
        /* Add a node to the queue*/
        if (enqueue(&head, arr) == NULL) {
            perror("enqueue");
            exit(EXIT_FAILURE);
        }
    }
    /* While nodes in queue show the data and free */
    while ((arr = dequeue(&head))) {
        printf("%s %s", arr[0], arr[1]);
        free(arr[0]);
        free(arr[1]);
        free(arr);
    }
    return 0;
}
David Ranieri
  • 39,972
  • 7
  • 52
  • 94
0

You can use fscanf format strings instead.

fscanf(fp, " %[^,\n], %[^\n]", values[i][0], values[i][1]);

Like so

#include <stdio.h>

int main() {
    FILE* fp = fopen("file.txt", "r");

    char values[1000][2][100];

    int i = 0;
    while(!feof(fp)) {
      fscanf(fp, " %[^,\n], %[^\n]", values[i][0], values[i][1]);
      printf("%s %s\n", values[i][0], values[i][1]);
    }   

}

The fscanf format string:

  • <space> = skip leading whitespaces (like a newline from the line before)
  • %[^,\n] = read a string until, but not including, "," or "\n"
  • , = expect a comma and discard it
  • %[^\n] = read a string until, but not including, "\n"

values is a 2D array of strings, with 1000 rows and 2 columns. Each element is a string of maximum length of 100 chars.

Also, if you use %m[^,\n] instead, fscanf will allocate the memory for the string read.

Aravind Voggu
  • 1,491
  • 12
  • 17