1

I am trying to split a line from a file in different parts separated by a single space, but it doesn`t work... So my question is: How can i split my line in different parts and do that for every single line of the file and then put those parts in dynamically allocated vectors/matrix? Or by columns as well. Tell me as you see fit.

The file looks like this:

BERLIN CAR 1 U        
BERLIN CAR 1 R 
BUCHAREST JACKET 2 D
NEW_YORK DOLL 7 U 
BERLIN ROBOT 5 L 
BUCHAREST BALL 4 L 

I want to do this. Example:

Locations[i]={"BERLIN","BERLIN","BUCHAREST","NEW_YORK"."BERLIN","BUCHAREST"}
TOYS[j]={"CAR","CAR","JACKET","DOLL","ROBOT","BALL"}
NUMBER[k]={1,1,2,7,5,4}
LETTER[l]={'U','R','D','U','L','L'}

My code so far (MAX_STRING_LENGTH is defined to 30 ) :

int i;
char *p,**a,delim[]=" ";
a=malloc(100 * sizeof(char));
for(i = 0; i < 100; i++)
{
    a[i]=calloc(MAX_STRING_LENGTH,sizeof(char));
}
while(!feof(stdin))
{
    fgets(*a,500,stdin);
    p=strtok(*a,delim);
}
  • 3
    `while(!feof(stdin)) { fgets(*a,500,stdin);`: so the last line is tokenized twice [why is while(!feof()) always wrong](https://stackoverflow.com/questions/5431941/why-is-while-feof-file-always-wrong) – Jean-François Fabre Jan 08 '18 at 20:34
  • @Jean-FrançoisFabre Put the link to the famous *why is `while(!feof())` always wrong*. – Iharob Al Asimi Jan 08 '18 at 20:35
  • Possible duplicate of [Read .CSV file in C](https://stackoverflow.com/questions/12911299/read-csv-file-in-c) – Jean-François Fabre Jan 08 '18 at 20:35
  • I think `a=malloc(100 * sizeof(char));` should be `a=malloc(100 * sizeof(char*));`. – Benjamin J. Jan 08 '18 at 20:36
  • 1
    @BenjaminJ. - The first allocation should be for the pointers that will point to each line, therefore it should probable be`a=malloc(100 * sizeof(char *));` – ryyker Jan 08 '18 at 20:40
  • @ryyker: Oh, thanks. I've edited my comment. – Benjamin J. Jan 08 '18 at 20:41
  • The other question is should the next statement be `a[i]=calloc(MAX_STRING_LENGTH,sizeof(char));` or `a[i]=calloc(MAX_STRING_LENGTH + 1,1);` – ryyker Jan 08 '18 at 20:43
  • @ryyker it`s the same thing i think because char has the size of 1 – Matei Cristian Nicusor Jan 08 '18 at 20:48
  • In the `!feof` loop `a` does not advance to the next element. Also, `strtok` only extracts the first token (the city) so that needs a loop to extract the other fields. Also, you must count the number of file records that were read. The code lacks a `struct` to store these fields, and an array of that `struct` is better than separate arrays of each field. I suggest you find some examples of similar code: there are plenty. – Weather Vane Jan 08 '18 at 20:49
  • @WeatherVane that is exactly what i am doing . i have a matrix in which every single element is a struct, but i don`t know if the fields retains those things and if i can acces those fields independetly,Like the field from my structure called destinations is a vector of strings and i can play with it like so? i simply lack the information and i can`t`find it anywhere and i am new to programming..... – Matei Cristian Nicusor Jan 08 '18 at 20:57
  • @MateiCristianNicusor can you include in your post the definition of the struct? Also you can have an array of `struct carinfo` or a struct that contains all cars. Both are valid solution but depending on your needs, one can be better than the other. – Pablo Jan 08 '18 at 21:02
  • @Pablo i have added my entire code.. sorry if i am being stressful... – Matei Cristian Nicusor Jan 08 '18 at 21:10
  • @MateiCristianNicusor I made an update of my answer with an example of how to implement your code. – Pablo Jan 08 '18 at 21:58

1 Answers1

2

strtok is the correct function, however you are using it wrong.

man strtok

The strtok() function breaks a string into a sequence of zero or more nonempty tokens. On the first call to strtok(), the string to be parsed should be specified in str. In each subsequent call that should parse the same string, str must be NULL.

I made the most important part of the quote bold.

Also bear in mind that strtok modifies the source, if you need the source afterwards, you have to make a copy.

// assuming that line is either a char[] or char*

char *token = strtok(line, " ");
if(token == NULL)
{
    // error detection
}

while(token = strtok(NULL, " "))
{
    // do the work
}

Also I recommend not to use sizeof(<data type>) in malloc/calloc/realloc calls. It's easy to overlook a * and make mistakes. Better:

int *myarray = malloc(size * sizeof *myarray);

// or

int *mayarray = calloc(size, sizeof *myarray);

Using sizeof *var is better because it will always returns the correct size.

One last thing:

while(!feof(stdin))

See Why is “while ( !feof (file) )” always wrong?

better

char buffer[1024];
while(fgets(buffer, sizeof buffer, stdin))
{
    // do the work here
}

EDIT

Here you have a sample implementation using strtok. My implementation uses an array of MAPs. Look at the way I construct/destruct the the MAP objects and how the memory is allocated. Obviously this can be done with less code and less strdups, but I think this shows more precisely how to use these functions. You can use this code as your base or just use it as a basic idea.

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

typedef struct{
    char *destination;
    char *type_present;
    int no_available_presents;
    char *direction;
} MAP;

MAP *create_map(const char *dest, const char *type, int present, const char *dir);
void free_map(MAP *map);
void print_map(MAP *map);


MAP *create_map(const char *dest, const char *type, int present, const char *dir)
{
    MAP *map = calloc(1, sizeof *map);
    if(map == NULL)
        return NULL;

    int errors = 0;

    if(!(map->destination = strdup(dest)))
        errors++;

    if(!(map->type_present = strdup(type)))
        errors++;

    if(!(map->direction = strdup(dir)))
        errors++;

    map->no_available_presents = present;

    if(!errors)
        return map;

    free_map(map);
    return NULL;
}

void free_map(MAP *map)
{
    if(map == NULL)
        return;

    free(map->destination);
    free(map->type_present);
    free(map->direction);
    free(map);
}

void print_map(MAP *map)
{
    if(map == NULL)
    {
        puts("(null)");
        return;
    }

    printf("destination: %s\n", map->destination);
    printf("type:        %s\n", map->type_present);
    printf("present:     %d\n", map->no_available_presents);
    printf("direction:   %s\n", map->direction);
}

int main(char argc, char **argv)
{
    FILE *fp;
    if(argc != 1 && argc != 2)
    {
        fprintf(stderr, "usage: %s [database]\n", argv[0]);
        return 1;
    }

    if(argc == 1)
        fp = stdin;
    else
        fp = fopen(argv[1], "r");

    if(fp == NULL)
    {
        fprintf(stderr, "Could not open '%s': %s\n", argv[1], strerror(errno)); 
        return 1;
    }

    MAP **maps = NULL;
    size_t map_len = 0;

    char line[1024];
    const char *delim = " \r\n";

    while(fgets(line, sizeof line, fp))
    {
        int pres;
        char *dest = NULL, *type = NULL, *dir = NULL, *token;

        token = strtok(line, delim);
        dest = strdup(token);

        token = strtok(NULL, delim);
        type = strdup(token);

        token = strtok(NULL, delim);
        pres = atoi(token);

        token = strtok(NULL, delim);
        dir = strdup(token);

        if(dest == NULL || type == NULL || dir == NULL)
        {
            // ignore line
            free(dest);free(type);free(dir);
            continue;
        }

        MAP *new_map = create_map(dest, type, pres, dir);

        if(new_map == NULL)
        {
            // ignore line
            free(dest);free(type);free(dir);
            continue;
        }

        MAP **tmp_map = realloc(maps, (map_len + 1) * sizeof *tmp_map);

        if(tmp_map == NULL)
        {
            // ignore line
            free_map(new_map);
            free(dest);free(type);free(dir);
            continue;
        }

        maps = tmp_map;
        maps[map_len++] = new_map;
        free(dest);free(type);free(dir);
    }

    for(int i = 0; i < map_len; ++i)
    {
        print_map(maps[i]);
        puts("---");
        free_map(maps[i]);
    }

    free(maps);


    if(fp != stdin)
        fclose(fp);

    return 0;
}

The output:

destination: BERLIN
type:        CAR
present:     1
direction:   U
---
destination: BERLIN
type:        CAR
present:     1
direction:   R
---
destination: BUCHAREST
type:        JACKET
present:     2
direction:   D
---
destination: NEW_YORK
type:        DOLL
present:     7
direction:   U
---
destination: BERLIN
type:        ROBOT
present:     5
direction:   L
---
destination: BUCHAREST
type:        BALL
present:     4
direction:   L
---
Pablo
  • 13,271
  • 4
  • 39
  • 59
  • I did that check with strtok, i thought that was irrelevant to this problem . So i understand that my code has a lot o little problmes... – Matei Cristian Nicusor Jan 08 '18 at 20:52
  • @MateiCristianNicusor do you mean the first `token == NULL` check? More important is that you have to loop `strtok` until you get `NULL`, otherwise you only retrieve the first token. – Pablo Jan 08 '18 at 20:54
  • 1
    The read function `fgets` retains any newline in the input string. In the context of `strtok` it would strip that by including newline perms in the delimitor string, say `strtok(.... , " \n\r")` – Weather Vane Jan 08 '18 at 20:57
  • @WeatherVane that's a good advice, I've never thought of doing that. I usually strip the the `\n` before anything else. – Pablo Jan 08 '18 at 21:00