bhylu.com

Advertisements

C Record Parser Project

Parse one deliberately small record format with checked conversion and no partial acceptance.

Your brief

Define the input grammar before writing the parser: a positive station number, one comma, and a depth from zero through five hundred. For this lab, numbers may use strtol’s leading whitespace and sign handling, but the delimiter and final end are exact. The helper must change its output only after both fields pass conversion and domain checks. This is not a general CSV parser: quoted fields, embedded commas, encodings and multiline records require a separate format-aware design.

Run it locally

Use a hosted C17 compiler in a separate lab folder. The command assumes a driver named cc; substitute gcc, clang or zig cc as appropriate. C does not currently run in Bhylu’s browser playground. Apache, PHP and MySQL are not needed for these C programs.

cc -std=c17 -Wall -Wextra -Wpedantic -Werror record-parser-project.c -o lesson

On Windows, use -o lesson.exe and run .\lesson.exe in PowerShell. On Unix-like systems run ./lesson. Math-library linking differs by toolchain; GCC on many Unix systems needs -lm after the source.

Download the complete C source. File-writing labs must run in an empty private directory, away from existing reports.

Worked solution

Show the worked solution
C17 · record-parser-project.c

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

struct Reading { int id; int depth; };
static bool parse_record(const char *text, struct Reading *out) {
    char *end = NULL;
    errno = 0;
    long id = strtol(text, &end, 10);
    if (end == text || *end != ',' || errno == ERANGE || id < 1 || id > 10000) { return false; }
    const char *depth_text = end + 1;
    errno = 0;
    long depth = strtol(depth_text, &end, 10);
    if (end == depth_text || *end != '\0' || errno == ERANGE || depth < 0 || depth > 500) { return false; }
    *out = (struct Reading){(int)id, (int)depth};
    return true;
}

int main(void) {
    struct Reading reading = {0, 0};
    if (!parse_record("17,42", &reading)) { return 1; }
    printf("Station %d: %d mm\n", reading.id, reading.depth);
    printf("Trailing junk accepted: %d\n", parse_record("17,42x", &reading));
    return 0;
}

Expected result

Station 17: 42 mm
Trailing junk accepted: 0

The comma is checked after the first conversion, and the end of text is checked after the second. A local pair is committed to the output only when the complete record is valid. Both accepted ranges fit even the minimum required int range. Feed this parser only bounded terminated input; a record grammar cannot compensate for an earlier buffer overflow.

Extend and test

Add tests for no comma, missing depth, zero station, depth 501, excessive integer magnitude and extra commas. Confirm that the output stays unchanged on each rejection.

For language and library rules, consult the WG14 C working group and its public C11 committee draft, which describes the core facilities used here; C17 incorporates corrections. Check your compiler and runtime documentation for implementation details.

Advertisements