我如何在c++中实现以下(Python伪代码)?
if argv[1].startswith('--foo='): foo_value = int(argv[1][len('--foo='):])
(例如,如果argv[1]是--foo=98,那么foo_value就是98。)
argv[1]
--foo=98
foo_value
98
更新:我很犹豫要不要看看Boost,因为我只是想对一个简单的小命令行工具做一个非常小的改变(我宁愿不学习如何链接并使用Boost进行一个小的改变)。
如果你已经在使用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有一个(提振。Program_options),如果你碰巧已经在使用Boost,这可能是有意义的。
否则,搜索“c++命令行解析器”会得到许多选项。
你会这样做:
std::string prefix("--foo="); if (!arg.compare(0, prefix.size(), prefix)) foo_value = std::stoi(arg.substr(prefix.size()));
寻找一个库,如Boost。为你做这件事的ProgramOptions也是一个好主意。
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相同。需要提高。StringAlgo和boost。lexicalcast。
检查字符串是否以另一个字符串开头,然后获取第一个字符串的子字符串('slice')并使用词法转换。
冒着被批评使用C结构的风险,我确实认为这个sscanf示例比大多数Boost解决方案更优雅。如果您在任何有Python解释器的地方运行,则不必担心链接!
sscanf
#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的任何东西都更正确(它将错误地忽略非数字后缀)。
atoi
$ ./scan --foo=2 --foo=2d --foo='2 ' ' --foo=2' number: 2 not-a-number not-a-number not-a-number
好的,为什么要复杂地使用库之类的东西?c++字符串对象重载[]操作符,所以你可以只比较字符。就像我刚才做的,因为我想列出目录中的所有文件忽略不可见文件和。和。pseudofiles。
while ((ep = readdir(dp))) { string s(ep->d_name); if (!(s[0] == '.')) // Omit invisible files and .. or . files.push_back(s); }
就是这么简单。
为了完整起见,我将提到C的方法:
如果str是你的原始字符串,substr是你想要的子字符串 检查 strncmp(str, substr, strlen(substr)) 如果str . 将返回0 从substr开始。函数strncmp和strlen在C . C . C . C 头文件<string.h>
str
substr
strncmp(str, substr, strlen(substr))
将返回0 从substr开始。函数strncmp和strlen在C . C . C . C 头文件<string.h>
0
strncmp
strlen
<string.h>
(最初由Yaseen Rauf发布here,添加标记)
对于不区分大小写的比较,使用strnicmp而不是strncmp。
strnicmp
这是C语言的方法,对于c++的字符串,你可以像这样使用相同的函数:
strncmp(str.c_str(), substr.c_str(), substr.size())
使用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; }
我自己使用的代码:
std::string prefix = "-param="; std::string argument = argv[1]; if(argument.substr(0, prefix.size()) == prefix) { std::string argumentValue = argument.substr(prefix.size()); }
你也可以使用strstr:
strstr
if (strstr(str, substr) == substr) { // 'str' starts with 'substr' }
但我认为它只适用于短字符串,因为当字符串实际上不是以'substr'开头时,它必须遍历整个字符串。
还没有人使用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; } }
为什么不使用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
鉴于这两个字符串——argv[1]和"--foo"——都是C字符串,@FelixDombek的回答无疑是最好的解决方案。
"--foo"
然而,看到其他答案,我认为值得注意的是,如果你的文本已经作为std::string可用,那么存在一个简单的、零复制的、最大效率的解决方案,到目前为止还没有提到:
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());
使用rfind重载,接受搜索位置pos参数,并为其传递0:
rfind
pos
std::string s = "tititoto"; if (s.rfind("titi", 0) == 0) { // pos=0 limits the search to the prefix // s starts with prefix }
谁还需要别的东西?纯STL !
许多人将其误解为“向后搜索整个字符串以寻找前缀”。这将给出错误的结果(例如,string("tititito").rfind("titi")返回2,因此与== 0比较时将返回false),而且效率很低(查看整个字符串而不是只查看开头)。但它没有这样做,因为它将pos参数传递为0,这将搜索限制为仅在或更早位置匹配。例如:
string("tititito").rfind("titi")
== 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)
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; }
我使用std::string::compare包装在实用方法中,如下所示:
std::string::compare
static bool startsWith(const string& s, const string& prefix) { return s.size() >= prefix.size() && s.compare(0, prefix.size(), prefix) == 0; }
text.substr(0, start.length()) == start
在c++ 17中,你可以使用std::basic_string_view &c++ 20 std::basic_string::starts_with或std::basic_string_view::starts_with。
std::basic_string_view
std::basic_string::starts_with
std::basic_string_view::starts_with
与std::string_view相比,在内存管理方面,std::string_view的好处是它只保存指向“字符串”(类似字符的连续对象序列)的指针,并且知道它的大小。示例:不移动/复制源字符串,只获取整数值:
std::string_view
#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 }
由于c++ 11 std::regex_search还可以用于提供更复杂的表达式匹配。下面的示例也处理通过std::stof的浮点数,并随后转换为int。
std::regex_search
std::stof
int
然而,如果前缀不匹配,下面所示的parseInt方法可能会抛出std::invalid_argument异常;这可以很容易地根据给定的应用程序进行调整:
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; }
正则表达式模式的魔力在下面的回答中详细描述。
编辑:之前的答案没有执行到整数的转换。
如果你需要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; }
在c++ 11或更高版本中,你可以使用find()和find_first_of()
find()
find_first_of()
使用find查找单个char的示例:
#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 }
好运!
从c++ 20开始,你可以使用starts_with方法。
starts_with
std::string s = "abcd"; if (s.starts_with("abc")) { ... }
在c++ 20中,现在有starts_with作为std::string的成员函数可用,定义为:
constexpr bool starts_with(string_view sv) const noexcept; constexpr bool starts_with(CharT c) const noexcept; constexpr bool starts_with(const CharT* s) const;
所以你的代码可以是这样的:
std::string s{argv[1]}; if (s.starts_with("--foo="))
std::string::starts_with
https://en.cppreference.com/w/cpp/string/basic_string/starts_with
std::string str_value = /* smthg */; const auto starts_with_foo = str_value.starts_with(std::string_view{"foo"});