C ++ std :: stringが特定の文字列で始まるかどうかを確認し、部分文字列をintに変換するにはどうすればよいですか?


242

C ++で次の(Python疑似コード)を実装するにはどうすればよいですか?

if argv[1].startswith('--foo='):
    foo_value = int(argv[1][len('--foo='):])

(たとえば、argv[1]ある--foo=98、次にfoo_valueです98。)

更新:単純な小さなコマンドラインツールに非常に小さな変更を加えることを検討しているだけなので、Boostを調べるのをためらっています(マイナーにBoostをリンクして使用する方法を学ぶ必要はありません)変化する)。


これも面白いですね。
manlio 2015

回答:


449

パラメータrfindを持つオーバーロードを使用しposます。

std::string s = "tititoto";
if (s.rfind("titi", 0) == 0) {
  // s starts with prefix
}

他に必要なものは?純粋なSTL!

多くの人はこれを「プレフィックスを探す文字列全体を逆方向に検索する」と誤解しています。これは間違った結果をstring("tititito").rfind("titi")返し(たとえば、2を返すので、比較する== 0とfalseを返します)、効率的ではありません(開始だけではなく文字列全体を調べる)。しかし、それはposパラメータをとして渡すため、それは行われません。0検索はその位置またはそれ以前でのみ一致するように制限されます。例えば:

std::string test = "0123123";
size_t match1 = test.rfind("123");    // returns 4 (rightmost match)
size_t match2 = test.rfind("123", 2); // returns 1 (skipped over later match)
size_t match3 = test.rfind("123", 0); // returns std::string::npos (i.e. not found)

32
この回答は、最高の投票ではなく、最高の投票になるはずです。
Iuliu Atudosiei 2017

@ sweisgerber.dev、私はあなたの最初の争いで混乱しています。以下からの戻り値は、find場合にのみゼロになるtitiのである先頭の文字列の。他の場所で見つかった場合は、ゼロ以外の戻り値を取得nposします。見つからない場合は、どちらもゼロ以外の値を取得します。私が正しいと仮定すると、非標準のものを持ち込む必要がないので、この答えを好みます(そうです、Boostは至る所にあるので、このような単純なものにはコアC ++ライブラリを好みます)。
paxdiablo 2017年

@paxdiablo:そうです、実際にで始まるかどうかをチェックしますtitiが、変換部分がありません。
sweisgerber.dev 2017年

2
これがほとんどのコンパイラで最適化されているという証拠はありますか?「find」または「rfind」のいずれかの最適化が、チェック対象の戻り値に基づく一般的な慣行であることに言及している他の場所はありません。
Superziyi

2
@alcoforado "rfindは文字列の後ろから始まります..."いいえ、それrfind()posパラメーターをとらないオーバーロードにのみ適用されます。posパラメータを取るオーバーロードを使用すると、文字列全体は検索されず、その位置以前のみが検索されます。(通常のようにfind()posパラメーターを指定すると、その位置以降のみが検索されます。)したがってpos == 0、この回答に示すようにを渡すと、文字通り、その1つの位置での一致のみが考慮されます。それは回答とコメントの両方ですでに説明されていました。
アーサータッカ

188

あなたはこのようにします:

std::string prefix("--foo=");
if (!arg.compare(0, prefix.size(), prefix))
    foo_value = atoi(arg.substr(prefix.size()).c_str());

これを行うBoost.ProgramOptionsなどのlibを探すのも良い考えです。


7
これの最大の問題はがatoi("123xyz")返されることですが123、Python int("123xyz")は例外をスローします。
トム

回避策は、sscanf()を実行して結果と元の値を比較し、続行するか例外をスローするかを決定することです。
Roopesh Majeti 2009

1
それとも置き換えるatoistrtolか、strtoll私たちは、入力値にエラー条件を検出することができますされ、。
トム

1
これはrfind、最適化に依存するソリューションよりも優れたソリューションです。
カルマリウス

143

完全を期すために、Cによる方法について説明します。

場合はstr、元の文字列である、substrあなたはそれから、チェックしたいサブストリングがあります

strncmp(str, substr, strlen(substr))

で 始まる0場合は戻ります。関数とCヘッダーファイルにありますstrsubstrstrncmpstrlen<string.h>

(もともとYaseen Raufがここに投稿し、マークアップが追加されました)

大文字と小文字を区別しない比較では、のstrnicmp代わりにを使用しますstrncmp

これはCの方法です。C++文字列の場合は、次のように同じ関数を使用できます。

