C
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions
-
Section 4: Arrays and Strings
-
Section 5: Pointers
-
Section 6: Memory Management
-
Section 7: Structures and Unions
-
Section 8: The Preprocessor and Build Process
-
Section 9: Standard Library: stdio.h
-
Section 10: Standard Library: stdlib.h
-
Section 11: Standard Library: string.h
-
Section 12: Standard Library: ctype.h and wctype.h
-
Section 13: Standard Library: math.h, complex.h, fenv.h, tgmath.h
-
Section 14: Standard Library: Type and Limit Headers
-
Section 15: Standard Library: Error Handling and Debugging
-
Section 16: Standard Library: Localization and Encoding
-
Section 17: Standard Library: time.h
-
Section 18: Standard Library: Concurrency (C11)
-
Section 19: POSIX and System Programming (unistd.h)
-
Section 20: More Data Structures
-
Section 21: Algorithms in C
-
Section 22: Bitwise Operations
-
Section 23: Command-Line Programs
-
Section 24: Debugging and Best Practices
-
Section 25: Compiler and Language Internals
-
Section 26: Embedded and Cross-Platform Considerations
-
Section 27: Networking Basics
-
Section 28: Practical Projects
-
Section 29: Interview Practice
-
Section 30: C23 Modern Features
-
Section 31: More Practice and Review
190: Reading Configuration Files in C
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
GameSettingsstruct 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, and0. - After reading the file, print the final settings to the console to verify they were loaded correctly.
There are no comments for now.