我该如何在C++中实现以下Python伪代码?
if argv[1].startswith('--foo='):
foo_value = int(argv[1][len('--foo='):])
(例如,如果argv [1]
是--foo=98
,那么foo_value
是98
。)
更新: 我不太愿意使用Boost,因为我只想对一个简单的命令行工具进行很小的更改(我不想为了一项小更改而学习如何链接和使用Boost)。我该如何在C++中实现以下Python伪代码?
if argv[1].startswith('--foo='):
foo_value = int(argv[1][len('--foo='):])
(例如,如果argv [1]
是--foo=98
,那么foo_value
是98
。)
更新: 我不太愿意使用Boost,因为我只想对一个简单的命令行工具进行很小的更改(我不想为了一项小更改而学习如何链接和使用Boost)。使用带有搜索位置参数pos
的rfind
重载函数,并将其设置为0:
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", 0)
返回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)
find
返回的值只有在titi
在字符串开头时才为零。如果它在其他地方找到,您将获得非零返回值,如果未找到,则会获得非零的npos
。假设我是正确的,我更喜欢这个答案,因为我不需要引入任何非标准的东西(是的,我知道Boost无处不在,但我更喜欢使用核心C ++库来处理这样简单的事情)。 - paxdiablos.find("toto", s.length() - 4) != std::string::npos
操作。将数字4替换为您要查找的后缀长度。 - BlueStaggo你可以像这样做:
std::string prefix("--foo=");
if (!arg.compare(0, prefix.size(), prefix))
foo_value = std::stoi(arg.substr(prefix.size()));
寻找像 Boost.ProgramOptions 这样的库来完成此任务也是一个好主意。
atoi("123xyz")
返回的是 123
,然而 Python 的 int("123xyz")
会抛出异常。 - Tomatoi
替换为 strtol
或 strtoll
,这样我们就可以检测输入值中的错误条件。 - Tom为了完整性,我提一下用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())
memcmp()
。 - Avishai Y如果您已经在使用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 有一个 (Boost.Program_options),如果您已经在使用 Boost ,那么可能会很合适。
否则,搜索“c++命令行解析器”将产生许多选项。
我使用的代码:
std::string prefix = "-param=";
std::string argument = argv[1];
if(argument.substr(0, prefix.size()) == prefix) {
std::string argumentValue = argument.substr(prefix.size());
}
substr
会导致不必要的复制。Thomas' answer 中使用的 str.compare(start, count, substr)
方法更高效。razvanco13's answer 提供另一种方法,通过使用 std::equal
避免复制。 - Felix DombekThomas使用的是只适用于Windows的atoi
,嗯?实际上,atoi
一直以来都是C标准库函数。事实上,atoi
不好的原因不是它是Windows专用的,而是因为它(1)是C而不是C++,(2)即使在C中已经被废弃了(你应该使用strtol
或其他相关函数),因为atoi
没有错误处理。但是,再次强调,这仅限于C语言。 - Parthian Shot目前还没有人使用STL algorithm/mismatch 函数。如果此函数返回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;
}
}
std::equal
来对字符串进行比较有一个缺点,它不能检测字符串的结束,因此您需要手动检查前缀是否短于整个字符串。(在上面的单行代码中未正确执行此操作,但在示例程序中已正确执行。) - Felix DombekendsWith
将会是 std::equal(suffix.rbegin(), suffix.rend(), toCheck.rbegin()
。 - krab使用 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 <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
}
std::atoi
完全没问题。它会在输入错误时抛出异常(这段代码已经处理了)。你有其他想法吗? - Roi Dantonatoi
而不是 std::atoi
。前者使用不安全,而后者则没问题。我在这里的代码中使用的是后者。 - Roi Dantonstd::atoi
而不是 std::stoi
是一个疏忽。我已经修复了这个问题。 - Roi Dantonstring_view
。
感谢您提供如此精心策划和强大的代码示例! - brita_鉴于两个字符串——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());
rfind()
(而非startswith()
)效率非常低 - 它会一直搜索到字符串末尾。 - ankostis从C++20开始,您可以使用starts_with
方法。
std::string s = "abcd";
if (s.starts_with("abc")) {
...
}
text.substr(0, start.length()) == start
starts_with
函数,用于判断一个字符串是否以另一个字符串开头。 - Louis Gorfind()
真是太好了。 - Melroy van den Berg