Fundamentals

Strings

Work with C++ strings — std::string methods, concatenation, searching, substring, conversion, string_view, raw string literals, and C-string interop.

Strings

C++ offers two ways to work with strings: C-style character arrays (char[]) inherited from C, and the modern std::string class from the standard library. Always prefer std::string in C++ — it manages its own memory, provides a rich interface, and eliminates an entire class of buffer-overflow bugs.


Creating Strings

cpp
#include <iostream>
#include <string>
using namespace std;

int main() {
    // Construction
    string s1 = "Hello";           // Copy initialisation
    string s2("World");            // Direct initialisation
    string s3{", C++!"};           // Uniform initialisation
    string s4(5, '*');             // 5 asterisks: "*****"
    string s5 = s1 + " " + s2;    // Concatenation: "Hello World"

    cout << s1 << "\n";    // Hello
    cout << s4 << "\n";    // *****
    cout << s5 << "\n";    // Hello World

    // Empty string
    string empty;
    cout << empty.empty() << "\n";   // 1 (true)
    cout << empty.length() << "\n";  // 0

    return 0;
}

Common String Operations

cpp
#include <iostream>
#include <string>
using namespace std;

int main() {
    string s = "Hello, World!";

    // Size and capacity
    cout << s.size()   << "\n";   // 13
    cout << s.length() << "\n";   // 13 (same as size())
    cout << s.empty()  << "\n";   // 0 (false)

    // Access individual characters
    cout << s[0]       << "\n";   // H (no bounds check)
    cout << s.at(0)    << "\n";   // H (throws if out of range)
    cout << s.front()  << "\n";   // H (first character)
    cout << s.back()   << "\n";   // ! (last character)

    // Modify
    s[0] = 'h';
    cout << s << "\n";            // hello, World!

    // Append
    s += " How are you?";
    s.append(" Fine!");
    s.push_back('?');             // Add one character

    // Insert and erase
    s.insert(5, " dear");         // Insert at position 5
    s.erase(5, 5);                // Erase 5 chars starting at pos 5

    return 0;
}

Searching

cpp
#include <iostream>
#include <string>
using namespace std;

int main() {
    string s = "The quick brown fox jumps over the lazy dog";

    // find — returns index, or string::npos if not found
    size_t pos = s.find("fox");
    if (pos != string::npos) {
        cout << "Found 'fox' at position: " << pos << "\n";  // 16
    }

    // Find from a position
    size_t pos2 = s.find("the", 10);   // Search from index 10
    cout << "Second 'the' at: " << pos2 << "\n";  // 31

    // rfind — search from the right
    size_t last = s.rfind("o");
    cout << "Last 'o' at: " << last << "\n";  // 41

    // find_first_of — find any of a set of characters
    size_t vowel = s.find_first_of("aeiou");
    cout << "First vowel at: " << vowel << "\n";  // 2 ('e')

    // find_last_of
    size_t lastVowel = s.find_last_of("aeiou");
    cout << "Last vowel at: " << lastVowel << "\n";  // 41 ('o')

    // Check prefix and suffix (C++20)
    cout << s.starts_with("The") << "\n";  // 1 (true)
    cout << s.ends_with("dog")   << "\n";  // 1 (true)

    return 0;
}

Substrings, Trimming, and Replacing

cpp
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;

int main() {
    string s = "  Hello, World!  ";

    // Substring
    string sub = s.substr(2, 5);   // Start at 2, length 5
    cout << sub << "\n";           // Hello

    // Replace
    string text = "I love cats. Cats are great.";
    size_t p = text.find("cats");
    if (p != string::npos) {
        text.replace(p, 4, "dogs");  // Replace 4 chars at p with "dogs"
    }
    cout << text << "\n";  // I love dogs. Cats are great.

    // Trim leading whitespace
    string trimmed = s;
    trimmed.erase(0, trimmed.find_first_not_of(" \t\n\r"));

    // Trim trailing whitespace
    trimmed.erase(trimmed.find_last_not_of(" \t\n\r") + 1);

    cout << "|" << trimmed << "|\n";  // |Hello, World!|

    // Convert to uppercase
    string upper = text;
    transform(upper.begin(), upper.end(), upper.begin(), ::toupper);
    cout << upper << "\n";  // I LOVE DOGS. CATS ARE GREAT.

    return 0;
}

String Comparison

cpp
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;

int main() {
    string a = "apple";
    string b = "banana";
    string c = "apple";

    // Comparison operators — lexicographic
    cout << (a == c) << "\n";    // 1 (true)
    cout << (a < b)  << "\n";    // 1 ("apple" < "banana" alphabetically)
    cout << (b > a)  << "\n";    // 1

    // compare() — like strcmp
    // Returns negative if a < b, 0 if equal, positive if a > b
    cout << a.compare(c) << "\n";   // 0
    cout << a.compare(b) << "\n";   // negative

    // Case-insensitive comparison (using transform)
    string s1 = "Hello";
    string s2 = "hello";

    string lower1 = s1, lower2 = s2;
    transform(lower1.begin(), lower1.end(), lower1.begin(), ::tolower);
    transform(lower2.begin(), lower2.end(), lower2.begin(), ::tolower);

    cout << (lower1 == lower2) << "\n";  // 1 (true — case insensitive)

    return 0;
}

Type Conversions

