最快的引号转义实现?

6

我正在处理一些数据的规范化工作。在处理结束时,将一些 key="value" 对写入文件中。

"value" 部分可以是任何内容,因此在输出时,值必须将任何嵌入引号的字符转义为 \" 。

目前,我正在使用以下代码:

outstream << boost::regex_replace(src, rxquotesearch, quoterepl);
// (where rxquotesearch is  boost::regex("\"")  and quoterepl is "\\\\\"")

然而,gprof显示我的执行时间大部分花费在该方法中,因为我必须为每个行的每个值调用它。
我很好奇是否有比这更快的方法。我不能使用std::replace,因为我要用两个字符替换一个字符。
感谢任何建议。
4个回答

6

如果速度是一个问题,您应该使用手写函数来执行此操作。请注意使用 reserve() 尝试尽量减少内存(重新)分配。

string escape_quotes(const string &before)
{
    string after;
    after.reserve(before.length() + 4);

    for (string::size_type i = 0; i < before.length(); ++i) {
        switch (before[i]) {
            case '"':
            case '\\':
                after += '\\';
                // Fall through.

            default:
                after += before[i];
        }
    }

    return after;
}

1
你可能需要预留 before.length+10% 或者更多的空间吗?假设至少有一个嵌套引用,我们需要扩展吗? - DeusAduro
随你需要,随便搞。 - John Kugelman
所以,我对我下面发布的评论很好奇。有什么想法,为什么这比在for循环中直接将每个字符输出到流中要快? - Joe
一个可能的小优化尝试是重复使用同一个“after”字符串对象(即:将after的指针作为参数传递并使函数返回void),这样可以减少构造函数和内存分配调用的次数,因为它只有在遇到比当前空间更大的字符串时才重新分配内存。 - KPexEA

2

我不会从源字符串中构建一个新的输出字符串。
我将遍历源字符串并打印每个字符,如果字符是引号,则在打印它之前只需打印 "\"。


有趣的是,我使用了John Kugelman的片段,直接将其写入流中,结果比先构建临时字符串再将整个字符串写入流中要慢得多。这完全出乎我的意料! - Joe
1
@Joe:我觉得这很有道理,因为它需要N次调用流输出函数,该函数内部可能有很多逻辑。如果你在临时缓冲区中构建输出,你可以(如果必要)将其减少到少量的方法调用和一个非常紧密的内部循环,这似乎更有效率。如果速度是真正重要的,那么它不会比这更快了。 - Charlie
只有实际编码并尝试后,你才会知道。这在不同的机器或编译器上“可能”更快,这完全取决于打印单个字符与完整字符串的开销。 - KPexEA

1

我并不惊讶正则表达式在这里非常慢 - 你正在使用一个大而全能的锤子来敲打一个微小的钉子。当然,如果你最终需要做一些更有趣的事情,正则表达式可能很快就会在简单性方面占据优势。

至于更简单/更快的方法,你可以尝试将转义后的字符串逐个字符地写入到单独的缓冲区中。然后添加转义变得非常简单,你不会浪费任何时间重新分配字符串或移动字符。最大的困难将是管理缓冲区的大小,但你可以使用向量来解决这个问题,并为每个字符串重复使用同一个向量以避免重复分配。效率的提高将取决于向量的工作细节,但如果需要,你总是可以将其简化为原始数组和手动内存管理。

如果你使用向量,该例程可能看起来像这样:

vector<char> buf;
for( some_iterator it = all_the_strings.begin();
     it != all_the_strings.end(); ++it )
{
    buf.clear();
    const string & str = *it;
    for( size_t i = 0; i < str.size(); ++i )
    {
        if( str[i] == '"' || str[i] == '\\' )
            buf.push_back( '\\' );
        buf.push_back( str[i] );
    }
    buf.push_back( '\0' );

    // note: this is not guaranteed to be safe, see answer comments
    const char * escaped = &buf[0];

    // print escaped string to file here...
}

启用优化和预分配后,向量在大多数情况下应该与需要增长的时间一样快。由于我的代码中存在移位操作,因此你的代码很可能比我的更快。 - DeusAduro
我相信这可能不是最好的选择 - 你能详细说明一下为什么,或者提出更好的替代方案吗? - Charlie
1
你不应该直接访问向量的后备存储器。虽然它可能会工作,但不能保证。另一种方法是使用string代替vector<char>并调用c_str()如果需要一个char*指针。 - John Kugelman
我想这可能是问题所在。那么,你的实现可能更好(从概念上讲它们几乎相同)。 - Charlie

0

这里有一个使用string::find和string::insert实现的例子,不确定它是否更快,你需要自己找出答案!代码如下:

std::string src = "hey there i have \" all \" over the f\"in pla\"ce\"";
size_t n = 0;
while ( (n=src.find("\"",n)) != std::string::npos )
{
    src.insert(n,"\\");
    n+=2;
}   
std::cout << src << std::endl;

输出结果为:

嘿,我在各个地方都有“所有”


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接