我正在检索并存储我可以使用std :: string :: erase或std :: string :: substr的字符串的一部分。
我想知道以下哪种方法更快(完成时间更短)和更高效(更少的内存分配/重新分配)。此外,有关如何通过erase和substr分配/重新分配内存的任何信息都将非常有用。谢谢!
std::string nodeName("ABCD#XYZ#NodeName");
const std::string levelSeparator("#");
选项1:使用std :: string :: substr
std::string::size_type nodeNameStartPosition = nodeName.rfind(levelSeparator);
if (nodeNameStartPosition != std::string::npos)
{
nodeNameStartPosition += levelSeparator.length();
nodeName = nodeName.substr(nodeNameStartPosition);
}
选项2:使用std :: string :: erase
std::string::size_type nodeNameStartPosition = nodeName.rfind(levelSeparator);
if (nodeNameStartPosition != std::string::npos)
{
nodeNameStartPosition += levelSeparator.length();
nodeName = nodeName.erase(0, nodeNameStartPosition);
}
答案 0 :(得分:2)
如果你真的在乎,请始终标杆。
你不需要自我分配ala nodeName = nodeName.erase(0, nodeNameStartPosition);
- 只需使用:
nodeName.erase(0, nodeNameStartPosition);
这是有效的,因为erase
已经修改了字符串nodeName
。
任何速度差异绝大多数都有erase
的优势,因为肯定没有内存分配 - 只是缓冲区内的复制。 substr()
可能会创建一个临时字符串 - 您可以从std :: string :: substr函数原型中的by-value返回类型告诉它:
string substr (size_t pos = 0, size_t len = npos) const;
除非进行短字符串优化,否则此按值返回可能需要堆分配。我对优化程序是否可以消除这些开销持怀疑态度。
另外,nodeNameStartSeparator
显然是用词不当,因为你将它指向关卡分隔符的开头。这一切归结为:
std::string::size_type levelSeparatorPos = nodeName.rfind(levelSeparator);
if (levelSeparatorPos != std::string::npos)
nodeName.erase(0, levelSeparatorPos + levelSeparator.length());
答案 1 :(得分:2)
这是一个基准测试,包括字符串的擦除和子字符串操作,用例有点不同,因为我试图从单词中删除单词中每个字母的1个字母。在这种情况下,擦除比substr更快。
#include <chrono>
#include <iostream>
#include <sstream>
using namespace std;
static const string STR(1000, 'a');
/*
* remove 1 letter from STR to create a shorter string
* every letter will be a candidate to remove
* eg: bcda -> cda, bda, bca, bcd
*
* result:
*
* stream way takes 63394.1 us
* append way takes 21007.5 us
* erase way takes 199.563 us
* substr way takes 416.735 us
*/
void stream_way() {
for (int skip = 0; skip < STR.size(); ++skip) {
stringstream ss;
for (int i = 0; i < STR.size(); ++i) {
if (i != skip) {
ss << STR[i];
}
}
(void) ss.str();
}
}
void append_way() {
for (int skip = 0; skip < STR.size(); ++skip) {
string s;
for (int i = 0; i < STR.size(); ++i) {
if (i != skip) {
s += STR[i];
}
}
(void) s;
}
}
void erase_way() {
for (int i = 0; i < STR.size(); ++i) {
string copy = STR;
copy.erase(i, 1);
(void) copy;
}
}
void substr_way() {
for (int first_part = 0; first_part < STR.size(); ++first_part) {
string s = STR.substr(0, first_part) + STR.substr(first_part + 1, STR.size() - first_part - 1);
(void) s;
}
}
int main() {
auto start = chrono::steady_clock::now();
stream_way();
auto end = chrono::steady_clock::now();
chrono::duration<double, micro> diff = end - start;
cout << "stream way takes " << diff.count() << " us\n";
start = chrono::steady_clock::now();
append_way();
end = chrono::steady_clock::now();
diff = end - start;
cout << "append way takes " << diff.count() << " us\n";
start = chrono::steady_clock::now();
erase_way();
end = chrono::steady_clock::now();
diff = end - start;
cout << "erase way takes " << diff.count() << " us\n";
start = chrono::steady_clock::now();
substr_way();
end = chrono::steady_clock::now();
diff = end - start;
cout << "substr way takes " << diff.count() << " us\n";
return 0;
}
答案 2 :(得分:1)
没有给出完整的上下文,substr
版本应该更快。
原因是:假设您没有修改原始nodeName
(并且您没有显示nodeName
的声明或初始化)。以上暗示您正在复制一个字符串,对其进行初始化,然后对其进行操作。但是,您可以使用substr
变体直接初始化它,而不是进行复制/擦除。
即使它不是本地副本,它仍然会更快,因为substr
对内存的写入更少。它将从开始位置到结束位置返回一个新字符串,仅对所需部分执行单个副本(可能是memcpy)。使用擦除示例,您必须复制整个字符串,然后复制所需的所有字符到适当的点。
substr
几乎在所有情况下都应该更快。
编辑:
如果您正在使用本地字符串进行操作,则需要进行配置以查看差异,并且可能因实现而异。 如果您使用的是本地字符串,我希望erase
能够更好地执行,因为它不需要进行任何分配,但substr
可能总是会这样做。