cpp
#include <iostream>
#include <string>
using namespace std;

int main() {
    // Number → string
    int  i = 42;
    double d = 3.14;
    string si = to_string(i);    // "42"
    string sd = to_string(d);    // "3.140000"
    cout << si << " " << sd << "\n";

    // string → number
    string numStr = "123";
    int    parsed = stoi(numStr);     // string to int: 123
    long   parsed2= stol("9999999");  // string to long
    double parsedD= stod("3.14");     // string to double

    cout << parsed + 1 << "\n";   // 124

    // Safe conversion with error handling
    try {
        int x = stoi("not a number");
    } catch (const invalid_argument& e) {
        cout << "Invalid: " << e.what() << "\n";
    } catch (const out_of_range& e) {
        cout << "Out of range: " << e.what() << "\n";
    }

    // String to C-string (for APIs that need char*)
    const char* cstr = si.c_str();   // "42"
    cout << cstr << "\n";

    // C-string to std::string
    const char* literal = "Hello from C";
    string cpp_str(literal);
    cout << cpp_str << "\n";

    return 0;
}

string_view (C++17)

string_view is a non-owning reference to a string — it avoids copying when you only need to read:

cpp
#include <iostream>
#include <string>
#include <string_view>
using namespace std;

// Using string_view — works with std::string, literals, and char arrays
// without copying
void printLength(string_view sv) {
    cout << "'" << sv << "' has " << sv.size() << " characters\n";
}

bool startsWith(string_view str, string_view prefix) {
    return str.substr(0, prefix.size()) == prefix;
}

int main() {
    string s       = "Hello, World!";
    const char* cs = "Raw literal";

    printLength(s);            // 'Hello, World!' has 13 characters
    printLength(cs);           // 'Raw literal' has 11 characters
    printLength("Direct");     // 'Direct' has 6 characters  (no copy!)

    cout << startsWith(s, "Hello") << "\n";   // 1

    // string_view is cheap to copy and pass by value
    string_view sv = s;
    sv.remove_prefix(7);   // Skip "Hello, "
    cout << sv << "\n";    // World!  (original string unchanged)

    return 0;
}

Note: Never return a string_view that refers to a local string — the view becomes a dangling reference when the string is destroyed.


Raw String Literals

cpp
#include <iostream>
using namespace std;

int main() {
    // Normal string — escape sequences required
    string path    = "C:\\Users\\Alice\\Documents";
    string html    = "<div class=\"main\">\n  <p>Hello</p>\n</div>";

    // Raw string literal — no escape sequences needed
    string rawPath = R"(C:\Users\Alice\Documents)";
    string rawHtml = R"(
<div class="main">
  <p>Hello</p>
</div>)";

    string regex   = R"(\d{4}-\d{2}-\d{2})";   // Regex without escaping

    cout << rawPath << "\n";   // C:\Users\Alice\Documents
    cout << regex   << "\n";   // \d{4}-\d{2}-\d{2}

    return 0;
}

Practical Example: CSV Parser

cpp
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
using namespace std;

vector<string> splitCSV(const string& line, char delimiter = ',') {
    vector<string> fields;
    stringstream ss(line);
    string field;

    while (getline(ss, field, delimiter)) {
        // Trim whitespace from each field
        size_t start = field.find_first_not_of(" \t");
        size_t end   = field.find_last_not_of(" \t");
        if (start != string::npos) {
            fields.push_back(field.substr(start, end - start + 1));
        } else {
            fields.push_back("");
        }
    }
    return fields;
}

int main() {
    vector<string> csvData = {
        "Alice, 30, Engineer, 85000",
        "Bob, 25, Designer, 72000",
        "Charlie, 35, Manager, 95000",
    };

    cout << left;
    cout << setw(12) << "Name"
         << setw(6)  << "Age"
         << setw(12) << "Role"
         << setw(10) << "Salary" << "\n";
    cout << string(40, '-') << "\n";

    for (const auto& row : csvData) {
        vector<string> fields = splitCSV(row);
        if (fields.size() >= 4) {
            cout << setw(12) << fields[0]
                 << setw(6)  << fields[1]
                 << setw(12) << fields[2]
                 << setw(10) << fields[3] << "\n";
        }
    }
    // Output:
    // Name        Age   Role        Salary
    // ----------------------------------------
    // Alice       30    Engineer    85000
    // Bob         25    Designer    72000
    // Charlie     35    Manager     95000

    // stringstream for building strings
    ostringstream oss;
    oss << "Today: " << 2025 << "-" << setfill('0') << setw(2) << 3 << "-01";
    string result = oss.str();
    cout << result << "\n";  // Today: 2025-03-01

    return 0;
}

Summary

  • Always prefer std::string over C-style char[] in C++ — it is safe, resizable, and richly featured
  • Key methods: size(), empty(), find(), substr(), replace(), insert(), erase(), append()
  • string::npos is the sentinel returned when find() does not find a match
  • Use to_string() to convert numbers to strings; use stoi(), stod() etc. to parse numbers from strings
  • string_view (C++17) provides a zero-copy read-only view — use for function parameters that just read a string
  • Raw string literals R"(...)" eliminate escape sequences — great for file paths, HTML, and regex patterns
  • stringstream / ostringstream build strings from mixed types like cout does — useful for formatting

Next, we'll cover control flow — if/else, the ternary operator, and making decisions in your programs.