Lab: CSV parser
Parse a comma-separated value file in C using fgets and sscanf — applying file I/O, string processing, and error handling together.
CSV (Comma-Separated Values) is one of the most common data exchange formats. Parsing it in C requires combining file reading, string splitting, and type conversion. This lab builds a parser for a simple CSV format with a known schema.
The input file
Create students.csv:
name,grade,gpa
Alice,11,3.8
Bob,10,3.2
Carol,12,3.9
David,11,2.7
Eve,12,4.0Goal
Write csv_parser.c that reads students.csv and:
- Skips the header line.
- Parses each data row into a struct.
- Prints all students with GPA above a threshold.
- Prints the average GPA.
The struct
typedef struct {
char name[32];
int grade;
double gpa;
} Student;Approach
Read lines with fgets. Skip the first (header). For each subsequent line, strip the newline, then use sscanf to parse:
sscanf(line, "%31[^,],%d,%lf", student.name, &student.grade, &student.gpa);%31[^,] reads up to 31 non-comma characters (the name). %d reads the grade. %lf reads the GPA as a double.
Worked solution
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char name[32];
int grade;
double gpa;
} Student;
int main(void) {
FILE *fp = fopen("students.csv", "r");
if (!fp) {
perror("fopen");
return EXIT_FAILURE;
}
char line[256];
/* Skip header */
if (!fgets(line, sizeof(line), fp)) {
fprintf(stderr, "Empty file\n");
fclose(fp);
return EXIT_FAILURE;
}
Student students[100];
int count = 0;
while (fgets(line, sizeof(line), fp) && count < 100) {
/* Strip trailing newline */
size_t len = strlen(line);
if (len > 0 && line[len-1] == '\n') { line[len-1] = '\0'; }
Student s;
int parsed = sscanf(line, "%31[^,],%d,%lf",
s.name, &s.grade, &s.gpa);
if (parsed == 3) {
students[count++] = s;
} else {
fprintf(stderr, "Skipping malformed line: %s\n", line);
}
}
fclose(fp);
/* Print students with GPA > 3.5 */
printf("High achievers (GPA > 3.5):\n");
for (int i = 0; i < count; i++) {
if (students[i].gpa > 3.5) {
printf(" %-10s grade %d GPA %.1f\n",
students[i].name, students[i].grade, students[i].gpa);
}
}
/* Average GPA */
double total = 0.0;
for (int i = 0; i < count; i++) {
total += students[i].gpa;
}
printf("\nAverage GPA: %.2f (across %d students)\n",
count > 0 ? total / count : 0.0, count);
return EXIT_SUCCESS;
}Expected output:
High achievers (GPA > 3.5):
Alice grade 11 GPA 3.8
Carol grade 12 GPA 3.9
Eve grade 12 GPA 4.0
Average GPA: 3.52 (across 5 students)Extension: quoted fields
Real CSV files allow commas inside quoted fields: "Smith, John",11,3.8. Extend the parser to handle quoted names by checking whether the first character is " and reading until the closing ".
Extension: dynamic allocation
The current solution uses a fixed-size array of 100 students. Replace it with a DynArray of Student * using malloc — calling realloc as needed. This reuses the growable buffer pattern from the Memory Management lab.
What you practised
- Reading a file line by line with
fgets - Parsing structured text with
sscanf - Error handling for malformed input
- Computing aggregate statistics from a collection of structs
You have completed the Intermediate tier. The Advanced tier begins with the C compilation model — understanding exactly what gcc does in four stages, and how to build multi-file projects with Makefiles.
Error handling with errno
Understand how C reports errors through errno and return values — using perror, strerror, and building robust error-handling patterns.
The four stages: preprocessor, compilation, assembly, linking
Trace what gcc does in four distinct stages — preprocessing, compilation to assembly, assembly to object code, and linking — and understand each stage's inputs and outputs.