DMarlon Studio

Lesson 4 · 4 of 20

Strings in depth · C

Split lines with strtok, build text with snprintf without overflowing the buffer and copy safely with strncpy in C, plus strchr and strstr.

C · Strings in depth in DMarlon Studio: the example's code and its output in the terminal

`strtok` splits a line into fields. The first call takes the string and the separators (";"); later calls take NULL to carry on where it stopped, until it returns NULL. It works by writing '\0' over each separator: that is why line is a modifiable array and, afterwards, line holds only "Ana Lopez". Never use it on a string literal.

`snprintf` formats like printf, but into a buffer and never past its size: it always ends the text with '\0'. It returns how many characters it would have needed; if that is greater than or equal to sizeof badge, the text was cut, as happens with "Maximiliano Fernandez".

`strncpy` copies at most n characters, but when the source is longer it does not add the final `'\0'`. The safe pattern is to copy sizeof code - 1 characters and put the terminator in the last position yourself. That leaves code as "IB3170".

`strchr` finds a character and `strstr` a substring; both return a pointer to what they found or NULL. dash + 1 is the string that starts right after the first dash: the route MAD-BCN.

strtok keeps its position in a hidden variable, so you cannot split two strings at the same time. That is what strtok_r (POSIX) and strtok_s (Windows) are for.

Code · main.c

#include <stdio.h>#include <string.h>int main(void) {    // strtok splits a line into fields. It writes '\0' over each separator,    // so it needs a modifiable array, never a string literal.    char line[] = "Ana Lopez;Madrid;34;Engineer";    int i = 0;    for (char *field = strtok(line, ";"); field; field = strtok(NULL, ";"))        printf("Field %d: %s\n", i++, field);    printf("line is now just \"%s\"\n", line);    // snprintf never writes past the buffer and returns the length it needed.    const char *names[] = {"Ana", "Maximiliano Fernandez"};    for (int k = 0; k < 2; k++) {        char badge[16];        int needed = snprintf(badge, sizeof badge, "Hi, %s!", names[k]);        printf("Badge \"%s\" needed %d chars: %s\n", badge, needed,               needed < (int)sizeof badge ? "fits" : "truncated");    }    // strncpy does not add '\0' when the source is too long: add it yourself.    const char *flight = "IB3170-MAD-BCN";    char code[7];    strncpy(code, flight, sizeof code - 1);    code[sizeof code - 1] = '\0';    printf("Flight code: %s (%zu chars)\n", code, strlen(code));    // strchr and strstr search inside a string and return a pointer.    const char *dash = strchr(flight, '-');    printf("Route: %s, has MAD: %s\n", dash + 1, strstr(flight, "MAD") ? "yes" : "no");    return 0;}

Output · cc main.c -o main && ./main

Field 0: Ana Lopez
Field 1: Madrid
Field 2: 34
Field 3: Engineer
line is now just "Ana Lopez"
Badge "Hi, Ana!" needed 8 chars: fits
Badge "Hi, Maximiliano" needed 26 chars: truncated
Flight code: IB3170 (6 chars)
Route: MAD-BCN, has MAD: yes

Key points

  • strtok modifies the string: use an array, not a literal.
  • snprintf never overflows and returns the length it needed.
  • After strncpy, add the final '\0' yourself.
  • strchr and strstr return a pointer or NULL.

Exercise

Split the line "Madrid,Sevilla,,Bilbao" with strtok and see what happens to the empty field; then build the list "Madrid / Sevilla / Bilbao" with snprintf in a 20-character buffer and report whether it was cut.