strncmp(str.c_str(), substr.c_str(), substr.size())

9
確かに、誰もが「ブーストを使用する」だけで、stlまたはOSライブラリのバージョンに感謝しています
Force Gaia

はい。ただし、文字列にnull文字がないことを前提としています。そうでない場合-使用する必要がありますmemcmp()
Avishai Y

なぜ誰もがこの単純な美しいソリューション以外のものを使用するのですか?
Adam Zahran、

88

すでにBoostを使用している場合は、ブースト文字列アルゴリズム + ブースト字句キャストを使用できます

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

try {    
    if (boost::starts_with(argv[1], "--foo="))
        foo_value = boost::lexical_cast<int>(argv[1]+6);
} catch (boost::bad_lexical_cast) {
    // bad parameter
}

この種類のアプローチは、ここで提供される他の多くの回答と同様に、非常に単純なタスクでは問題ありませんが、長期的には、コマンドライン解析ライブラリを使用するほうがよいでしょう。Boostには1つ(Boost.Program_options)があり、すでにBoostを使用している場合は、これが意味をなす場合があります。

それ以外の場合は、「c ++コマンドラインパーサー」を検索すると、いくつかのオプションが表示されます。


107
文字列の接頭辞チェックのために巨大な依存関係を取り込むことは、カノンで鳥を撃つようなものです。
トビ、

150
C ++で単純な文字列操作を実行する方法を誰かが尋ねた場合、「Use Boost」は常に間違った答えです。
Glenn Maynard

90
Boostを提案する場合はマイナス1
uglycoyote 2016年

37
プロジェクトで既にブーストを使用している場合は、ここでブーストを使用するのが適切です。
Alex Che 2017年

17
回答の前に「Boostを使用している場合...」が付いています。明らかに、これは「... Boostを使用している場合」の正解です。そうでない場合は、@ Thomasによる提案をご覧ください
NuSkooler

82

私が使用するコード:

std::string prefix = "-param=";
std::string argument = argv[1];
if(argument.substr(0, prefix.size()) == prefix) {
    std::string argumentValue = argument.substr(prefix.size());
}

2
最も簡潔で、std :: stringにのみ依存しますが、最後のsubstrの最後にあるオプションで誤解を招くargument.size()を削除します。
ベンブライアント

@ ben-bryant:頭を上げてくれてありがとう。それがオプションであることを知りませんでした。
フセインYağlı

16
使用substrすると、不要なコピーが発生します。str.compare(start, count, substr)で使用される方法トーマスの答えは、より効率的です。razvanco13の答えには、を使用してコピーを回避する別の方法がありstd::equalます。
Felix Dombek 2013

4
@HüseyinYağlıHuh Thomas uses atoi which is only for windowsatoi以来、C標準ライブラリ関数となっています... 実際は、atoiそれはWindowsの-specific-だがために、それは(1)C、いないC ++、および(2)(あなたが使用する必要があるにもCで非推奨になりますので、ないbad-であるstrtolか、他の、関連する機能の一つ。のでatoiありエラー処理はありませんが、とにかく、それはCでのみです)。
パルティアンショット

50

まだ誰もSTL アルゴリズム/不一致関数を使用していません。これがtrueを返す場合、prefixは 'toCheck'のプレフィックスです。

std::mismatch(prefix.begin(), prefix.end(), toCheck.begin()).first == prefix.end()

完全なプログラム例:

#include <algorithm>
#include <string>
#include <iostream>

int main(int argc, char** argv) {
    if (argc != 3) {
        std::cerr << "Usage: " << argv[0] << " prefix string" << std::endl
                  << "Will print true if 'prefix' is a prefix of string" << std::endl;
        return -1;
    }
    std::string prefix(argv[1]);
    std::string toCheck(argv[2]);
    if (prefix.length() > toCheck.length()) {
        std::cerr << "Usage: " << argv[0] << " prefix string" << std::endl
                  << "'prefix' is longer than 'string'" <<  std::endl;
        return 2;
    }
    if (std::mismatch(prefix.begin(), prefix.end(), toCheck.begin()).first == prefix.end()) {
        std::cout << '"' << prefix << '"' << " is a prefix of " << '"' << toCheck << '"' << std::endl;
        return 0;
    } else {
        std::cout << '"' << prefix << '"' << " is NOT a prefix of " << '"' << toCheck << '"' << std::endl;
        return 1;
    }
}

編集:

@James T. Huggettが示唆しているように、std :: equalは質問に適しています。AはBのプレフィックスですか?そして少し短いコードです:

