C++で次の(Python疑似コード)を実装する方法
if argv[1].startswith('--foo='):
foo_value = int(argv[1][len('--foo='):])
(例えば、argv[1]
が--foo=98
の場合、foo_value
は98
です。)
更新:単純な小さなコマンドラインツールにごくわずかな変更を加えることを検討しているだけなので、Boostを検討することを躊躇しています(リンクする方法を学ぶ必要はありません)。マイナーチェンジのためにBoostを使ってください)。
すでにBoostを使っているのであれば、 boost文字列アルゴリズム + boost lexical cast: でそれを行うことができます。
#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 ++ command line parser"を検索すると多くのオプションが得られます。
std::string s = "tititoto";
if (s.rfind("titi", 0) == 0) {
// s starts with prefix
}
他に何か必要な人は?純粋なSTL!
あなたはこのようにするでしょう:
std::string prefix("--foo=");
if (!arg.compare(0, prefix.size(), prefix))
foo_value = atoi(arg.substr(prefix.size()).c_str());
Boost.ProgramOptionsのようにあなたのためにこれをするライブラリを探すのも良い考えです。
完全を期すために、これを行うためのCの方法について説明します。
str
が元の文字列の場合、substr
が確認したい部分文字列です。
strncmp(str, substr, strlen(substr))
str
がsubstr
で始まる場合、0
を返します。関数strncmp
およびstrlen
は、Cヘッダーファイル<string.h>
にあります。
(もともとYaseen Raufによって投稿されました ここ 、マークアップが追加されました)
大文字と小文字を区別しないで比較するには、strnicmp
の代わりにstrncmp
を使用します。
これはそれを行うためのCの方法です。C++文字列の場合は、次のように同じ関数を使用できます。
strncmp(str.c_str(), substr.c_str(), substr.size())
私が自分で使うコード:
std::string prefix = "-param=";
std::string argument = argv[1];
if(argument.substr(0, prefix.size()) == prefix) {
std::string argumentValue = argument.substr(prefix.size());
}
誰もSTL algorithm/mismatch 関数を使っていません。これがtrueを返す場合、prefixは 'toCheck'のプレフィックスです。
std::mismatch(prefix.begin(), prefix.end(), toCheck.begin()).first == prefix.end()
Progの完全な例
#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())
Progの完全な例
#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;
}
}
argv[1]
と"--foo"
の両方の文字列がCの文字列であることを考えると、 @ FelixDombek's answer が一番下の解決策です。
しかし、他の答えを見てみると、あなたのテキストがすでに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());
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;
}
text.substr(0, start.length()) == start
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
C++ 17では std::basic_string_view
&C++ 20では std::basic_string::starts_with
または std::basic_string_view::starts_with
を使用できます。
std::string_view
と比較したstd::string
の利点 - メモリ管理に関して - は、それが "文字列"(charのようなオブジェクトの連続したシーケンス)へのポインタを保持するだけで、そのサイズを知っているということです。整数値を取得するためだけにソース文字列を移動/コピーしない例
#include <string_view>
#include <exception>
#include <iostream>
const char * argument = "--foo=42"; // Emulating command argument.
const char * argumentPrefix = "--foo";
int inputValue = 0;
std::string_view argView = argument;
if (argView.starts_with(argumentPrefix))
{
std::string_view prefixView = argumentPrefix; // Helper for getting the size of argumentPrefix.
try
{
// The underlying data of argView is nul-terminated, therefore we can use data().
inputValue = std::atoi(argView.substr(prefixView.size() + 1).data());
}
catch (std::exception& e)
{
std::cerr << e.what();
}
}
以下のようにユーティリティメソッドにラップされたstd::string::compare
を使用します。
static bool startsWith(const string& s, const string& prefix) {
return s.size() >= prefix.size() && s.compare(0, prefix.size(), prefix) == 0;
}
なぜ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
C++ 11の互換性が必要で、boostを使うことができない場合には、使い方の例を示すboost互換のドロップインがある。
#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;
}
strstr
を使用することもできます。
if (strstr(str, substr) == substr) {
// 'str' starts with 'substr'
}
しかし、文字列が実際に 'substr'で始まっていない場合は文字列全体をループ処理しなければならないので、短い文字列だけに適していると思います。
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;
}
さて、なぜライブラリやものを複雑に使うのでしょうか。 C++のStringオブジェクトは[]演算子をオーバーロードしているので、charを比較することができます。ディレクトリ内のすべてのファイルを一覧表示し、見えないファイルと..とを無視したいので。疑似ファイル.
while ((ep = readdir(dp)))
{
string s(ep->d_name);
if (!(s[0] == '.')) // Omit invisible files and .. or .
files.Push_back(s);
}
それはとても簡単です。
C++ 11以降では std :: regex_search も使用できます。次のようになります(失敗した場合は空の文字列を返します)。
#include <regex>
std::string startsWith(const std::string &str, const std::string &prefix) {
std::smatch match;
std::regex_search(str, match, std::regex("^" + prefix));
return match.suffix();
}
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
}
がんばろう!