Strings
Strings are objects that represent sequences of characters. We use strings to store text.
Table of Contents
- Defining and Initializing Strings
- String Length
- Copying Strings
- Accessing Chars Within Strings
- Looping Over the Chars of a String
- String Concatenation
- String Concatenation with Primitives
- Output Stream String Concatenation
- Input Stream String Parsing
- Comparing Strings
- Sub-Strings
- Replacing Sub-Strings
- Searching Strings
- Passing Strings to Functions
- C-Style Strings
- Unreal Engine String
- Further Reading
Defining and Initializing Strings
Strings are part of the std
namespace and can be used in your code by including the <string>
header.
#include <string>
The easiest way to define and initialize a string is with a string literal:
std::string description{ "It was a dark and stormy night!" };
String Length
The length of a string can be retrieved using the length()
method.
std::string numerals{ "123456789" };
std::cout << "The length of the string '" << numerals << "' is " << numerals.length() << ".";
Copying Strings
Strings are easily copied to other variables using initializer lists and the assignment operator.
std::string original{ "I am truly a unique and valued string." };
std::string copycat1{ original }; // Both strings now contain the same characters.
std::string copycat2 = original; // All three strings now contain the same characters.
Accessing Chars Within Strings
We can access particular characters within a string using square braces []
or with the at()
method. Like arrays and vectors, access to characters in a string is zero-based.
std::string hacker{ "Acid Burn" };
char space = hacker[4]; // The space character.
char capitalB = hacker.at(5); // The capital letter B.
⚡ Warning:
The .at()
does bounds checking but []
access does not. Use []
with caution.
Looping Over the Chars of a String
We can visit every character of a string using a for loop combined with character indexing using square braces or the at()
method.
// Print out each character in the string on separate lines:
for(audo i{0}; i < hacker.length(); i++) {
std::cout << hacker[i] << "\n"; // We could also have used: hacker.at(i)
}
String Concatenation
Multiple strings can be combined using the +
concatenation operator. The +
operator can also concatenate chars to strings.
std::string hack{ "Hack" };
std::string the{ "The" };
std::string planet{ "Planet" };
char space = ' ';
std::string sentence = hack + space + the + space + planet;
String Concatenation with Primitives
Although the +
operator can be used to glue strings and characters together, it won’t work with numeric types. Instead we use std::to_string()
to first convert numeric data to strings.
int time = 4;
std::string townCrier{ "It's " + std::to_string(time) + " o'clock and all is well!" };
// It's 4 o'clock and all is well!
Output Stream String Concatenation
If you like how strings and primitives can be compared using std::cout
you can build strings in a similar way using an std::ostringstream
from the <sstream>
header:
int numberOfAxes{ 4 };
int numberOfRubies{ 2 };
std::ostringstream groceryList;
groceryList << "Trade " << numberOfAxes << " axes for " << numberOfRubies << " rubies.";
std::string list = groceryList.str();
Input Stream String Parsing
String streams can also be used to do simple string parsing:
void parseSentence(std::string sentence) {
std::istringstream iss{ sentence };
std::string animal, adjective;
int number;
iss >> number >> animal >> adjective;
std::cout << "There were " << number << " " << adjective << " " << animal << ".\n";
}
// Later used like this:
parseSentence("25 elephants chill"); // There were 25 chill elephants.
parseSentence("14 gazel angry"); // There were 14 angry gazel.
Comparing Strings
Two strings can be easily compared using the equality operator.
#include <iostream>
#include <string>
int main() {
std::string adminUser{ "administrator" };
std::string userPrompt{};
std::cout << "Username: ";
std::cin >> userPrompt;
if (userPrompt == adminUser) {
std::cout << "Your wish is my command.\n";
} else {
std::cout << "Sorry, access denied!\n";
}
}
Sub-Strings
We can use the .substr()
method to extract sub-strings from std::string
.
- First argument: Starting position of the string.
- Second argument: Length of the sub-string you want.
The second argument is optional. Sub-string will go to the end of the string if only the first argument is provided.
std::string maryPoppins{ "supercalifragilisticexpialidocious" };
std::string super{maryPoppins.substr(0, 5)}; // super
std::string docious{maryPoppins.substr(27)}; // docious
Replacing Sub-Strings
We can use the .replace()
method to replace a portion of a string with another string:
std::string phrase{ "Today is a good day to dine!" };
phrase.replace(11, 4, "great"); // Replace character 11 through 14 "good" with "great".
🎵 Note:
There are many other forms of the replace()
method. Details.
Searching Strings
We can search for characters or sub-strings in a string using find()
. The method returns the start position (std::string::size_type
) of the found character/sub-string. If not found, the method returns std::string::npos
.
#include <iostream>
#include <string>
int main() {
std::string haystack{ "I still haven't found what I'm looking for." };
std::string::size_type position;
position = haystack.find("found"); // Search from start.
if (position != std::string::npos) {
std::cout << "'Found' position: " << position << "\n";
}
position = haystack.find('I', 1); // Search starting at positon 1.
if (position != std::string::npos) {
std::cout << "Second 'I' position: " << position << "\n";
}
}
Passing Strings to Functions
Passing large strings to functions can be expensive. Although we can mitigate this by passing as const
reference, we have another option: std::string_view
.
Introduced in C++17, a std::string_view
gives us a view into an existing string. String views are very cheap to copy and have a similar API to regular standard strings.
#include <iostream>
#include <string_view>
void doesStringInclude(std::string_view str, std::string substr) {
if (str.find(substr) != std::string::npos) {
std::cout << "'" << str << "' DOES contain '" << substr << "'.\n";
} else {
std::cout << "'" << str << "' DOES NOT contain '" << substr << "'.\n";
}
}
int main() {
std::string sentence{ "A needle in a haystack" };
doesStringInclude(sentence, "needle");
doesStringInclude(sentence, "ghost");
}
C-Style Strings
You will occasionally run across code that uses old school C-style strings. C-style strings are arrays of characters with the end of the string denoted by a null terminator (ascii code 0). This is why they are sometimes referred to as null-terminated strings.
Defining a C-style string:
char username[]{ "GhostlyGrinner" }; // C++ automatically adds the null terminator.
If you need to use a function that requires a C-style string parameter:
std::string modernString{ "I'm so fancy and modern!" };
someOldSchoolFunction(modernString.c_str());
Converting a C-style string into std::string
is easy:
char oldSkool[]{ "I'm totally 31337!" };
std::string newSchool{oldSkool};
⚡ Warning:
C-style strings cannot be assigned values with an assignment statement.
Unreal Engine String
When working in Unreal Engine there are three different lightweight string types we can use FText
, FString
, and FName
.
FName
is the most lightweight, is case-insensitive, and mainly used for internal IDs, tags, and names.
FText
is used for any text that might be shown to the user and forms the basis for UI translation/localization.
FString
is similar to FText
but includes all sorts of handy helper functions at the cost of using more memory.