std::equal(prefix.begin(), prefix.end(), toCheck.begin())

完全なプログラム例:

#include <algorithm>
#include <string>
#include <iostream>

int main(int argc, char **argv) {
  if (argc != 3) {
    std::cerr << "Usage: " << argv[0] << " prefix string" << std::endl
              << "Will print true if 'prefix' is a prefix of string"
              << std::endl;
    return -1;
  }
  std::string prefix(argv[1]);
  std::string toCheck(argv[2]);
  if (prefix.length() > toCheck.length()) {
    std::cerr << "Usage: " << argv[0] << " prefix string" << std::endl
              << "'prefix' is longer than 'string'" << std::endl;
    return 2;
  }
  if (std::equal(prefix.begin(), prefix.end(), toCheck.begin())) {
    std::cout << '"' << prefix << '"' << " is a prefix of " << '"' << toCheck
              << '"' << std::endl;
    return 0;
  } else {
    std::cout << '"' << prefix << '"' << " is NOT a prefix of " << '"'
              << toCheck << '"' << std::endl;
    return 1;
  }
}

2
std :: equalを使用しないのはなぜですか?
Brice M. Dempsey

私にはいいですね。コードも短くなります。私はポーズをとって、今すぐ答えを編集する必要があります:p
matiu

2
std::equalfor文字列を使用すると、文字列の終わりが検出されないという欠点があるため、プレフィックスが文字列全体よりも短いかどうかを手動で確認する必要があります。(例のプログラムでは正しく行われていますが、上記のワンライナーでは省略されています。)
Felix Dombek '29

それで、rfindに勝る利点はありませんか?
АндрейВахрушев

26

-両方の文字列ことを考えるargv[1]とは"--foo"- 、Cの文字列であるFelixDombekの答えは@ハンズダウン最適なソリューションです。

しかし、他の回答を見て、あなたのテキストが既にとして利用可能である場合、std::stringこれまで言及されていない、シンプルでゼロコピーの、最も効率的なソリューションが存在することに注意する価値があると思いました:

const char * foo = "--foo";
if (text.rfind(foo, 0) == 0)
    foo_value = text.substr(strlen(foo));

fooがすでに文字列の場合:

std::string foo("--foo");
if (text.rfind(foo, 0) == 0)
    foo_value = text.substr(foo.length());

6
rfind(x, 0) == 0規格では実際に次のように定義する必要がありますstarts_with
ポルジェス2016

1
いいえrfind()(の代わりにstartswith())は非常に効率が悪いため、文字列の最後まで検索を続けます。
ankostis

4
@ankostis rfind(x)は、実際にxが見つかるまで、終わりから始まりまで検索します。しかし、rfind(x、0)は、開始(position = 0)から開始まで検索を開始します。したがって、検索が必要な場所のみを検索します。最後から検索しません。
匿名の臆病者

18

C ++ 17ではstd::basic_string_viewC ++ 20で&を使用できますstd::basic_string::starts_withまたはでstd::basic_string_view::starts_with

メモリー管理に関して- std::string_viewと比較した場合の利点はstd::string、「文字列」(charのようなオブジェクトの連続したシーケンス)へのポインターのみを保持し、そのサイズを知っていることです。整数値を取得するためだけにソース文字列を移動/コピーしない例:

#include <exception>
#include <iostream>
#include <string>
#include <string_view>

int main()
{
    constexpr auto argument = "--foo=42"; // Emulating command argument.
    constexpr auto prefix = "--foo=";
    auto inputValue = 0;

    constexpr auto argumentView = std::string_view(argument);
    if (argumentView.starts_with(prefix))
    {
        constexpr auto prefixSize = std::string_view(prefix).size();
        try
        {
            // The underlying data of argumentView is nul-terminated, therefore we can use data().
            inputValue = std::stoi(argumentView.substr(prefixSize).data());
        }
        catch (std::exception & e)
        {
            std::cerr << e.what();
        }
    }
    std::cout << inputValue; // 42
}

1
@RolandIlligいいえ、std::atoiまったく問題ありません。不正な入力に対して例外をスローします(このコードで処理されます)。他に何か考えましたか?
Roi Danton、

あなたは話しているatoiから<cstdlib>ドキュメントは言う、「それが例外をスローすることはありません」。
Roland Illig

@RolandIllig私はあなたの最初のコメントを参照しています。のatoi代わりに誤って話しているようですstd::atoi。前者は安全ではありませんが、後者は問題ありません。ここではコードで後者を使用しています。
Roi Danton

