简体   繁体   中英

How to check if string ends with .txt

I am learning basic C++, and right now I have gotten a string from a user and I want to check if they typed the entire file name (including .txt) or not. I have the string, but how can I check if the string ends with ".txt" ?

string fileName;

cout << "Enter filename: \n";
cin >> fileName;

string txt = fileName.Right(4);

The Right(int) method only works with CString, so the above code does not work. I want to use a regular string, if possible. Any ideas?

Unfortunately this useful function is not in the standard library. It is easy to write.

bool has_suffix(const std::string &str, const std::string &suffix)
{
    return str.size() >= suffix.size() &&
           str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
}

Using boost ends_with predicate:

#include <boost/algorithm/string/predicate.hpp>

if (boost::ends_with(fileName, ".txt")) { /* ... */ }

You've gotten quite a few answers already, but I decided to add yet another:

bool ends_with(std::string const &a, std::string const &b) {
    auto len = b.length();
    auto pos = a.length() - len;
    if (pos < 0)
        return false;
    auto pos_a = &a[pos];
    auto pos_b = &b[0];
    while (*pos_a)
        if (*pos_a++ != *pos_b++)
            return false;
    return true;
}

Since you have gotten quite a few answers, perhaps a quick test and summary of results would be worthwhile:

#include <iostream>
#include <string>
#include <vector>
#include <time.h>
#include <iomanip>

bool ends_with(std::string const &a, std::string const &b) {
    auto len = b.length();
    auto pos = a.length() - len;
    if (pos < 0)
        return false;
    auto pos_a = &a[pos];
    auto pos_b = &b[0];
    while (*pos_a)
        if (*pos_a++ != *pos_b++)
            return false;
    return true;
}

bool ends_with_string(std::string const& str, std::string const& what) {
    return what.size() <= str.size()
        && str.find(what, str.size() - what.size()) != str.npos;
}

bool has_suffix(const std::string &str, const std::string &suffix)
{
    return str.size() >= suffix.size() &&
        str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
}

bool has_suffix2(const std::string &str, const std::string &suffix)
{
    bool index = str.find(suffix, str.size() - suffix.size());
    return (index != -1);
}

bool isEndsWith(const std::string& pstr, const std::string& substr)
{
    int tlen = pstr.length();
    int slen = substr.length();

    if (slen > tlen)
        return false;

    const char* tdta = pstr.c_str();
    const char* sdta = substr.c_str();

    while (slen)
    {
        if (tdta[tlen] != sdta[slen])
            return false;

        --slen; --tlen;
    }
    return true;
}

bool ends_with_6502(const std::string& str, const std::string& end) {
    size_t slen = str.size(), elen = end.size();
    if (slen <= elen) return false;
    while (elen) {
        if (str[--slen] != end[--elen]) return false;
    }
    return true;
}

bool ends_with_rajenpandit(std::string const &file, std::string const &suffix) {
    int pos = file.find(suffix);
    return (pos != std::string::npos);
}

template <class F>
bool test(std::string const &label, F f) {
    static const std::vector<std::pair<std::string, bool>> tests{
        { "this is some text", false },
        { "name.txt.other", false },
        { "name.txt", true }
    };
    bool result = true;

    std::cout << "Testing: " << std::left << std::setw(20) << label;
    for (auto const &s : tests)
        result &= (f(s.first, ".txt") == s.second);
    if (!result) {
        std::cout << "Failed\n";
        return false;
    }
    clock_t start = clock();
    for (int i = 0; i < 10000000; i++)
        for (auto const &s : tests)
            result &= (f(s.first, ".txt") == s.second);
    clock_t stop = clock();
    std::cout << double(stop - start) / CLOCKS_PER_SEC << " Seconds\n";
    return result;
}

int main() {
    test("Jerry Coffin", ends_with);
    test("Dietrich Epp", has_suffix);
    test("Dietmar", ends_with_string);
    test("Roman", isEndsWith);
    test("6502", ends_with_6502);
    test("rajenpandit", ends_with_rajenpandit);
}

Results with gcc:

Testing: Jerry Coffin           3.416 Seconds
Testing: Dietrich Epp           3.461 Seconds
Testing: Dietmar                3.695 Seconds
Testing: Roman                  3.333 Seconds
Testing: 6502                   3.304 Seconds
Testing: rajenpandit            Failed

Results with VC++:

Testing: Jerry Coffin           0.718 Seconds
Testing: Dietrich Epp           0.982 Seconds
Testing: Dietmar                1.087 Seconds
Testing: Roman                  0.883 Seconds
Testing: 6502                   0.927 Seconds
Testing: rajenpandit            Failed

Yes, those were run on identical hardware, and yes I ran them a number of times, and tried different optimization options with g++ to see if I could get it to at least come sort of close to matching VC++. I couldn't. I don't have an immediate explanation of why g++ produces so much worse code for this test, but I'm fairly confident that it does.

Use std::string::substr

if (filename.substr(std::max(4, filename.size())-4) == std::string(".txt")) {
    // Your code here
}
bool has_suffix(const std::string &str, const std::string &suffix)
{
    std::size_t index = str.find(suffix, str.size() - suffix.size());
    return (index != std::string::npos);
}

you can just use another string to verify the extension like this :

string fileName;

cout << "Enter filename: \n";
cin >> fileName;

//string txt = fileName.Right(4);
string ext="";
for(int i = fileName.length()-1;i>fileName.length()-5;i--)
{
    ext += fileName[i];
}
cout<<ext;
if(ext != "txt.")
    cout<<"error\n";

checking if equals "txt." cause i starts with the length of the filename so ext is filled in the opposite way

The easiest approach is probably to verify that the string is long enough to hold ".txt" at all and to see if the string can be found at the position size() - 4 , eg:

bool ends_with_string(std::string const& str, std::string const& what) {
    return what.size() <= str.size()
        && str.find(what, str.size() - what.size()) != str.npos;
}

This is something that, unfortunately enough, is not present in the standard library and it's also somewhat annoying to write. This is my attempt:

bool ends_with(const std::string& str, const std::string& end) {
    size_t slen = str.size(), elen = end.size();
    if (slen < elen) return false;
    while (elen) {
        if (str[--slen] != end[--elen]) return false;
    }
    return true;
}

2 options I can think of beside mentioned ones:
1) regex - prob overkill for this, but simple regexes are nice and readable IMHO
2) rbegin - kind of nice, could be I am missing something, but here it is:

bool ends_with(const string& s, const string& ending)
{
return (s.size()>=ending.size()) && equal(ending.rbegin(), ending.rend(), s.rbegin());
}

http://coliru.stacked-crooked.com/a/4de3eafed3bff6e3

This should do it.

bool ends_with(const std::string & s, const std::string & suffix) {
     return s.rfind(suffix) == s.length() - suffix.length();
}

Verify here

Since C++17 you can utilize the path class from the filesystem library .

#include <filesystem>

bool ends_with_txt(const std::string& fileName) {
    return std::filesystem::path{fileName}.extension() == ".txt";
}

Here is the "fully self-written" solution:

bool isEndsWith(const std::string& pstr, const std::string& substr) const
{
    int tlen = pstr.length();
    int slen = substr.length();

    if(slen > tlen)
        return false;

    const char* tdta = pstr.c_str();
    const char* sdta = substr.c_str();

    while(slen)
    {
        if(tdta[tlen] != sdta[slen])
            return false;

        --slen; --tlen;
    }
    return true;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM