Tìm hiểu xem chuỗi kết thúc bằng một chuỗi khác trong C ++


270

Làm thế nào tôi có thể tìm ra nếu một chuỗi kết thúc bằng một chuỗi khác trong C ++?

Câu trả lời:


211

Chỉ cần so sánh n ký tự cuối cùng bằng cách sử dụng std::string::compare:

#include <iostream>

bool hasEnding (std::string const &fullString, std::string const &ending) {
    if (fullString.length() >= ending.length()) {
        return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending));
    } else {
        return false;
    }
}

int main () {
    std::string test1 = "binary";
    std::string test2 = "unary";
    std::string test3 = "tertiary";
    std::string test4 = "ry";
    std::string ending = "nary";

    std::cout << hasEnding (test1, ending) << std::endl;
    std::cout << hasEnding (test2, ending) << std::endl;
    std::cout << hasEnding (test3, ending) << std::endl;
    std::cout << hasEnding (test4, ending) << std::endl;

    return 0;
}

Vâng, đây là cách tốt nhất để làm điều đó, không nghi ngờ gì.
Noldorin

3
Tôi luôn ghét tính toán các chỉ số của các chuỗi con, nó rất dễ bị sai lệch ... Tôi thay vì lặp đi lặp lại từ cuối của cả hai chuỗi, cố gắng tìm một sự không phù hợp.
xtofl

17
@Noldorin Tôi không đồng ý. Đây là một việc không có trí tuệ - cách tốt nhất để làm điều đó là sử dụng thư viện. Thật xấu hổ khi thư viện C ++ Standard thực hiện quá ít thứ hữu ích.
masterxilo

1
@masterxilo Thư viện nào bạn đề xuất để giải quyết vấn đề này và làm thế nào thư viện đó là lựa chọn tốt hơn so với chức năng một dòng (về cơ bản)?
Brandin

33
@Brandin Bởi vì đó là một chức năng cơ bản như vậy. C ++ buộc chúng ta phải lập trình lại nhiều lần các chức năng tương tự được cung cấp ngoài luồng trong bất kỳ ngôn ngữ máy tính hiện đại nào khác. Thực tế là mọi người cần phải đi đến stackoverflow để giải quyết câu hỏi này cho thấy có một pb.
Conliclicultor

175

Sử dụng chức năng này:

inline bool ends_with(std::string const & value, std::string const & ending)
{
    if (ending.size() > value.size()) return false;
    return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
}

