Beyond std::strrchr: Exploring Alternative Techniques for String Searches in C++


Function Definition

const char* strrchr( const char* str, int ch );

Parameters

  • ch: This is an integer representing the character you're looking for. The int type is used here because characters are internally stored as numbers in C++.
  • str: This is a pointer to the null-terminated string you want to search.

Return Value

  • If the character ch is not found in the string, std::strrchr returns a null pointer (nullptr).
  • If the character ch is found in the string str, std::strrchr returns a pointer to the last occurrence of that character within the string.

Important Points

  • std::strrchr is included in the <cstring> header file.
  • The function performs a linear search through the string, starting from the end and going towards the beginning. This means it finds the last occurrence, not the first.
  • std::strrchr considers the null terminator (\0) as part of the string. So, you can search for the null character itself.

Example

#include <iostream>
#include <cstring>

int main() {
  const char* str = "Hello, world!";
  char ch = 'o';

  const char* last_occurrence = std::strrchr(str, ch);

  if (last_occurrence != nullptr) {
    std::cout << "The last occurrence of '" << ch << "' is: " << last_occurrence << std::endl;
  } else {
    std::cout << "Character '" << ch << "' not found in the string." << std::endl;
  }

  return 0;
}

This code will output:

The last occurrence of 'o' is: world!


Extracting Filename from Path

This code snippet extracts the filename (including extension) from a path string:

#include <iostream>
#include <cstring>

int main() {
  const char* filePath = "dir1/dir2/filename.txt";

  const char* separator = std::strrchr(filePath, '/');

  if (separator) {
    // Increment the pointer to skip the separator itself
    ++separator;
    std::cout << "Extracted filename: " << separator << std::endl;
  } else {
    std::cout << "No separator found in the path." << std::endl;
  }

  return 0;
}
  • If a separator is found (separator won't be null), we increment the pointer by one to skip the '/' and point to the actual filename.
  • We use std::strrchr to find the last occurrence of the '/' character in the filePath.

Checking for File Extension

This code checks if a filename has a specific extension:

#include <iostream>
#include <cstring>

int main() {
  const char* filename = "image.jpg";
  const char* extension = ".jpg";

  const char* ext_ptr = std::strrchr(filename, '.');

  if (ext_ptr && !std::strcmp(ext_ptr, extension)) {
    std::cout << filename << " has the extension: " << extension << std::endl;
  } else {
    std::cout << filename << " doesn't have the extension: " << extension << std::endl;
  }

  return 0;
}
  • If a dot is found (ext_ptr won't be null), we compare the string from that point onwards with the expected extension using std::strcmp.
  • We use std::strrchr to find the last occurrence of '.' in the filename.

Finding Last Occurrence of Substring (C++11 and later)

While std::strrchr works for single characters, for finding the last occurrence of a substring in C++11 and later, you can use the std::string::rfind member function:

#include <iostream>
#include <string>

int main() {
  std::string str = "This is a test string";
  std::string subStr = "st";

  std::size_t pos = str.rfind(subStr);

  if (pos != std::string::npos) {
    std::cout << "Last occurrence of '" << subStr << "' found at position: " << pos << std::endl;
  } else {
    std::cout << "Substring '" << subStr << "' not found in the string." << std::endl;
  }

  return 0;
}
  • std::string::npos is a special value indicating the substring wasn't found.
  • rfind searches for the last occurrence of the substring subStr within the string str.
  • This code uses the std::string class and its rfind member function.


std::find with Iterators (Modern C++)

If you're working with modern C++ and prefer iterators, you can use std::find with iterators pointing to the beginning and end of the string. This approach works for both C-style strings and std::string objects.

#include <algorithm>
#include <iterator>
#include <cstring>

const char* str = "Hello, world!";
char ch = 'o';

auto it = std::find_last(std::begin(str), std::end(str), ch);

if (it != std::end(str)) {
  // Use iterator to access the character
  std::cout << "Last occurrence of '" << ch << "' is: " << *it << std::endl;
} else {
  // Character not found
}

std::string::rfind (C++11 and later)

For std::string objects specifically (C++11 and later), you can use the rfind member function. This offers a more type-safe and potentially more readable approach compared to std::strrchr.

#include <iostream>
#include <string>

std::string str = "This is a test string";
std::string subStr = "st";

std::size_t pos = str.rfind(subStr);

if (pos != std::string::npos) {
  std::cout << "Last occurrence of '" << subStr << "' found at position: " << pos << std::endl;
} else {
  // Substring not found
}

std::char_traits<char>::find (Less Common)

This approach might be less common but offers a lower-level, more versatile option. It works with any character type and provides more control over the search process.

#include <iostream>
#include <type_traits>
#include <cstring>

const char* str = "Hello, world!";
char ch = 'o';

const char* last_occurrence = std::char_traits<char>::find(str, std::strlen(str), ch);

if (last_occurrence) {
  std::cout << "The last occurrence of '" << ch << "' is: " << last_occurrence << std::endl;
} else {
  // Character not found
}
  • If you need more control and work with various character types, std::char_traits<char>::find can be considered, but it's less common.
  • For std::string objects specifically, std::string::rfind is a type-safe and readable option.
  • If you're working with modern C++ and prefer iterators, std::find with iterators is a good choice.