文字列がC ++で別の文字列で終わっているかどうかを調べる


270

文字列が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;
}

ええ、これは間違いなくそれを行うための最良の方法です。
ノルドリン2009年

3
私は常に部分文字列のインデックスを計算するのが嫌いです。それは1つずれる傾向があります...両方の文字列の末尾から逆方向に反復して、不一致を見つけようとします。
xtofl 2009年

17
@Noldorin同意しません。これは非常に簡単です-それを行う最善の方法はライブラリを使用することです。C ++標準ライブラリが便利なことをほとんど行わないのは残念です。
masterxilo 2014

1
@masterxiloこの問題を解決するためにどのライブラリを提案しますか。そのライブラリは(基本的に)1行の関数よりもどのように選択するのがよいですか。
ブランディン2014

33
@Brandinそれはそのような基本的な機能だからです。C ++は、他の最新のコンピューター言語でそのまま使用できる同じ機能を何度も再プログラミングするように強制します。この質問を解決するために人々がstackoverflowに行く必要があるという事実は、pbがあることを示しています。
コンキリクリトール

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 std :: string から開始すると、最終的にstarts_withends_withが提供されることに注意してください。c ++ 30までにc ++の文字列が最終的に使用可能になる可能性があるようです。遠い将来からこれを読んでいない場合は、これらのstartsWith / endsWithを使用できます。

#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はブール値を返さないため、not( "!")演算子を使用して "== 0"をテストすることは、読者を混乱させる可能性があるため、それほど賢明ではありません。明確にするために "... && str.compare(...)== 0"を使用してください。
トーマステンペルマン2017

@Pavel「startsWith」メソッドでstd :: string :: findを使用しない理由はありますか?
Maxime Oudot

4
@MaximeOudotもちろんオフです!文字列が何かで始まるかどうかを知る必要があるのに、なぜ文字列全体を検索したいのですか?つまり、100 MBの長い文字列を検索して、最後の部分を見つけ、その結果が文字列の先頭にないため、その結果を無視することになります。
Pavel P

1
さらに、c ++ 30予測の場合は「1」。
イノセントバイスタンダー

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 '18年

3
私はそれだけでSOの質問に値するものだと思います:利用可能なstl関数を閲覧したことがありますか?
xtofl 2009年

2
これと同じ要件があることに注意しstd::equalてください。想定されるサフィックスが、検索対象の文字列よりも長くないことを事前に確認する必要があります。確認を怠ると、未定義の動作が発生します。
ロブ・ケネディ

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
@nodakai、たまたま1Mbの文字列がある場合、それはナノ秒よりもはるかに長くなります。
Alexis Wilke

私はそうは思いません...とにかくstrlenを実行する必要があり、それから最後から見ていきます。
LtWorf 2017年

2
@LtWorf std::string::size()は一定時間の操作です。必要ありませんstrlen
トーマス

2
suffix.size()== s.size()+ 1の場合に失敗した場合のソリューションと見なすにはどうすればよいですか。このonlinegdb.com/S1ITVqKDLを示すコードスニペット。すべてのケースで適切に機能しない場合、複雑さは関係ありません。
c0ntrol

10

ましょうa文字列となりb、あなたが探して文字列。a.substrの最後のn文字を取得し、abと比較するために使用します(nはの長さb

または使用std::equal(含む<algorithm>

例:

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または両方が付いている場合もtrueを返すにはどうすればよいですか?ありがとう!
SOFR

@Dario:std :: equal()を使用するソリューションは優れていますが、substr()を使用するソリューションはそれほどではありません-COW文字列を使用している場合(そして私が信じている人はほとんどいない)、substr()は2番目のコピーを作成することを意味します文字列の一部の動的メモリ割り当てが含まれることを意味します。これは失敗する可能性があり、どの場合でも他のソリューションよりも多くのメモリが使用されることを意味します(他のソリューションよりもほぼ確実に遅いです)。
j_random_hacker 2009年

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'string :: compare'を使用するのですか?なんでstd::equal(start.begin(), start.end(), str.begin())
Dmytro Ovdiienko 16

starts_withが最初に必要だったからです。end_withは後で追加されました。
dodjango

3

別のオプションは、正規表現を使用することです。次のコードは、検索で大文字と小文字を区別しません。

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

C ++では正規表現が非常に遅くなる可能性があることに注意してください。
mxmlnkn

これの正規表現は次のようなものです...私はこれを反対票にする必要があります。私はしませんが、すべきです。
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 2009年

必要な関数のリンクを配置するだけで、ドキュメントstr.rfind(key、str.length()-key.length()、key.length());から簡単に実行できると思います。
アーメドは

OK、それは効率的ですが、その場合、string :: find()も同様に機能します。また、key.length()> str.length()の場合についても言及する必要があります。この場合、コメントで提案するコードがクラッシュします。この情報を使用して回答を更新した場合、-1をドロップします。
j_random_hacker 2009年

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 :: equalアルゴリズム<algorithms>を逆反復で使用します。

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

2
このコードは質問に対する解決策を提供するかもしれませんが、それがなぜ/どのように機能するかに関してコンテキストを追加する方が良いです。これは、将来のユーザーが学習し、その知識を自分のコードに適用するのに役立ちます。また、コードが説明されている場合は、賛成票の形でユーザーから肯定的なフィードバックを受ける可能性があります。
borchvm

@borchvm、いくつかの説明を追加、理解するのに役立つことを願っています
セルゲイ

1

Grzegorz Baziorの対応について。この実装を使用しましたが、元の実装にはバグがあります(「..」を「.so」と比較するとtrueが返されます)。私は修正された関数を提案します:

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 :: stringが特定の文字列で始まるかどうかを確認し、部分文字列を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タイプで使用する場合、2つ以上の文字が論理的に同一である場合など、文字マッピングをサポートしないため、いくつかの欠点があります。

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

私の2セント:

bool endsWith(std::string str, std::string suffix)
{
   return str.find(suffix, str.size() - suffix.size()) != string::npos;
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.