std::atoi適切なリファレンスを引用することで、実際に例外がスローされることを証明してください。あなたがやるまでは、非常に両方を持つように混乱されるので、私はあなたを信じていない::atoistd::atoi完全に異なる方法で動作します。
Roland Illig

4
@RolandIlligしつこくありがとうございます!あなたが正しい、それはのstd::atoi代わりに使用された見落とし でしたstd::stoi。私はそれを修正しました。
Roi Danton、

12
text.substr(0, start.length()) == start

3
@GregorDoroschenkoは、「文字列が別の文字列で始まるかどうかを確認する」部分に答えます。
エタリオン2018

1
std :: stringを使用した効率的でエレガントなもの。私はこれから最も多くを学びました。
マイケルB

1
if (one-liner)
Adam.at.Epsilon

@Roland Illigその場合の動作が未定義であるとなぜ信じるのですか?enstrがen.cppreference.com/w/cpp/string/basic_string/substr
Macsinus

11

STLを使用すると、次のようになります。

std::string prefix = "--foo=";
std::string arg = argv[1];
if (prefix.size()<=arg.size() && std::equal(prefix.begin(), prefix.end(), arg.begin())) {
  std::istringstream iss(arg.substr(prefix.size()));
  iss >> foo_value;
}

2
それはあるはずですif (prefix.size()<=arg.size() && std::equal(...))
Jared Grubb

10

Cのコンストラクトを使用することで非難されるリスクがあるため、このsscanf例はほとんどのBoostソリューションよりもエレガントだと思います。また、Pythonインタープリターのある場所で実行している場合でも、リンケージについて心配する必要はありません。

#include <stdio.h>
#include <string.h>

int main(int argc, char **argv)
{
    for (int i = 1; i != argc; ++i) {
        int number = 0;
        int size = 0;
        sscanf(argv[i], "--foo=%d%n", &number, &size);
        if (size == strlen(argv[i])) {
            printf("number: %d\n", number);
        }
        else {
            printf("not-a-number\n");
        }
    }
    return 0;
}

これは、ソリューションが先行/末尾のガベージを同等のPythonコードと同じように正しく処理し、atoi(数値以外のサフィックスを誤って無視する)より正確に処理することを示すいくつかの出力例です。

$ ./scan --foo=2 --foo=2d --foo='2 ' ' --foo=2'
number: 2
not-a-number
not-a-number
not-a-number

7
場合argv[i]である"--foo=9999999999999999999999999"(ほとんどまたはすべての実装がsanely振る舞うべきですが)、動作は未定義です。私は仮定してい9999999999999999999999999 > INT_MAXます。
キース・トンプソン、

10

私はstd::string::compare以下のようにラップされたユーティリティメソッドを使用します:

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

5

なぜgnu getoptsを使わないのですか?基本的な例を以下に示します(安全チェックなし)。

#include <getopt.h>
#include <stdio.h>

int main(int argc, char** argv)
{
  option long_options[] = {
    {"foo", required_argument, 0, 0},
    {0,0,0,0}
  };

  getopt_long(argc, argv, "f:", long_options, 0);

  printf("%s\n", optarg);
}

次のコマンドの場合:

$ ./a.out --foo=33

あなたが得るでしょう

33

5

C ++ 11との互換性が必要で、ブーストを使用できない場合は、ブースト互換のドロップインと使用例を以下に示します。

#include <iostream>
#include <string>

static bool starts_with(const std::string str, const std::string prefix)
{
    return ((prefix.size() <= str.size()) && std::equal(prefix.begin(), prefix.end(), str.begin()));
}

int main(int argc, char* argv[])
{
    bool usage = false;
    unsigned int foos = 0; // default number of foos if no parameter was supplied

    if (argc > 1)
    {
        const std::string fParamPrefix = "-f="; // shorthand for foo
        const std::string fooParamPrefix = "--foo=";

        for (unsigned int i = 1; i < argc; ++i)
        {
            const std::string arg = argv[i];

            try
            {
                if ((arg == "-h") || (arg == "--help"))
                {
                    usage = true;
                } else if (starts_with(arg, fParamPrefix)) {
                    foos = std::stoul(arg.substr(fParamPrefix.size()));
                } else if (starts_with(arg, fooParamPrefix)) {
                    foos = std::stoul(arg.substr(fooParamPrefix.size()));
                }
            } catch (std::exception& e) {
                std::cerr << "Invalid parameter: " << argv[i] << std::endl << std::endl;
                usage = true;
            }
        }
    }

    if (usage)
    {
        std::cerr << "Usage: " << argv[0] << " [OPTION]..." << std::endl;
        std::cerr << "Example program for parameter parsing." << std::endl << std::endl;
        std::cerr << "  -f, --foo=N   use N foos (optional)" << std::endl;
        return 1;
    }

    std::cerr << "number of foos given: " << foos << std::endl;
}

