Strings

Strings are objects that represent sequences of characters. We use strings to store text.

Table of Contents

  1. Defining and Initializing Strings
  2. String Length
  3. Copying Strings
  4. Accessing Chars Within Strings
  5. Looping Over the Chars of a String
  6. String Concatenation
  7. String Concatenation with Primitives
  8. Output Stream String Concatenation
  9. Input Stream String Parsing
  10. Comparing Strings
  11. Sub-Strings
  12. Replacing Sub-Strings
  13. Searching Strings
  14. Passing Strings to Functions
  15. C-Style Strings
  16. Unreal Engine String
  17. 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.

Further Reading