ค้นหาว่าสตริงสิ้นสุดด้วยสตริงอื่นใน C ++ หรือไม่


คำตอบ:


211

เพียงเปรียบเทียบอักขระสุดท้ายnตัวโดยใช้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;
}

ใช่นี่เป็นวิธีที่ดีที่สุดที่จะทำโดยไม่ต้องสงสัย
Noldorin

3
ฉันเกลียดการคำนวณดัชนีของสารตั้งต้นบ่อยมากมันเป็นแบบออฟไลน์โดยง่าย ... ฉันควรทำซ้ำไปเรื่อย ๆ จากจุดสิ้นสุดของทั้งสองสายพยายามหาสิ่งที่ไม่ตรงกัน
xtofl

17
@Noldorin ฉันไม่เห็นด้วย วิธีนี้เป็นวิธีที่ดีที่สุดในการใช้ห้องสมุด น่าเสียดายที่ไลบรารี C ++ Standard ทำสิ่งที่มีประโยชน์น้อยมาก
masterxilo

1
@masterxilo ห้องสมุดใดที่คุณเสนอให้แก้ปัญหานี้และห้องสมุดนั้นเป็นตัวเลือกที่ดีกว่าฟังก์ชั่นหนึ่งบรรทัด (โดยทั่วไป) อย่างไร
Brandin

33
@Brandin เพราะมันเป็นฟังก์ชั่นพื้นฐานดังกล่าว C ++ บังคับให้เราทำการ reprogram ซ้ำแล้วซ้ำอีกฟังก์ชั่นเดียวกันซึ่งมีให้ในทันทีในภาษาคอมพิวเตอร์สมัยใหม่อื่น ๆ ความจริงที่ว่าผู้คนต้องไปที่ stackoverflow เพื่อแก้ปัญหานี้แสดงให้เห็นว่ามี pb
Conchylicultor

175

