The Challenge of Path Parsing on Microcontrollers
Whether you are building an SD card datalogger on an ATmega328P or routing HTTP requests on an ESP32-S3 web server, you will inevitably need to parse file paths or URL routes. The specific challenge—how to Arduino get parts of path from string—seems trivial on a desktop PC but becomes a critical memory management exercise on resource-constrained microcontrollers.
In modern embedded development, a file path like /data/logs/2026/sensor.csv or an MQTT topic like home/livingroom/temperature must be split into discrete components. Doing this incorrectly leads to heap fragmentation, out-of-memory crashes, or multi-threading race conditions. This guide explores the exact techniques to extract directories, filenames, and extensions safely, contrasting the beginner-friendly Arduino String class with professional C-string manipulation.
Method 1: The Arduino String Object (Quick but Risky)
The Arduino String class provides high-level methods like lastIndexOf() and substring(). According to the official Arduino String Reference, these methods are intuitive for beginners. However, they dynamically allocate memory on the heap.
Extracting Directory and Filename
To split a path into its parent directory and the base filename, you locate the final forward slash and extract the substrings on either side.
String fullPath = "/data/logs/2026/sensor.csv";
int lastSlash = fullPath.lastIndexOf('/');
String fileName = fullPath.substring(lastSlash + 1);
String dirPath = fullPath.substring(0, lastSlash);
Serial.println(dirPath); // Outputs: /data/logs/2026
Serial.println(fileName); // Outputs: sensor.csv
The Hidden Danger: Heap Fragmentation
While the code above works perfectly in the setup() function, placing it inside a loop() that runs hundreds of times a second is a recipe for disaster on AVR boards (like the Uno or Nano) with only 2KB of SRAM. Every time substring() is called, it allocates a new block of heap memory. Over time, this creates "holes" in the RAM (fragmentation), eventually causing the board to lock up or reboot. For ESP32 boards with 520KB+ of SRAM, this is less immediately fatal but still considered poor practice in production firmware.
Method 2: C-Strings and strtok() (The Memory-Safe Standard)
Professional embedded engineers avoid the String class for parsing, relying instead on null-terminated C-strings (char arrays) and the <string.h> library. The strtok() (string token) function is the industry standard for splitting paths.
How strtok() Mutates Memory
Unlike substring(), strtok() does not allocate new memory. Instead, it destructively modifies the original string by replacing the delimiter (e.g., /) with a null terminator (\0). This means your original path variable will be altered.
#include <string.h>
char path[] = "/data/logs/2026/sensor.csv";
char *token = strtok(path, "/");
while (token != NULL) {
Serial.println(token);
token = strtok(NULL, "/");
}
Output:
data
logs
2026
sensor.csv
Expert Warning: Becausestrtok()modifies the original array, you must pass a mutablechararray (e.g.,char path[] = ...), not a read-only string literal (e.g.,char *path = "..."), which is stored in flash memory and will cause a hard fault if modified. The C++ Reference for strtok explicitly warns about this undefined behavior.
Method 3: Thread-Safe Parsing on ESP32 with strtok_r()
If you are developing for the ESP32 family (including the S3 and C6) using FreeRTOS, you must account for Symmetric Multiprocessing (SMP). The standard strtok() function uses a static internal pointer to remember its place between calls. If two separate FreeRTOS tasks attempt to parse paths simultaneously, they will overwrite each other's internal state, leading to corrupted data and crashes.
To solve this, use the reentrant version: strtok_r(). As detailed in the Espressif FreeRTOS SMP Guide, reentrant functions require you to pass a pointer to a variable that stores the state locally.
char path[] = "/api/v1/sensor";
char *saveptr; // Local state variable
char *token = strtok_r(path, "/", &saveptr);
while (token != NULL) {
Serial.println(token);
token = strtok_r(NULL, "/", &saveptr);
}
Extracting File Extensions Safely
Often, you need to isolate the file extension (e.g., .csv or .json) to determine how to parse the payload. Using strrchr() (string reverse character) is the most efficient way to find the last occurrence of a dot . without iterating through the entire array manually.
char filename[] = "sensor_data_2026.csv";
char *extension = strrchr(filename, '.');
if (extension != NULL) {
// extension points to ".csv"
Serial.println(extension + 1); // Outputs: csv
} else {
Serial.println("No extension found.");
}
Memory & Performance Comparison Matrix
Choosing the right parsing method depends on your target hardware and application architecture. Below is a direct comparison of the three primary techniques.
| Feature | Arduino String Class |
C-String strtok() |
C-String strtok_r() |
|---|---|---|---|
| SRAM Overhead | High (Dynamic Allocation) | Zero (In-place mutation) | Minimal (1 pointer variable) |
| Heap Fragmentation | Severe Risk | None | None |
| Thread Safety | Yes (but risky) | No (Static internal state) | Yes (Reentrant) |
| Execution Speed | Slow | Fast | Fast |
| Modifies Original? | No (Creates copies) | Yes (Inserts \0) |
Yes (Inserts \0) |
Handling Real-World Edge Cases
Production firmware must handle malformed paths gracefully. Here is how to handle common edge cases when parsing strings in Arduino C++:
- Double Slashes (
/data//logs/):strtok()automatically treats consecutive delimiters as a single delimiter, safely skipping the empty space. TheStringclass will return an empty string, requiring manual validation. - Trailing Slashes (
/data/logs/): If you uselastIndexOf('/')to find a filename, a trailing slash will result in an empty filename string. Always strip trailing slashes before parsing. - Root Paths (
/): Callingsubstring()on a single slash can cause out-of-bounds errors if not explicitly checked. Always verifypath.length() > 1before splitting. - Missing Extensions: If a file is named simply
MakefileorREADME,strrchr()will returnNULL. Failing to check forNULLbefore dereferencing the pointer will result in a Guru Meditation Error (panic) on ESP32 or a silent crash on AVR.
Summary and Best Practices
To successfully get parts of a path from a string in Arduino, you must look beyond basic syntax and consider the underlying memory architecture. For rapid prototyping on high-memory boards, the String class offers convenience. However, for robust, production-ready firmware—especially on AVR microcontrollers or multi-core ESP32 environments—mastering C-strings with strtok() and strtok_r() is mandatory. By avoiding heap fragmentation and ensuring thread safety, your dataloggers and web servers will run indefinitely without memory leaks.






