HOWTO · Arduino
How to Split String in Arduino
Learn how to extract Arduino String fields by index or delimiter, find the last line, and split mutable character arrays safely.
On this page
Arduino does not provide one String method that returns an array of every field. You can extract a known range with substring(), find an unknown separator with indexOf(), or tokenize a writable character array with the C function strtok(). A manual parsing loop gives you control over empty fields and multiple separators. Choose based on whether positions are known, whether the input may be modified, and how separators should be treated.
The following complete sketch uses Arduino’s String and Serial APIs. A small host-side compatibility shim lets the same sketch run as standard C++ when ARDUINO is not defined; on a board, the Arduino core supplies those APIs. The serial lines shown here are generated by the sketch.
Use substring() for Index-Based Extraction in Arduino
Use substring(begin, end) when you already know the range to extract. The begin index is included, while the end index is excluded. Thus, for "Hello,World", indices 0 through 4 spell Hello, and the end argument is 5. A one-argument call such as substring(begin) extracts from that index through the end of the string.
Run this sketch:
#ifdef ARDUINO
#include <Arduino.h>
#else
#include <iostream>
#include <string>
class String {
public:
String(const char *value) : value_(value) {}
int indexOf(char target) const {
const auto position = value_.find(target);
return position == std::string::npos ? -1 : static_cast<int>(position);
}
int indexOf(char target, unsigned int fromIndex) const {
const auto position = value_.find(target, fromIndex);
return position == std::string::npos ? -1 : static_cast<int>(position);
}
int lastIndexOf(char target) const {
const auto position = value_.rfind(target);
return position == std::string::npos ? -1 : static_cast<int>(position);
}
String substring(unsigned int begin, unsigned int end) const {
return String(value_.substr(begin, end - begin).c_str());
}
String substring(unsigned int begin) const {
return String(value_.substr(begin).c_str());
}
const char *c_str() const { return value_.c_str(); }
private:
std::string value_;
};
struct HostSerial {
void begin(unsigned long) {}
void println(const String &value) { std::cout << value.c_str() << '\n'; }
void println(const char *value) { std::cout << value << '\n'; }
};
HostSerial Serial;
#endif
#include <cstring>
void setup() {
Serial.begin(9600);
String greeting = "Hello,World";
Serial.println(greeting.substring(0, 5));
String record = "sensor1:value1";
int separator = record.indexOf(':');
if (separator != -1) {
Serial.println(record.substring(0, separator));
}
String recordWithoutColon = "sensor1";
int absentSeparator = recordWithoutColon.indexOf(':');
if (absentSeparator == -1) {
Serial.println("delimiter not found");
} else {
Serial.println(recordWithoutColon.substring(0, absentSeparator));
}
String lines = "hello world\nGreetings\nPeople";
int finalNewline = lines.lastIndexOf('\n');
String finalLine = finalNewline == -1
? lines
: lines.substring(finalNewline + 1);
Serial.println(finalLine);
String oneLine = "single line";
int noNewline = oneLine.lastIndexOf('\n');
String onlyLine = noNewline == -1 ? oneLine : oneLine.substring(noNewline + 1);
Serial.println(onlyLine);
char names[] = "Alice,Bob,Charlie,David";
for (char *token = std::strtok(names, ","); token != nullptr;
token = std::strtok(nullptr, ",")) {
Serial.println(token);
}
String fields = "red,green,blue";
unsigned int start = 0;
while (true) {
int nextSeparator = fields.indexOf(',', start);
if (nextSeparator == -1) {
Serial.println(fields.substring(start));
break;
}
Serial.println(fields.substring(start, nextSeparator));
start = nextSeparator + 1;
}
}
void loop() {}
#ifndef ARDUINO
int main() {
setup();
return 0;
}
#endif
Hello
sensor1
delimiter not found
People
single line
Alice
Bob
Charlie
David
red
green
blue
The host shim is only for checking the sketch logic with a general C++ compiler; it does not emulate a physical board or Arduino hardware. With Arduino selected, the conditional code uses the core’s actual String and Serial implementations. The sketch checks indexOf() before extraction and handles lastIndexOf('\n') == -1 by using the complete single-line string.
Find a Delimiter with indexOf() and Extract a Field
When the separator position varies, call indexOf() and use its returned position as the end index for substring(). Arduino returns -1 when the requested character or text is absent. Check for that value before passing the result to substring(); otherwise the result does not represent a valid field boundary.
For example, with String record = "sensor1:value1", find ':'. If the result is nonnegative, record.substring(0, separator) extracts sensor1. If the colon is absent, decide explicitly whether to treat the whole record as one field or reject it. For more than one field, repeat the search from the character after the previous separator.
Extract the Last Line from a Multiline String
Use lastIndexOf('\n') to locate the final line break, then start the extraction one character after it. Starting at the newline itself includes the line break and can make the serial output appear to contain a blank line. If no newline exists, lastIndexOf() returns -1; handle this separately, for example by treating the complete string as the only line.
For a string containing hello world, Greetings, and People on separate lines, the final-line extraction begins at lastIndexOf('\n') + 1, and the resulting text is People.
Tokenize a Mutable Character Array with strtok()
strtok() takes a writable, null-terminated character array on its first call. It replaces separators in that array with null characters as it finds tokens, so do not pass a string literal or expect the original array to remain intact. Call strtok(nullptr, delimiters) to continue scanning the same array; stop when it returns nullptr.
The delimiter argument is a set of individual characters, not a multi-character separator. For example, ":|" splits at every colon or pipe. Consecutive separators are skipped, so strtok() does not preserve empty fields. Use manual parsing when empty fields or the original input must be retained.
Split Delimited Text with a Manual Parsing Loop
A manual loop is useful when the format needs rules that tokenizers do not provide. Search for a separator, process the range before it, and move the next start position past that separator. After the loop, process the remaining suffix even when the input does not end with a separator; this is what includes the final field in the example.
For multiple separator characters, test each character against the chosen set and define whether adjacent separators represent empty fields. To preserve an empty field, process a zero-character range between adjacent separators instead of skipping ahead. To preserve the source, read from the original Arduino String without modifying it, or copy a character array before using a mutating tokenizer.
Choose an Approach and Handle Edge Cases
Use substring() for fixed positions, indexOf() when a separator must first be found, and lastIndexOf() when the final occurrence matters. Use strtok() for simple token streams when changing a character array and discarding empty fields are acceptable. A manual loop is more suitable when the format has explicit rules for multiple separators, empty fields, or input preservation.
Before extracting a field, decide what a missing separator means and check for -1. Remember that Arduino’s substring() end is exclusive, and that the newline should generally be excluded when extracting the text after it. These choices determine whether the result contains exactly the intended characters.