Самая быстрая реализация без кавычек? - PullRequest
5 голосов
/ 22 июля 2009

Я работаю над кодом, который нормализует много данных. В конце обработки в файл записывается несколько пар ключ = «значение».

Часть "value" может быть любой, поэтому в точке вывода значения должны иметь любые вложенные кавычки, экранированные как \ ".

Сейчас я использую следующее:

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

Однако gprof показывает, что я трачу большую часть своего времени на выполнение этого метода, поскольку мне приходится вызывать его для каждого значения для каждой строки.

Мне любопытно, есть ли более быстрый способ, чем этот. Я не могу использовать std :: replace, так как заменяю один символ двумя.

Спасибо за любой совет.

Ответы [ 4 ]

6 голосов
/ 22 июля 2009

Если скорость имеет значение, вы должны использовать рукописную функцию для этого. Обратите внимание на использование 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;
}
2 голосов
/ 22 июля 2009

Я бы вообще не брал исходную строку и не строил новую выходную строку.
Я бы перебирал исходную строку и печатал каждый символ, если символ является кавычкой, просто напечатайте «\» перед печатью. .

1 голос
/ 22 июля 2009

Меня не удивляет, что регулярное выражение здесь очень медленное - вы используете большой универсальный молоток, чтобы вбить крошечный гвоздь. Конечно, если вам понадобится сделать что-то более интересное, регулярное выражение может быстро получить преимущество с точки зрения простоты.

Что касается более простого / быстрого подхода, вы можете попробовать записать экранированную строку в отдельный буфер по одному символу за раз. Затем добавление escape-символов становится тривиальным, и вы не тратите время на перераспределение строки или смещение символов. Самой большой трудностью будет управление размером буфера, но вы можете просто использовать вектор для этого и повторно использовать один и тот же вектор для каждой строки, чтобы избежать повторного выделения. Повышение эффективности будет во многом зависеть от деталей работы вектора, но при необходимости вы всегда можете свести его к необработанным массивам и ручному управлению памятью.

Процедура может выглядеть примерно так, если вы используете vector:

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...
}
0 голосов
/ 22 июля 2009

Хорошо, здесь есть реализация, использующая 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;

Который напечатан:

эй, у меня есть \ "все \" над f \ "в пла \" ce \ "

...