2

あなたも使うことができますstrstr

if (strstr(str, substr) == substr) {
    // 'str' starts with 'substr'
}

しかし、文字列が実際に「substr」で始まらない場合、文字列全体をループする必要があるため、短い文字列にのみ適していると思います。


2

なぜライブラリーやスタッフの複雑な使用法なのか?C ++ Stringオブジェクトは[]演算子をオーバーロードするので、charsを比較するだけです。疑似ファイル。

while ((ep = readdir(dp)))
{
    string s(ep->d_name);
    if (!(s[0] == '.')) // Omit invisible files and .. or .
        files.push_back(s);
}

とても簡単です。



2
@robertwb Google+は利用できなくなりました
_Static_assert '

0
std::string text = "--foo=98";
std::string start = "--foo=";

if (text.find(start) == 0)
{
    int n = stoi(text.substr(start.length()));
    std::cout << n << std::endl;
}

3
コードの説明なしにコードを貼り付けないようにすると、すばらしいでしょう。ありがとうございました。
2017年

1
非効率的なコードは、文字列の先頭を超えて検索を続行します。
ankostis

0

C ++ 11以降ではfind()find_first_of()

findを使用して単一の文字を見つける例:

#include <string>
std::string name = "Aaah";
size_t found_index = name.find('a');
if (found_index != std::string::npos) {
    // Found string containing 'a'
}

findを使用して完全な文字列を検索し、位置5から開始する例:

std::string name = "Aaah";
size_t found_index = name.find('h', 3);
if (found_index != std::string::npos) {
    // Found string containing 'h'
}

find_first_of()最初と最初の文字のみを使用して、最初のみを検索する例:

std::string name = ".hidden._di.r";
size_t found_index = name.find_first_of('.');
if (found_index == 0) {
    // Found '.' at first position in string
}

幸運を!


なぜrfindではないのですか?rfind(str、0)は、文字列全体をスキャンして選択を進めることができないため、選択を行う必要はありません。他を参照してください。
user2864740

0

C ++ 11 std::regex_searchは、さらに複雑な式のマッチングを提供するためにも使用できるため。次の例は、浮動小数点数std::stofとそれに続くキャストも処理しますintます。

ただし、parseInt以下に示すメソッドstd::invalid_argumentは、プレフィックスが一致しない場合に例外をスローする可能性があります。これは、特定のアプリケーションに応じて簡単に適応できます。

#include <iostream>
#include <regex>

int parseInt(const std::string &str, const std::string &prefix) {
  std::smatch match;
  std::regex_search(str, match, std::regex("^" + prefix + "([+-]?(?=\\.?\\d)\\d*(?:\\.\\d*)?(?:[Ee][+-]?\\d+)?)$"));
  return std::stof(match[1]);
}

int main() {
    std::cout << parseInt("foo=13.3", "foo=") << std::endl;
    std::cout << parseInt("foo=-.9", "foo=") << std::endl;
    std::cout << parseInt("foo=+13.3", "foo=") << std::endl;
    std::cout << parseInt("foo=-0.133", "foo=") << std::endl;
    std::cout << parseInt("foo=+00123456", "foo=") << std::endl;
    std::cout << parseInt("foo=-06.12e+3", "foo=") << std::endl;

//    throw std::invalid_argument
//    std::cout << parseInt("foo=1", "bar=") << std::endl;

    return 0;
}

正規表現パターンの魔法の種類は、次の回答で詳しく説明されています

編集:前の答えは整数への変換を実行しませんでした。


0

C ++ 20以降では、このstarts_withメソッドを使用できます。

std::string s = "abcd";
if (s.starts_with("abc")) {
    ...
}

-3
if(boost::starts_with(string_to_search, string_to_look_for))
    intval = boost::lexical_cast<int>(string_to_search.substr(string_to_look_for.length()));

これは完全にテストされていません。原理はPythonのものと同じです。Boost.StringAlgoおよびBoost.LexicalCastが必要です。

文字列が他の文字列で始まるかどうかを確認し、最初の文字列の部分文字列( 'スライス')を取得して、字句キャストを使用して変換します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.