3
Xin lưu ý rằng MSVC10 không thích giải pháp này: std::equal(suffix.rbegin(), suffix.rend(), str.rbegin()Trong chế độ gỡ lỗi, nó sẽ ném:_DEBUG_ERROR("string iterator not decrementable");
remi.chateauneu

154

Sử dụng boost::algorithm::ends_with(xem ví dụ: http://www.boost.org/doc/libs/1_34_0/doc/html/boost/alerskym/ends_with.html ):

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

// works with const char* 
assert(boost::algorithm::ends_with("mystring", "ing"));

// also works with std::string
std::string haystack("mystring");
std::string needle("ing");
assert(boost::algorithm::ends_with(haystack, needle));

std::string haystack2("ng");
assert(! boost::algorithm::ends_with(haystack2, needle));

83

Lưu ý, bắt đầu từ c ++ 20 std :: string cuối cùng sẽ cung cấp started_withend_with . Có vẻ như có khả năng bởi c ++ 30 chuỗi trong c ++ cuối cùng có thể trở nên có thể sử dụng được, nếu bạn không đọc được điều này từ tương lai xa, bạn có thể sử dụng các startWith / endWith:

#include <string>

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

static bool startsWith(const std::string& str, const std::string& prefix)
{
    return str.size() >= prefix.size() && 0 == str.compare(0, prefix.size(), prefix);
}

và một số quá tải trợ giúp thêm:

static bool endsWith(const std::string& str, const char* suffix, unsigned suffixLen)
{
    return str.size() >= suffixLen && 0 == str.compare(str.size()-suffixLen, suffixLen, suffix, suffixLen);
}

static bool endsWith(const std::string& str, const char* suffix)
{
    return endsWith(str, suffix, std::string::traits_type::length(suffix));
}

static bool startsWith(const std::string& str, const char* prefix, unsigned prefixLen)
{
    return str.size() >= prefixLen && 0 == str.compare(0, prefixLen, prefix, prefixLen);
}

static bool startsWith(const std::string& str, const char* prefix)
{
    return startsWith(str, prefix, std::string::traits_type::length(prefix));
}

Các chuỗi IMO, c ++ rõ ràng là không hoạt động và không được sử dụng trong mã thế giới thực. Nhưng có một hy vọng rằng điều này sẽ trở nên tốt hơn ít nhất.


2
Vì str.compare không trả về boolean, nên sẽ không thông minh khi kiểm tra "== 0" bằng cách sử dụng toán tử không ("!"), Vì điều đó có thể gây nhầm lẫn cho độc giả. Vui lòng sử dụng "... && str.compare (...) == 0" để rõ ràng.
Thomas Tempelmann

@Pavel Có lý do để không sử dụng std :: string :: find trong các phương thức "startedWith" của bạn không?
Maxime Oudot

4
@MaximeOudot Tất nhiên là có! Tại sao bạn muốn tìm kiếm toàn bộ chuỗi nếu bạn cần biết nếu nó bắt đầu bằng một cái gì đó? Nói cách khác, cuối cùng bạn có thể tìm kiếm chuỗi dài 100mb để tìm đoạn cuối và sau đó bỏ qua kết quả đó vì nó không nằm ở đầu chuỗi.
Pavel P

1
Cộng "1" cho dự đoán c ++ 30.
Bystander vô tội

40

Tôi biết câu hỏi dành cho C ++, nhưng nếu có ai cần chức năng C thời trang tốt để làm điều này:


/*  returns 1 iff str ends with suffix  */
int str_ends_with(const char * str, const char * suffix) {

  if( str == NULL || suffix == NULL )
    return 0;

  size_t str_len = strlen(str);
  size_t suffix_len = strlen(suffix);

  if(suffix_len > str_len)
    return 0;

  return 0 == strncmp( str + str_len - suffix_len, suffix, suffix_len );
}


25

Các std::mismatchphương pháp có thể phục vụ mục đích này khi được sử dụng để ngược lặp từ ngày kết thúc cả chuỗi:

const string sNoFruit = "ThisOneEndsOnNothingMuchFruitLike";
const string sOrange = "ThisOneEndsOnOrange";

const string sPattern = "Orange";

assert( mismatch( sPattern.rbegin(), sPattern.rend(), sNoFruit.rbegin() )
          .first != sPattern.rend() );

assert( mismatch( sPattern.rbegin(), sPattern.rend(), sOrange.rbegin() )
          .first == sPattern.rend() );

3
+1. Tôi chưa bao giờ nhận thấy std :: mismatch () trước đây - Tôi tự hỏi có gì khác trong tệp tiêu đề thuật toán mà tôi chưa bao giờ nhìn vào ...
j_random_hacker

3
Tôi nghĩ rằng đó là giá trị của một câu hỏi SO trên chính nó: bạn đã bao giờ duyệt qua các chức năng stl có sẵn?
xtofl

2
Lưu ý rằng điều này có cùng yêu cầu như std::equal: bạn cần kiểm tra trước rằng hậu tố được cho là không dài hơn chuỗi bạn đang tìm kiếm. Bỏ qua để kiểm tra dẫn đến hành vi không xác định.
Rob Kennedy

18

Theo tôi đơn giản nhất, giải pháp C ++ là:

bool endsWith(const string& s, const string& suffix)
{
    return s.rfind(suffix) == std::abs(s.size()-suffix.size());
}

10
Điều này khá chậm vì bạn sẽ tìm kiếm toàn bộ chuỗi sthay vì chỉ kiểm tra kết thúc của chuỗi!
Alexis Wilke 7/12/13

2
@nodakai, nếu tôi có chuỗi 1Mb, nó sẽ dài hơn nhiều so với nano giây.
Alexis Wilke

Tôi không nghĩ vì thế mà nó cần phải thực hiện strlen trong mọi trường hợp, và sau đó bắt đầu tìm kiếm từ cuối.
LtWorf

2
@LtWorf std::string::size()là một hoạt động liên tục; nó không cần strlen.
Thomas

2
Làm thế nào có thể được coi là giải pháp này khi nó không thành công trong trường hợp khi hậu tố.size () == s.size () + 1. Đoạn mã hiển thị trực tuyến nàygdb.com/S1ITVqKDL . Sự phức tạp là không liên quan nếu nó không hoạt động đúng cho tất cả các trường hợp.
c0ntrol

10

Hãy alà một chuỗi và bchuỗi bạn tìm kiếm. Sử dụng a.substrđể lấy n ký tự cuối cùng avà so sánh chúng với b (trong đó n là độ dài của b)

Hoặc sử dụng std::equal(bao gồm <algorithm>)

Ví dụ:

bool EndsWith(const string& a, const string& b) {
    if (b.size() > a.size()) return false;
    return std::equal(a.begin() + a.size() - b.size(), a.end(), b.begin());
}

Làm thế nào tôi có thể trả về true nếu nó kết thúc sau chuỗi của tôi bằng \ r hoặc \ n hoặc cả hai ??? cảm ơn!
sofr

@Dario: Giải pháp của bạn sử dụng std :: bằng () là tốt, giải pháp sử dụng chất nền () không quá nhiều - trừ khi bạn đang sử dụng chuỗi COW (và tôi tin rằng ít người), chất nền () ngụ ý tạo bản sao thứ hai của một phần của chuỗi, ngụ ý phân bổ bộ nhớ động có liên quan. Điều này có thể thất bại và trong mọi trường hợp có nghĩa là bộ nhớ được sử dụng nhiều hơn các giải pháp khác (và nó gần như chắc chắn chậm hơn các giải pháp khác).
j_random_hacker

4

Hãy để tôi mở rộng giải pháp của Joseph với phiên bản không nhạy cảm trường hợp ( bản demo trực tuyến )

static bool EndsWithCaseInsensitive(const std::string& value, const std::string& ending) {
    if (ending.size() > value.size()) {
        return false;
    }
    return std::equal(ending.rbegin(), ending.rend(), value.rbegin(),
        [](const char a, const char b) {
            return tolower(a) == tolower(b);
        }
    );
}

3

giống như trên, đây là giải pháp của tôi

 template<typename TString>
  inline bool starts_with(const TString& str, const TString& start) {
    if (start.size() > str.size()) return false;
    return str.compare(0, start.size(), start) == 0;
  }
  template<typename TString>
  inline bool ends_with(const TString& str, const TString& end) {
    if (end.size() > str.size()) return false;
    return std::equal(end.rbegin(), end.rend(), str.rbegin());
  }

1
Tại sao starts_withsử dụng 'chuỗi :: so sánh'? Tại sao không std::equal(start.begin(), start.end(), str.begin())?
Dmytro Ovdiienko

Chỉ vì started_with là người đầu tiên tôi cần. end_with đã được thêm vào sau.
dodjango

3

tùy chọn khác là sử dụng regex. Đoạn mã sau làm cho tìm kiếm không nhạy cảm với chữ hoa / thường:

bool endsWithIgnoreCase(const std::string& str, const std::string& suffix) {
  return std::regex_search(str,
     std::regex(std::string(suffix) + "$", std::regex_constants::icase));
}

có lẽ không hiệu quả, nhưng dễ thực hiện.


Đối với bất kỳ ai có C ++ 11 trở lên, điều này rất thuận tiện.
Clare Macrae

Coi chừng, regexes có thể cực kỳ chậm trong C ++!
mxmlnkn

regex cho điều này giống như ... tôi cần phải hạ cấp nó. Tôi sẽ không nhưng tôi nên.
MK.

2

bạn có thể sử dụng chuỗi :: rfind

Ví dụ đầy đủ dựa trên ý kiến:

bool EndsWith(string &str, string& key)
{
size_t keylen = key.length();
size_t strlen = str.length();

if(keylen =< strlen)
    return string::npos != str.rfind(key,strlen - keylen, keylen);
else return false;
}

3
-1. Có, bạn có thể sử dụng nó, nhưng nó chậm một cách không cần thiết trong trường hợp chuỗi không kết thúc với kết thúc được cung cấp - quá trình quét sẽ tiếp tục quay trở lại bắt đầu chuỗi. Ngoài ra, bạn không đề cập rằng bạn cần một bài kiểm tra tiếp theo để đảm bảo rằng kết thúc khớp với phần cuối của chuỗi , chứ không phải ở nơi khác trong chuỗi.
j_random_hacker

Tôi chỉ đặt liên kết của hàm cần thiết và tôi nghĩ rằng nó rất dễ thực hiện từ tài liệu str.rfind (key, str.length () - key.length (), key.length ());
Ahmed nói

OK, đó là hiệu quả - nhưng trong trường hợp đó chuỗi :: find () cũng sẽ hoạt động tốt. Ngoài ra, bạn cần đề cập đến trường hợp key.length ()> str.length () - mã bạn đề xuất trong nhận xét của bạn sẽ bị sập trong trường hợp này. Nếu bạn cập nhật câu trả lời của mình với thông tin này, tôi sẽ giảm -1.
j_random_hacker

2

Kiểm tra xem strhậu tố không , sử dụng bên dưới:

/*
Check string is end with extension/suffix
*/
int strEndWith(char* str, const char* suffix)
{
  size_t strLen = strlen(str);
  size_t suffixLen = strlen(suffix);
  if (suffixLen <= strLen) {
    return strncmp(str + strLen - suffixLen, suffix, suffixLen) == 0;
  }
  return 0;
}

2

Sử dụng thuật toán std :: bằng <algorithms>với lặp lại:

std::string LogExt = ".log";
if (std::equal(LogExt.rbegin(), LogExt.rend(), filename.rbegin())) {
   
}

2
Mặc dù mã này có thể cung cấp một giải pháp cho câu hỏi, tốt hơn hết là thêm ngữ cảnh vào lý do tại sao / cách thức hoạt động của nó. Điều này có thể giúp người dùng trong tương lai tìm hiểu và áp dụng kiến ​​thức đó vào mã của riêng họ. Bạn cũng có thể có phản hồi tích cực từ người dùng dưới dạng upvote, khi mã được giải thích.
borchvm

@borchvm, thêm một số giải thích, hy vọng nó sẽ giúp hiểu
Sergei

1

Về phản ứng của Grzegorz Bazior. Tôi đã sử dụng triển khai này, nhưng bản gốc có lỗi (trả về true nếu tôi so sánh ".." với ".so"). Tôi đề xuất chức năng sửa đổi:

bool endsWith(const string& s, const string& suffix)
{
    return s.size() >= suffix.size() && s.rfind(suffix) == (s.size()-suffix.size());
}

1

Tôi nghĩ thật hợp lý khi đăng một giải pháp thô không sử dụng bất kỳ chức năng thư viện nào ...

// Checks whether `str' ends with `suffix'
bool endsWith(const std::string& str, const std::string& suffix) {
    if (&suffix == &str) return true; // str and suffix are the same string
    if (suffix.length() > str.length()) return false;
    size_t delta = str.length() - suffix.length();
    for (size_t i = 0; i < suffix.length(); ++i) {
        if (suffix[i] != str[delta + i]) return false;
    }
    return true;
}

Thêm một đơn giản std::tolowerchúng ta có thể làm cho trường hợp này không nhạy cảm

// Checks whether `str' ends with `suffix' ignoring case
bool endsWithIgnoreCase(const std::string& str, const std::string& suffix) {
    if (&suffix == &str) return true; // str and suffix are the same string
    if (suffix.length() > str.length()) return false;
    size_t delta = str.length() - suffix.length();
    for (size_t i = 0; i < suffix.length(); ++i) {
        if (std::tolower(suffix[i]) != std::tolower(str[delta + i])) return false;
    }
    return true;
}

cảm ơn vì đã thêm cái này giải pháp ánh sáng luôn tuyệt vời
ekkis

1

Tìm thấy câu trả lời hay này cho chương trình "startWith" tương tự:

Làm cách nào để kiểm tra xem một chuỗi C ++ std :: bắt đầu bằng một chuỗi nhất định và chuyển đổi một chuỗi con thành int?

Bạn có thể áp dụng giải pháp để chỉ tìm kiếm ở vị trí cuối cùng trong chuỗi:

bool endsWith(const std::string& stack, const std::string& needle) {
    return stack.find(needle, stack.size() - needle.size()) != std::string::npos;
}

Bằng cách này bạn có thể làm cho nó ngắn, nhanh, sử dụng c ++ tiêu chuẩn và làm cho nó dễ đọc.


0

Nếu bạn giống tôi và không thích chủ nghĩa thuần túy C ++, thì đây là một bản lai tạo cũ. Có một số lợi thế khi các chuỗi nhiều hơn một số ký tự, vì hầu hết các memcmptriển khai đều so sánh các từ máy khi có thể.

Bạn cần kiểm soát bộ ký tự. Ví dụ: nếu cách tiếp cận này được sử dụng với loại utf-8 hoặc wchar, sẽ có một số bất lợi vì nó không hỗ trợ ánh xạ ký tự - ví dụ: khi hai hoặc nhiều ký tự giống nhau về mặt logic .

bool starts_with(std::string const & value, std::string const & prefix)
{
    size_t valueSize = value.size();
    size_t prefixSize = prefix.size();

    if (prefixSize > valueSize)
    {
        return false;
    }

    return memcmp(value.data(), prefix.data(), prefixSize) == 0;
}


bool ends_with(std::string const & value, std::string const & suffix)
{
    size_t valueSize = value.size();
    size_t suffixSize = suffix.size();

    if (suffixSize > valueSize)
    {
        return false;
    }

    const char * valuePtr = value.data() + valueSize - suffixSize;

    return memcmp(valuePtr, suffix.data(), suffixSize) == 0;
}

0

Theo quan điểm của tôi:

bool endsWith(std::string str, std::string suffix)
{
   return str.find(suffix, str.size() - suffix.size()) != string::npos;
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.