Skip to Content
Course content

190: Reading Configuration Files in C

Click on the "Edit" button in the top corner of the screen to edit your slide content.

What's the simplest way to handle a basic key-value config file?

C doesn't give you a built-in read_config() function, so you have to build one. In my experience, the most reliable approach for a simple key=value format is to read the file line-by-line using fgets() and then split the string. I generally avoid fscanf() for this because it's a nightmare to handle whitespace and newline characters correctly.

Let's say we're building a simple network server and we have a file called server.conf that looks like this: port=8080 and ip=127.0.0.1. Here is the basic logic I use to pull those values out:

char line[256];
FILE *fp = fopen("server.conf", "r");

if (fp == NULL) {
    perror("Unable to open config file");
    return 1;
}

while (fgets(line, sizeof(line), fp)) {
    char *key = strtok(line, "=");
    char *value = strtok(NULL, "\n");

    if (key && value) {
        printf("Found setting: %s is set to %s\n", key, value);
    }
}
fclose(fp);

It's a bit primitive, but for a small tool, it gets the job done without over-engineering the solution.

How do I stop my parser from breaking when there are spaces or comments?

Real users (and you, when you're tired) will put spaces around the equals sign or add comments starting with #. If you use the code above, a line like port = 8080 will result in a key named "port " (with a trailing space), which will fail your string comparisons later. It's frustrating.

I always write a quick helper function to trim whitespace from the start and end of the strings. Also, you should check the first character of every line; if it's a #, just continue to the next iteration of the loop. I've found that adding this small bit of "cleaning" logic saves hours of debugging weird "file not found" or "invalid port" errors that are actually just hidden spaces.

void trim(char *s) {
    char *p = s;
    int l = strlen(p);
    while(isspace(p[l - 1])) p[--l] = 0;
    while(*p && isspace(*p)) ++p, --l;
    memmove(s, p, l + 1);
}

// Inside the loop:
if (line[0] == '#' || line[0] == '\n') continue; 
// ... then strtok ...
trim(key);
trim(value);

Should I just keep these values as strings, or is there a better way?

You definitely don't want to be calling strcmp() every time you need to check a setting in your main logic. It's slow and makes your code ugly. Instead, define a struct that represents your entire configuration. Load the file once at the very start of your program, populate that struct, and then pass a pointer to that struct to whatever functions need it.

For our server example, it would look something like this:

typedef struct {
    int port;
    char ip[16];
    int timeout;
} ServerConfig;

ServerConfig config = {80, "127.0.0.1", 30}; // Default values

// When parsing:
if (strcmp(key, "port") == 0) {
    config.port = atoi(value);
} else if (strcmp(key, "ip") == 0) {
    strncpy(config.ip, value, sizeof(config.ip) - 1);
}

I always initialize the struct with default values first. That way, if a user forgets to put the timeout setting in their config file, your program doesn't crash or behave randomly—it just falls back to the safe default you provided.




📋 Practical Task

Exercise: Build a Game Settings Loader

Create a program that reads a file named settings.conf. The file will contain the following keys: player_name (string), difficulty (integer 1-3), and fullscreen (integer 0 or 1).

  • Define a GameSettings struct to hold these three values.
  • Implement a parser that ignores lines starting with # and trims whitespace around the = sign.
  • Initialize the struct with default values: "Guest", 1, and 0.
  • After reading the file, print the final settings to the console to verify they were loaded correctly.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.