Quick Reference: The 3 Best Methods to Extract File Extensions

When building data loggers, OTA update managers, or web servers on microcontrollers, you frequently need to parse filenames. Knowing how to safely extract file extension in Arduino C is critical for routing files correctly (e.g., serving a .css file with the correct MIME type) or filtering directories on an SD card. Because Arduino environments range from memory-constrained AVR chips (like the ATmega328P) to robust ESP32 modules, your string manipulation strategy must balance convenience with memory safety.

Method 1: The Arduino String Class (Quick but Risky)

The built-in Arduino String Reference provides an intuitive, high-level way to parse text. However, it relies on dynamic heap allocation, which can cause severe heap fragmentation on AVR boards over long uptimes.

String filename = "sensor_data_2026.csv";
int dotIndex = filename.lastIndexOf('.');
String ext = "";
if (dotIndex != -1) {
    ext = filename.substring(dotIndex); // Returns ".csv"
}
Serial.println(ext);

Method 2: C-Style Strings with strrchr (Memory-Safe & Standard)

For production firmware, relying on standard C library functions is the gold standard. The strrchr function searches for the last occurrence of a character in a C-string (null-terminated char array). It returns a pointer to that character, avoiding any memory allocation. Read more about pointer logic in the C++ Reference: strrchr.

const char* filename = "firmware_v2.bin";
const char* ext = strrchr(filename, '.');
if (ext != NULL) {
    // ext now points directly to ".bin" in memory
    Serial.println(ext);
} else {
    Serial.println("No extension found.");
}

Method 3: Manual Pointer Arithmetic (Fastest Execution)

If you are writing a high-speed interrupt service routine (ISR) or parsing thousands of directory entries via the SdFat Library Documentation, manual pointer iteration eliminates function call overhead.

const char* filename = "index.html";
const char* ext = NULL;
const char* p = filename;
while (*p) {
    if (*p == '.') ext = p;
    p++;
}
if (ext) Serial.println(ext);

FAQ: Common Pitfalls When Parsing Strings on Microcontrollers

Q1: Why does my Arduino crash after extracting extensions in a loop?

A: This is almost always caused by heap fragmentation. If you use Method 1 (the String class) inside a while loop that reads an SD card directory, the microcontroller continuously allocates and deallocates small blocks of SRAM. Eventually, the heap becomes fragmented, and a new allocation fails, causing a hard crash or silent reboot. Always use C-strings (char arrays) for repetitive file parsing tasks.

Q2: How do I handle case-insensitive extension matching?

A: Filesystems like FAT32 are case-insensitive, meaning you might encounter .CSV, .csv, or .Csv. If you use the String class, you must call ext.toLowerCase() before comparing. If you use C-strings, use the POSIX standard strcasecmp() function, which compares two strings without allocating new memory:

if (strcasecmp(ext, ".csv") == 0) {
    // Match found, regardless of case
}

Q3: What happens if the filename has no dot?

A: If you use strrchr and the character is not found, it returns a NULL pointer. If you attempt to pass a NULL pointer to Serial.println() or strcmp(), your ESP32 will throw a Guru Meditation Error (Memory Access Violation), and an AVR chip will likely lock up. Always wrap your pointer logic in an if (ext != NULL) guard.

Q4: Can I extract the extension without the dot?

A: Yes. Because strrchr returns a pointer to the dot itself, you simply increment the pointer by one to skip it. Just ensure you check that the dot isn't the very last character in the string to avoid reading past the null terminator.

const char* ext = strrchr(filename, '.');
if (ext && *(ext + 1) != '\0') {
    ext++; // Now points to "bin" instead of ".bin"
}

Comparison Table: String Class vs. C-Arrays for File Parsing

Choosing the right data structure is vital for long-running maker projects. Below is a decision matrix for extracting file extensions based on your target hardware.

Method Memory Impact Execution Speed Code Complexity Recommended Hardware
Arduino String Class High (Heap Allocation) Slow Very Low ESP32 / Teensy (Prototyping only)
C-String strrchr Zero (Pointer Math) Fast Low All MCUs (Production Standard)
Manual Iteration Zero Fastest Medium ATtiny / AVR (Extreme Optimization)
std::string_view Zero (C++17) Fast Medium ESP32 (Using modern ESP-IDF)

Real-World Scenario: Filtering SD Card Directories on ESP32

Imagine you are building an audio player using an ESP32 and an SD card module. You want to scan the root directory and only queue files that end in .wav or .mp3. Using the C-string method ensures your audio buffer remains intact and your scan completes in milliseconds.

#include "SD.h"

void scanAudioFiles() {
    File root = SD.open("/");
    if (!root || !root.isDirectory()) return;

    File file = root.openNextFile();
    while (file) {
        const char* fname = file.name();
        const char* ext = strrchr(fname, '.');
        
        if (ext != NULL) {
            if (strcasecmp(ext, ".wav") == 0 || strcasecmp(ext, ".mp3") == 0) {
                Serial.print("Queuing Audio: ");
                Serial.println(fname);
                // Add to playlist array...
            }
        }
        file = root.openNextFile();
    }
}
Pro Tip: When using the ESP32 SD library, file.name() returns a const char*. Never wrap this in a String object just to parse it; doing so wastes CPU cycles and fragments RAM unnecessarily.

Troubleshooting Checklist: Why Your Extension Extractor Fails

If your code compiles but yields bizarre results, run through this diagnostic checklist:

  • Gibberish Output: You likely forgot the null terminator. If you are copying the extension into a separate char buffer using strncpy, you must manually append '\0' to the end of the buffer. strrchr avoids this by pointing to the original string.
  • False Positives on Hidden Files: In Linux-based filesystems, a file named .gitignore starts with a dot. strrchr will return .gitignore as the extension. To fix this, verify that the pointer returned by strrchr is not the very first character of the filename.
  • Trailing Spaces: Older FAT16/FAT32 implementations sometimes pad filenames with spaces (e.g., DATA .TXT). Always trim trailing whitespace from your filename buffer before searching for the dot character.
  • Multiple Dots: A file named archive.tar.gz will yield .gz using strrchr. If you need .tar.gz, you must write a custom loop that finds the first dot using strchr instead of the last.

Mastering how to extract file extension in Arduino C using memory-safe pointers will drastically improve the stability of your data logging and file-management sketches. Ditch the String class for filesystem operations, embrace strrchr, and your microcontroller will run reliably for years.