获取字符串的一部分,std :: string :: erase或std :: string :: substr更快

时间:2013-05-29 06:08:18

标签: c++ stdstring

我正在检索并存储我可以使用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);
}

3 个答案:

答案 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可能总是会这样做。