2013-12-07 5 views
19

Я изучаю базовый C++, и прямо сейчас у меня есть строка от пользователя, и я хочу проверить, набрали ли они все имя файла (включая .txt) или нет. У меня есть строка, но как я могу проверить, заканчивается ли строка с «.txt»?Как проверить, заканчивается ли строка .txt

string fileName; 

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

string txt = fileName.Right(4); 

Right(int) метод работает только с CString, поэтому приведенный выше код не работает. Я хочу использовать регулярную строку, если это возможно. Есть идеи?

+1

Я подозреваю, что ваш вопрос имеет больше общего с именами файлов, чем строки, поэтому я хотел бы предложить вам искать решения, которые извлекают расширение имени файла в переносимым способом. Многие библиотеки поддерживают это. –

+0

Возможный дубликат [Найти, если строка заканчивается другой строкой в ​​C++] (http://stackoverflow.com/questions/874134/find-if-string-endswith-another-string-in-c) –

ответ

36

К сожалению, эта полезная функция не в стандартной библиотеке. Легко писать.

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; 
} 
+0

Спасибо. Но теперь мне интересно, почему вы передали переданный параметр в константу? bool has_suffix (std :: string str, std :: string suffix) {} работает также. – Chronicle

+3

@Chronicle: Это более общий вопрос о «зачем использовать const». Спецификатор 'const' - это обещание, выполняемое компилятором, что код в функции не будет изменять строку. Так как функция не изменяет строку, вы также можете сделать это обещание - и тогда функция может быть передана const-строками без необходимости их скопировать. Если вы опустите const, вам может потребоваться скопировать строку, прежде чем вы сможете передать ее функции. –

+1

@Chronicle: быстрый поиск показал хороший материал, с ответом на Lightness Races на орбите: http://stackoverflow.com/questions/18157523/why-would-you-use-the-keyword-const-if-you -already-know-variable-should-be-const –

11

Использование BOOST ends_with предиката:

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

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

Это мой любимый. Может использовать boost :: iends_with (...) для сравнения без учета регистра. –

2

Самый простой подход, вероятно, чтобы проверить, что строка достаточно долго, чтобы держать ".txt" на всех, и чтобы увидеть, если строка может быть найдена в положении size() - 4, например:

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; 
} 
+0

Я не уверен, что это пример, который будет показан в книге, нажимая C++. Не ваша ошибка, конечно ... – 6502

6

Использование std::string::substr

if (filename.substr(std::max(4, filename.size())-4) == std::string(".txt")) { 
    // Your code here 
} 
+1

Это может вызвать проблемы, если 'filename.size() <4'. –

+0

@DietrichEpp Спасибо, отредактировано – Erbureth

+1

Это все равно вызовет проблемы, если 'filename.size() <4'. Функция 'std :: max()' не будет работать так, как вы ожидаете здесь. –

2
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); 
} 
3

вы можете просто у себе другая строка, чтобы проверить расширение, как это:

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"; 

проверки, если равна "txt." причины я начинается с длиной файла, так ext заполняется в обратном порядке

0

Вот это «полностью самостоятельно написано "решение:

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; 
} 
+0

+1, но зачем использовать указатели вместо того, чтобы просто использовать 'pstr [- tlen]! = substr [- slen] 'в тесте? – 6502

+1

@ 6502 ну, я думаю, что решение Дитриха замечательно, но я написал это, когда я был на первом курсе, и до сих пор использую его в своем моторе до сегодняшнего дня :) – Netherwire

+0

У каждого плохой код, лежащий вокруг, что было написано, когда младше. Но зачем цепляться за него (если только вас не принуждают)? – 6502

2

Это то, что, к сожалению, достаточно, нет в стандартной библиотеке, и это также несколько раздражает писать. Это моя попытка:

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; 
} 
11

Вы получили довольно много ответов уже, но я решил добавить еще один:

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; 
} 

Поскольку вы получили довольно много ответов, возможно, быстрый тест и обобщение результатов было бы целесообразно:

#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); 
} 

Результаты с НКУ:

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 

Результаты с 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 

Да, это были работают на идентичном оборудовании, да и я побежал их несколько раз, и пробовали различные варианты оптимизации с г ++, чтобы увидеть, если я мог бы получить его, по крайней мере, приходят вроде близко к VC++. Я не мог. У меня нет прямого объяснения того, почему g ++ производит гораздо худший код для этого теста, но я уверен, что он это делает.

+0

do me, do me, do me ...: P, что в стороне g ++ имеет crappy string implementation в прошлый раз, когда я читал вещи в Интернете;) aka no SSO http://herbsutter.com/2013/05/13/gotw- 2-solution-objects-objects/ – NoSenseEtAl

+1

@NoSenseEtAl: Эти результаты, несомненно, будут поддерживать это. –

+1

Ничего себе, хорошая работа! – Netherwire

2

2 варианта я могу думать кроме упомянутых них:
1) регулярное выражение - Проб излишеством для этого, но простые регулярные выражения хороши и читаемым IMHO
2) rbegin - вид хороший, может быть я что-то не хватает, но вот это:

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

+0

Чистое решение до сих пор – Slava

Смежные вопросы