Answers:
使用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
参数as 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 ++库)。
titi
,但是缺少转换部分。
rfind()
不带pos
参数的重载。如果您使用确实带有pos
参数的重载,则它将不会搜索整个字符串,只会搜索该位置或更早的位置。(就像常规的find()
带有pos
参数的参数只在该位置或更高位置上查找。)因此pos == 0
,如本答案所示,如果通过,则它将只考虑在该位置上的匹配。答案和评论中都已经说明了这一点。
您可以这样做:
std::string prefix("--foo=");
if (!arg.compare(0, prefix.size(), prefix))
foo_value = atoi(arg.substr(prefix.size()).c_str());
寻找诸如Boost.ProgramOptions之类的库为您完成此操作也是一个好主意。
atoi("123xyz")
return 123
,而Python的int("123xyz")
抛出异常。
atoi
为strtol
或strtoll
,这使我们可以检测输入值中的错误情况。
rfind
依赖于优化才能工作的解决方案更好。
仅出于完整性考虑,我将提到使用C的方式:
如果
str
是您的原始字符串,substr
是您要检查的子字符串,则
strncmp(str, substr, strlen(substr))
0
如果str
以开头,将返回substr
。函数strncmp
和strlen
在C头文件中<string.h>
(最初由Yaseen Rauf 在此处发布,添加了标记)
对于不区分大小写的比较,请使用strnicmp
代替strncmp
。
这是C的方法,对于C ++字符串,您可以使用如下相同的函数:
strncmp(str.c_str(), substr.c_str(), substr.size())
memcmp()
如果您已经在使用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的答案中str.compare(start, count, substr)
使用的方法更有效。razvanco13的答案还有另一种避免使用进行复制的方法。std::equal
Thomas uses atoi which is only for windows
嗯?atoi
自……以来一直是C标准库函数。实际上,这atoi
是不好的-不是因为它是Windows特定的-而是因为(1)C,而不是C ++,以及(2)即使在C语言中也已弃用(您应该使用strtol
一个或其他相关功能。atoi
)没有错误处理。不过,无论如何,这仅是C语言。
尚无人使用过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;
}
}
std::equal
字符串的缺点是无法检测到字符串结尾,因此您需要手动检查前缀是否短于整个字符串。(如示例程序中正确完成,但在上面的单行代码中省略了。)
鉴于这两个字符串(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(x, 0) == 0
应该在标准中真正定义为starts_with
rfind()
(代替startswith()
)效率很低-它一直搜索到字符串的末尾。
在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
完全可以。它在错误的输入上引发异常(在此代码中处理)。您还有其他想法吗?
atoi
而不是std::atoi
。前者不安全,而后者很好。我在这里的代码中使用后者。
std::atoi
通过引用适当的引用向我证明确实引发了异常。除非您这样做,否则我不相信您,因为同时拥有这两者::atoi
并std::atoi
以完全不同的方式行事会非常令人困惑。
std::atoi
代替使用的疏忽 std::stoi
。我已经解决了。
text.substr(0, start.length()) == start
if (one-liner)
使用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;
}
if (prefix.size()<=arg.size() && std::equal(...))
。
冒着使用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
argv[i]
为is "--foo=9999999999999999999999999"
,则该行为是未定义的(尽管大多数或所有实现应具有合理的行为)。我假设9999999999999999999999999 > INT_MAX
。
我使用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-compatible插件,其中包含用法示例:
#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 ++字符串对象重载[]运算符,因此您可以比较字符。就像我刚才所做的那样,因为我想列出目录中的所有文件,而忽略不可见的文件以及..和。伪文件。
while ((ep = readdir(dp)))
{
string s(ep->d_name);
if (!(s[0] == '.')) // Omit invisible files and .. or .
files.push_back(s);
}
就这么简单..
在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
}
祝好运!
由于C ++ 11 std::regex_search
也可以用于提供更复杂的表达式匹配。以下示例还处理了浮点数thorugh 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;
}
下面的答案详细介绍了正则表达式模式的魔力。
编辑:先前的答案未执行转换为整数。