Modern Approaches to Check String Prefix and Convert Substring in C++

Nov 14, 2025 · Programming · 10 views · 7.8

Keywords: C++ | String Processing | Prefix Check | Substring Conversion | Command-Line Parsing

Abstract: This article provides an in-depth exploration of various methods to check if a std::string starts with a specific prefix and convert the subsequent substring to an integer in C++. It focuses on the C++20 introduced starts_with member function while also covering traditional approaches using rfind and compare. Through detailed code examples, the article compares performance and applicability across different scenarios, addressing error handling and edge cases essential for practical development in tasks like command-line argument parsing.

Introduction

In C++ programming, when handling command-line arguments or configuration files, it is often necessary to check if a string starts with a specific prefix and extract numerical values that follow. This pattern can be concisely implemented in Python using the startswith method and slicing operations, but requires more detailed handling in C++. Based on high-scoring Stack Overflow answers, this article systematically introduces several mainstream implementation approaches.

Modern Solution with C++20

The C++20 standard introduced the starts_with member function for std::string and std::string_view, making prefix checks extremely straightforward. The following code demonstrates the complete workflow:

#include <string>
#include <iostream>

int main(int argc, char* argv[]) {
    if (argc > 1) {
        std::string arg = argv[1];
        std::string prefix = "--foo=";
        
        if (arg.starts_with(prefix)) {
            try {
                std::string value_str = arg.substr(prefix.size());
                int foo_value = std::stoi(value_str);
                std::cout << "Extracted value: " << foo_value << std::endl;
            } catch (const std::invalid_argument& e) {
                std::cerr << "Invalid number format" << std::endl;
            } catch (const std::out_of_range& e) {
                std::cerr << "Number out of range" << std::endl;
            }
        }
    }
    return 0;
}

This approach results in clear code that directly expresses programming intent. Note that when starts_with accepts string literals, the C++14 introduced user-defined literal "s" ensures type safety: arg.starts_with("--foo="s).

Traditional Approach Using rfind

Prior to C++20, prefix checking could be achieved using an overloaded version of the rfind function. The key technique is to set the search position parameter to 0:

std::string s = "tititoto";
if (s.rfind("titi", 0) == 0) {
    // String starts with "titi"
}

The principle behind this method is that when the pos parameter is 0, rfind only matches at the start of the string or earlier. Since no characters exist before the start of the string, it effectively checks only the prefix. Contrary to common misconceptions, this does not search the entire string but limits the search range to before the specified position.

The following examples further illustrate the behavior of rfind:

std::string test = "0123123";
size_t match1 = test.rfind("123");    // Returns 4 (rightmost match)
size_t match2 = test.rfind("123", 2); // Returns 1 (skips later matches)
size_t match3 = test.rfind("123", 0); // Returns std::string::npos (not found)

Alternative Approach with compare

Another traditional method uses the compare member function:

std::string prefix("--foo=");
if (arg.compare(0, prefix.size(), prefix) == 0) {
    int foo_value = std::stoi(arg.substr(prefix.size()));
}

This method precisely controls the comparison range by specifying the start position and length. compare returns 0 on successful match, offering clearer semantics compared to the rfind approach.

C-Style String Handling

For scenarios requiring interaction with C code, C standard library functions can be used:

#include <cstring>

if (strncmp(str.c_str(), substr.c_str(), substr.size()) == 0) {
    // Prefix match successful
}

strncmp compares the first n characters of two strings, returning 0 for exact matches. Note that this method requires null-terminated strings, which the std::string::c_str() method provides.

Error Handling and Edge Cases

In practical applications, various edge cases and error handling must be considered:

std::string extract_foo_value(const std::string& arg) {
    const std::string prefix = "--foo=";
    
    if (arg.length() <= prefix.length()) {
        throw std::invalid_argument("Argument too short");
    }
    
    if (!arg.starts_with(prefix)) {
        throw std::invalid_argument("Invalid prefix");
    }
    
    std::string value_part = arg.substr(prefix.length());
    if (value_part.empty()) {
        throw std::invalid_argument("Missing value after prefix");
    }
    
    return value_part;
}

int parse_foo_value(const std::string& arg) {
    std::string value_str = extract_foo_value(arg);
    
    try {
        return std::stoi(value_str);
    } catch (const std::exception& e) {
        throw std::invalid_argument("Invalid integer value: " + value_str);
    }
}

This layered error handling ensures code robustness by separately addressing cases such as arguments that are too short, mismatched prefixes, missing values, and format errors.

Performance Comparison and Selection Advice

From a performance perspective, each method performs differently across various scenarios:

For new projects, strongly prefer C++20's starts_with. When maintaining legacy code, choose between rfind or compare based on existing code style.

Practical Application Example

The following complete example demonstrates how to apply these techniques in a command-line tool:

#include <string>
#include <iostream>
#include <map>

class CommandLineParser {
private:
    std::map<std::string, int> options;
    
public:
    void parse_argument(const std::string& arg) {
        const std::string foo_prefix = "--foo=";
        const std::string bar_prefix = "--bar=";
        
        if (arg.starts_with(foo_prefix)) {
            options["foo"] = std::stoi(arg.substr(foo_prefix.length()));
        } else if (arg.starts_with(bar_prefix)) {
            options["bar"] = std::stoi(arg.substr(bar_prefix.length()));
        }
    }
    
    int get_option(const std::string& name) const {
        auto it = options.find(name);
        return (it != options.end()) ? it->second : -1;
    }
};

int main(int argc, char* argv[]) {
    CommandLineParser parser;
    
    for (int i = 1; i < argc; ++i) {
        try {
            parser.parse_argument(argv[i]);
        } catch (const std::exception& e) {
            std::cerr << "Error parsing argument " << argv[i] 
                      << ": " << e.what() << std::endl;
        }
    }
    
    std::cout << "Foo value: " << parser.get_option("foo") << std::endl;
    std::cout << "Bar value: " << parser.get_option("bar") << std::endl;
    
    return 0;
}

This example shows how to build a robust command-line argument parser that correctly handles multiple options and error conditions.

Conclusion

C++ offers multiple methods for checking string prefixes and converting substrings, ranging from traditional rfind and compare to modern starts_with. Selecting the appropriate method requires considering code clarity, performance requirements, and the target C++ standard version. In practical development, combining these with proper error handling mechanisms enables the construction of efficient and robust string processing logic.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.