1

I'm trying to read in a data file with variables and values in standard C, and assign the values and variables each to their own arrays.

The data file appears like so:

 recipName=Fork
 friend=Cup
 sonName=Spork

...and so on. I want to store the variables before the = in one array, and the values in another array. I don't want to use a 2D array because I have a function later on that requires separate items. The end goal is to replace all of the variables in a template file with the values provided in the data file, but I'm unable to figure out how to get them out of the data file due to the = sign. I used fgets() to retrieve the template file, but am not sure how to proceed here.

1

3 Answers 3

1

You could use strtok to tokenise the string, something like this:

char *opt;
char *par;

char myString[20] = "recipName=Fork";

/**
 * tokenise the string at the = character...
 */
opt = strtok(myString, "=");

/**
 * The initial call to strtok returns the first token, everything up
 * to the first = character. The next call to strtok returns the
 * next token, etc.
 */
par = strtok(NULL, "=");

/**
 * You can now reference your option/lvalue in opt and the parameter/rvalue 
 * using par.
 */
printf("%s = %s\n", opt, par);

This is fine if each line is the same format - option=value. If your file contains more complex lines you might want to look into using something like flex.

This is a pretty vague answer, I know - sorry, the question is quite broad. To me it's important to know about the file, what values and data it might potentially contain, how big it is, etc, and design something suitable from there. How is the data to be stored, etc...i suggest you man strtok() and possibly look into regex and or flex/bison.

But based on the information you provided, hope it helps.

Sign up to request clarification or add additional context in comments.

1 Comment

That should work fine, the assignment I'm working on could have any size of data file I believe, but should always follow the option=value format. Thanks for your help!
1

Avoid using strtok. (Read the BUGS section in the man page.)

Here is a version which, according to your question, reads the variable names and values into two separate arrays vars and vals. The arrays are dynamically reallocated while parsing the file.

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

int main()
{
    FILE *fp;
    char buf[1024];
    char *p;
    void *tmp;
    int cnt = 0;
    int ret = 1;
    int i;
    char **vars = NULL;
    char **vals = NULL;

    if (!(fp = fopen("data", "r"))) {
        perror("opening data");
        return ret;
    }

    while (fgets(buf, sizeof(buf), fp)) {
        buf[strcspn(buf, "\n")] = 0;
        if (!(p = strchr(buf, '=')))
            continue;
        *p++ = 0;
        /* disallow empty names and values */
        if (!strlen(buf) || !strlen(p))
            continue;

        if (!(tmp = realloc(vars, (cnt + 1) * sizeof(char*))))
            goto out;
        vars = (char**)tmp;

        if (!(tmp = realloc(vals, (cnt + 1) * sizeof(char*))))
            goto out;
        vals = (char**)tmp;

        vars[cnt] = strdup(buf);
        vals[cnt] = strdup(p);

        cnt++;

        if (!vars[cnt-1] || ! vals[cnt-1])
            goto out;
    }

    ret = 0;

    printf("successfully parsed %d values:\n", cnt);

    for (i = 0; i < cnt; i++)
        printf("variable [%s] has value [%s]:\n", vars[i], vals[i]);

out:
    fclose(fp);

    if (vars)
        for (i = 0; i < cnt; i++)
            free(vars[i]);
    if (vals)
        for (i = 0; i < cnt; i++)
            free(vals[i]);
    free(vars);
    free(vals);

    return ret;
}

2 Comments

Nice implementation of goto btw :)
Good example, and legitimate use of goto. You might change buf[strlen(buf)-1] = 0; to buf[strcspn(buf, "\n")] = '\0'; to avoid truncating the last byte of the last value if the file does not end with a line-feed. Also if (!(p = strchr(buf, '='))) is redundant.
0

If you have a function that requires separate items, then strtok might not be the way to go. The strtok is used to locate substrings in a string, and its most common use is to parse a string into tokens, much as a compiler parses lines of code.

Let's take a look at the following example.

char str[20] = "recipName=Fork";
char *one = NULL;
char *two = NULL;

The first parameter is the string that is being parsed; the second is a set of delimiters that will be used to parse the first string. Strtok first skips over all leading delimiter characters. If all the characters in the string are delimiters then it terminates and returns a null pointer. However, when it finds a non-delimiter character, it changes its search and skips over all characters that are not in the set; that is, strtok will search until it finds a delimiter. When a delimiter is found, it is changed to a null character ('\0'), marking the end of the token, and returns a pointer to the new token string.

       +---+---+---+---+---+---+---+---+---+---+---+---+---+---+----+
str -> | r | e | c | i | p | N | a | m | e | = | F | o | r | k | \0 |
       +---+---+---+---+---+---+---+---+---+---+---+---+---+---+----+

Calling strtok like so

one = strtok( str, "=" );

causes the following ( the "=" is now a null character )

       +---+---+---+---+---+---+---+---+---+----+---+---+---+---+----+
str -> | r | e | c | i | p | N | a | m | e | \0 | F | o | r | k | \0 |
       +---+---+---+---+---+---+---+---+---+----+---+---+---+---+----+
         ^
         |
one -----+  (first token)

Calling strtok one more time

two = strtok( NULL, "=" );

causes the following

       +---+---+---+---+---+---+---+---+---+----+---+---+---+---+----+
str -> | r | e | c | i | p | N | a | m | e | \0 | F | o | r | k | \0 |
       +---+---+---+---+---+---+---+---+---+----+---+---+---+---+----+
         ^                                        ^
         |                                        |
one -----+  (first token)                two -----+ (second token)

Everything up to this point is dandy. Nevertheless, if you were to use the strlen function you will run into trouble. In addition, if you were to modify the values of "one" and "two", you will most likely override their data.

You can avoid these issues by reading character by character instead.

I hope this helps. :)

1 Comment

Nice explanation, but you should warn about the hidden static state. What kind of trouble would using strlen lead to?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.