How to Trim Whitespace from a String in C++
Short answer: C++ has no built-in trim(). Use find_first_not_of and find_last_not_of to locate the first and last non-whitespace characters, then take the substring between them. Copy-paste function below.
The Copy-Paste Trim Function
#include <iostream>
#include <string>
const std::string WHITESPACE = " \t\n\r\f\v";
std::string ltrim(const std::string& s) {
size_t start = s.find_first_not_of(WHITESPACE);
return (start == std::string::npos) ? "" : s.substr(start);
}
std::string rtrim(const std::string& s) {
size_t end = s.find_last_not_of(WHITESPACE);
return (end == std::string::npos) ? "" : s.substr(0, end + 1);
}
std::string trim(const std::string& s) {
return rtrim(ltrim(s));
}
int main() {
std::string messy = " hello world ";
std::cout << "[" << trim(messy) << "]\n"; // [hello world]
std::cout << "[" << ltrim(messy) << "]\n"; // [hello world ]
std::cout << "[" << rtrim(messy) << "]\n"; // [ hello world]
}
That is the whole job. The rest of this article explains why it works and the traps around it.
Why There Is No std::string::trim
std::string is a container of bytes, not a text type. It does not assume your data is human-readable text, so it does not assume you want whitespace removed. Trimming also needs a decision — is a non-breaking space whitespace? what about a null byte? — and the standard library avoids making that choice for you.
The practical consequence: every C++ codebase ends up with its own small trim helper. Put the one above in a utility header and stop rewriting it.
How find_first_not_of Actually Works
find_first_not_of scans from the left and returns the index of the first character that is not in the set you pass:
std::string s = " abc";
size_t i = s.find_first_not_of(" "); // 3 — index of 'a'
find_last_not_of does the same from the right:
std::string s = "abc ";
size_t i = s.find_last_not_of(" "); // 2 — index of 'c'
So substr(start, end - start + 1) gives you everything between the first and last real characters.
The All-Whitespace Trap
If the string contains nothing but whitespace, find_first_not_of returns std::string::npos — a huge unsigned value. Passing that to substr throws std::out_of_range, or silently produces garbage if you do arithmetic on it first:
std::string blank = " ";
size_t start = blank.find_first_not_of(WHITESPACE); // npos
// blank.substr(start); // throws std::out_of_range
That is exactly why both functions above check for npos and return an empty string. Skipping that check is the single most common bug in hand-written trim code.
Trimming in Place
If you would rather modify the string instead of returning a copy, erase from both ends:
void trimInPlace(std::string& s) {
s.erase(0, s.find_first_not_of(WHITESPACE));
size_t end = s.find_last_not_of(WHITESPACE);
if (end != std::string::npos) s.erase(end + 1);
else s.clear();
}
erase(0, n) removes the first n characters, and erase(pos) removes everything from pos onward.
Trimming Other Characters
Nothing here is whitespace-specific. Pass whatever set you want to strip:
std::string quoted = "\"hello\"";
std::string bare = trimChars(quoted, "\""); // hello
std::string padded = "000042000";
std::string digits = trimChars(padded, "0"); // 42
with:
std::string trimChars(const std::string& s, const std::string& chars) {
size_t start = s.find_first_not_of(chars);
if (start == std::string::npos) return "";
size_t end = s.find_last_not_of(chars);
return s.substr(start, end - start + 1);
}
Useful for stripping quotes off CSV fields or leading zeros off IDs.
Trimming Input from cin
The most common reason people need trim is user input. Note that std::getline already strips the newline, but it keeps any spaces the user typed:
std::string name;
std::getline(std::cin, name);
name = trim(name); // " Sahil " becomes "Sahil"
If you are mixing cin >> and getline and getting skipped input, that is a different problem — see clearing the input buffer with cin.ignore.
Removing All Spaces Instead
Trim only touches the ends. To delete every space in the string, use the erase-remove idiom:
#include <algorithm>
std::string s = "a b c d";
s.erase(std::remove(s.begin(), s.end(), ' '), s.end());
std::cout << s; // abcd
std::remove shuffles the unwanted characters to the back and returns an iterator to the new logical end; erase then actually shortens the string. It does not remove anything on its own — forgetting the erase half is a classic mistake.
Quick Reference
| Goal | Code |
|---|---|
| Trim both ends | trim(s) from above |
| Trim left only | s.erase(0, s.find_first_not_of(WS)) |
| Trim right only | s.erase(s.find_last_not_of(WS) + 1) |
| Strip specific chars | trimChars(s, "\"") |
| Remove all spaces | s.erase(std::remove(s.begin(), s.end(), ' '), s.end()) |
Take Your C++ Further
If you want strings, vectors and the STL explained properly rather than looked up one function at a time, the C++ Better Explained Ebook covers the fundamentals in plain English with diagrams and runnable examples. Just $19.
👉 Get the C++ Better Explained Ebook — $19
Related Articles
- C++ String Handling: A Complete Guide — the full picture on std::string.
- How to Split a String in C++ — break a line into fields after trimming it.
- C++ substr(): Extracting Substrings — the function trim is built on.
- cin.ignore() and Clearing the Input Buffer — fix skipped or stuck input.
- How to Check if a String Contains a Substring — searching within strings.