ใช้ฟังก์ชั่นนี้:

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
ระวังว่า MSVC10 ไม่ชอบวิธีนี้: std::equal(suffix.rbegin(), suffix.rend(), str.rbegin()ในโหมดดีบั๊กมันจะพ่น:_DEBUG_ERROR("string iterator not decrementable");
remi.chateauneu

154

ใช้boost::algorithm::ends_with(ดูเช่นhttp://www.boost.org/doc/libs/1_34_0/doc/html/boost/algorithm/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

ทราบว่าเริ่มต้นจากC ++ 20มาตรฐาน :: สตริงในที่สุดก็จะให้starts_withและends_with ดูเหมือนว่ามีโอกาสที่ c ++ 30 สายใน c ++ อาจใช้งานได้ในที่สุดหากคุณไม่ได้อ่านสิ่งนี้จากอนาคตอันไกลคุณสามารถใช้การเริ่มต้นเหล่านี้ด้วย / สิ้นสุดด้วย:

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

และบางตัวช่วยพิเศษทำงานเกิน:

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

IMO, สตริง c ++ นั้นผิดปกติอย่างชัดเจนและไม่ได้ถูกใช้ในรหัสโลกแห่งความจริง แต่มีความหวังว่าสิ่งนี้จะดีขึ้นอย่างน้อย


2
เนื่องจาก str.compare ไม่ส่งคืนบูลีนจึงไม่ฉลาดนักที่จะทดสอบ "== 0" โดยใช้ตัวดำเนินการ not ("!") เนื่องจากอาจทำให้ผู้อ่านสับสน โปรดใช้ "... && str.compare (... ) == 0" เพื่อความชัดเจน
Thomas Tempelmann

@Pavel มีเหตุผลที่จะไม่ใช้ std :: string :: find ในเมธอด "startsWith" ของคุณหรือไม่?
Maxime Oudot

4
@MaximeOudot ปิดหลักสูตรที่นั่น! ทำไมคุณถึงต้องการค้นหาข้อความทั้งหมดถ้าคุณต้องการรู้ว่ามันเริ่มต้นด้วยอะไร กล่าวอีกนัยหนึ่งคุณอาจค้นหาสตริงขนาดยาว 100mb เพื่อค้นหาชิ้นส่วนที่อยู่ด้านท้ายสุดแล้วละเว้นผลลัพธ์นั้นเพราะไม่อยู่ที่จุดเริ่มต้นของสตริง
Pavel P

1
บวก "1" สำหรับการคาดการณ์ c ++ 30
Bystander ผู้บริสุทธิ์

40

ฉันรู้คำถามสำหรับ C ++ แต่ถ้าใครต้องการฟังก์ชั่น C ที่ล้าสมัยดีในการทำเช่นนี้:


/*  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

std::mismatchวิธีสามารถตอบสนองวัตถุประสงค์นี้เมื่อใช้ในการถอยหลังย้ำจากจุดสิ้นสุดของทั้งสองสาย:

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 ฉันไม่เคยสังเกต std :: mismatch () มาก่อน - ฉันสงสัยว่ามีอะไรอีกบ้างในไฟล์ส่วนหัวของอัลกอริทึมที่ฉันไม่เคยดู ...
j_random_hacker

3
ฉันคิดว่านั่นเป็นคำถามที่มีค่ามากสำหรับตัวคุณเอง: คุณเคยดูผ่านฟังก์ชั่น stl หรือไม่?
xtofl

2
โปรดทราบว่าสิ่งนี้มีความต้องการเช่นเดียวกับstd::equal: คุณต้องตรวจสอบล่วงหน้าว่าส่วนต่อท้ายที่ควรจะไม่ยาวกว่าสายอักขระที่คุณกำลังค้นหาข้อควรระวัง: เพื่อตรวจสอบสิ่งที่นำไปสู่พฤติกรรมที่ไม่ได้กำหนด
Rob Kennedy

18

ในความคิดของฉันง่ายที่สุดการแก้ปัญหา C ++ คือ:

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

10
ค่อนข้างช้าเนื่องจากคุณจะค้นหาทั้งสตริงsแทนที่จะทดสอบแค่ส่วนท้าย!
Alexis Wilke

2
@ โนะดะไกถ้าฉันมีสตริง 1Mb มันจะมากกว่านาโนวินาทีมาก
Alexis Wilke

ฉันไม่คิดอย่างนั้น ... มันจำเป็นต้องทำ strlen ในกรณีใด ๆ แล้วเริ่มมองจากท้าย
LtWorf

2
@LWWorf std::string::size()เป็นการทำงานที่คงที่ strlenมันไม่จำเป็นต้อง
โทมัส

2
วิธีนี้อาจเป็นวิธีการแก้ปัญหาที่พิจารณาแม้เมื่อล้มเหลวสำหรับกรณีเมื่อ suffix.size () == s.size () + 1 ตัวอย่างรหัสแสดงonlinegdb.com/S1ITVqKDLนี้ ความซับซ้อนไม่เกี่ยวข้องถ้ามันไม่ทำงานอย่างถูกต้องสำหรับทุกกรณี
c0ntrol

10

อนุญาตaเป็นสตริงและbสตริงที่คุณค้นหา ใช้a.substrเพื่อรับอักขระสุดท้าย n aและเปรียบเทียบกับ b (โดยที่ n คือความยาวของb)

หรือใช้std::equal(รวมถึง<algorithm>)

Ex:

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

ฉันจะคืนค่าจริงได้อย่างไรถ้ามันจบลงหลังจากสตริงของฉันด้วย \ r หรือ \ n หรือทั้งสอง ??? ขอบคุณ!
sofr

@Dario: วิธีการแก้ปัญหาของคุณโดยใช้ std :: เท่ากับ () เป็นสิ่งที่ดี substr () ไม่มาก - ถ้าคุณใช้สตริง COW (และฉันเชื่อว่ามีคนไม่กี่คน) substr () หมายถึงการสร้างสำเนาที่สอง ส่วนหนึ่งของสตริงเกี่ยวข้องกับการจัดสรรหน่วยความจำแบบไดนามิกที่เกี่ยวข้อง สิ่งนี้อาจล้มเหลวและในกรณีใด ๆ หมายความว่ามีการใช้หน่วยความจำมากกว่าโซลูชันอื่น (และเกือบจะช้ากว่าโซลูชันอื่น ๆ )
j_random_hacker

4

ให้ฉันขยายโซลูชันของโจเซฟด้วยรุ่นที่ไม่คำนึงถึงขนาดตัวพิมพ์ ( การสาธิตออนไลน์ )

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

เหมือนกับข้างบนนี่คือทางออกของฉัน

 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
เหตุใดจึงstarts_withใช้ 'สตริง :: เปรียบเทียบ' ทำไมstd::equal(start.begin(), start.end(), str.begin())ล่ะ
Dmytro Ovdiienko

เพียงเพราะ starts_ ซึ่งเป็นคนแรกที่ฉันต้องการ ends_with ถูกเพิ่มในภายหลัง
dodjango

3

ตัวเลือกอื่นคือการใช้ regex รหัสต่อไปนี้ทำให้การค้นหาไม่คำนึงถึงตัวพิมพ์ใหญ่หรือตัวพิมพ์เล็ก:

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 ++ 11 ขึ้นไปจะสะดวกมาก
Clare Macrae

ระวัง regexes อาจช้าอย่างบ้าคลั่งใน C ++!
mxmlnkn

regex สำหรับสิ่งนี้เป็นเหมือน ... ฉันต้องลงคะแนนนี้ ฉันจะไม่ทำ แต่ควรทำ
MK

2

คุณสามารถใช้string :: rfind

ตัวอย่างเต็มตามความคิดเห็น:

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 ใช่คุณสามารถใช้มันได้ แต่มันช้าโดยไม่จำเป็นในกรณีที่สตริงไม่ได้จบลงด้วยการสิ้นสุดที่ให้มา - การสแกนจะดำเนินต่อไปเรื่อย ๆ จนถึงจุดเริ่มต้นของสตริง นอกจากนี้คุณไม่ต้องพูดถึงว่าคุณจำเป็นต้องมีการทดสอบครั้งต่อไปเพื่อให้แน่ใจว่าการจับคู่สิ้นสุดที่ส่วนท้ายของสตริงไม่ใช่ที่อื่นในสตริง
j_random_hacker

ฉันเพิ่งใส่ลิงค์ของฟังก์ชั่นที่ต้องการและฉันคิดว่ามันง่ายมากที่จะทำจากเอกสาร str.rfind (คีย์, str.length () - key.length (), key.length ());
Ahmed Said

ตกลงว่ามีประสิทธิภาพ - แต่ในกรณีที่สตริง :: find () จะทำงานได้เช่นกัน นอกจากนี้คุณต้องพูดถึงกรณีที่ key.length ()> str.length () - รหัสที่คุณแนะนำในความคิดเห็นของคุณจะล้มเหลวในกรณีนี้ หากคุณอัปเดตคำตอบด้วยข้อมูลนี้ฉันจะลบ -1
j_random_hacker

2

ตรวจสอบว่าstrมีคำต่อท้ายโดยใช้ด้านล่าง:

/*
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

ใช้ std :: อัลกอริทึมที่เท่ากันจาก<algorithms>ด้วยการวนซ้ำย้อนกลับ:

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

2
ในขณะที่รหัสนี้อาจให้คำตอบสำหรับคำถาม แต่ก็ควรเพิ่มบริบทว่าทำไมมันถึงทำงานอย่างไร สิ่งนี้สามารถช่วยผู้ใช้ในอนาคตเรียนรู้และนำความรู้นั้นไปใช้กับรหัสของตนเอง คุณมีแนวโน้มที่จะได้รับการตอบรับเชิงบวกจากผู้ใช้ในรูปแบบของ upvotes เมื่ออธิบายรหัส
borchvm

@ Borchvm เพิ่มคำอธิบายบางอย่างหวังว่ามันจะช่วยให้เข้าใจ
Sergei

1

เกี่ยวกับการตอบสนองของ Grzegorz Bazior ฉันใช้การใช้งานนี้ แต่ดั้งเดิมมีข้อผิดพลาด (ผลตอบแทนจริงถ้าฉันเปรียบเทียบ ".. " กับ ".so") ฉันเสนอฟังก์ชั่นการแก้ไข:

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

1

ฉันคิดว่ามันสมเหตุสมผลแล้วที่จะโพสต์คำตอบที่ไม่ใช้ฟังก์ชั่นห้องสมุด ...

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

เพิ่มง่ายstd::tolowerเราสามารถทำให้กรณีนี้ตาย

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

ขอบคุณที่เพิ่มสิ่งนี้ โซลูชันแบบแสงนั้นยอดเยี่ยมเสมอ
ekkis

1

พบคำตอบที่ดีสำหรับคำว่า "startWith" ที่คล้ายกัน - ปัญหา:

ฉันจะตรวจสอบว่าสตริง C ++ std :: เริ่มต้นด้วยสตริงที่แน่นอนและแปลงสตริงย่อยเป็น int ได้อย่างไร

คุณสามารถใช้วิธีแก้ปัญหาเพื่อค้นหาเฉพาะตำแหน่งสุดท้ายในสตริง:

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

วิธีนี้ทำให้คุณสั้นเร็วใช้มาตรฐาน c ++ และทำให้อ่านง่าย


0

หากคุณเป็นเหมือนฉันและไม่ได้เป็นคนเจ้าเล่ห์ C ++ นี่คือลูกผสม skool แบบเก่า มีข้อได้เปรียบบางอย่างเมื่อสตริงมีอักขระมากกว่าหนึ่งตัวเนื่องจากmemcmpการใช้งานส่วนใหญ่จะเปรียบเทียบคำของเครื่องเมื่อเป็นไปได้

คุณต้องควบคุมชุดตัวละคร ตัวอย่างเช่นถ้าวิธีนี้จะใช้กับ UTF-8 หรือประเภท WCHAR มีข้อเสียบางอย่างที่มันจะไม่สนับสนุนการทำแผนที่ของตัวละคร - เช่นเมื่อสองคนหรือมากกว่าตัวอักษรเหมือนกันเหตุผล

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.