Вопрос об оптимальности (парсинг строки вида "R142C1543:R18999C24568")

Рейтинг: 3Ответов: 2Опубликовано: 31.05.2011

В итерационной процедуре (С++) из строки wchar_t вида "R142C1543:R18999C24568" необходимо "выкусить" числовые данные:

long RS=142
long RE=18999
long CS=1543
long CE=24568

Учитывая, что это надо сделать максимально эффективно, какие варианты вы можете предложить!? (причём количество разрядов чисел постоянно меняется)

Ответы

▲ 5Принят

Гляньте в сторону swscanf

Можно было бы написать небольшой парсер для данной строки, чтобы не использовать сторонние библиотеки, однако загвоздка в типе wchar_t судя по википедии его размер сильно зависит от компилятора. Но если удастся все-таки за что-то зацепиться можно было бы написать небольшую функцию, по типу этой (сейчас она сделана для char):

int main ()
{
    wchar_t * input = (wchar_t *)L"R142C1543:R18999C24568";

    struct {
        long RS;
        long CS;
        long RE;
        long CE;
    } result = {0, 0, 0, 0};

    long * current = (long *)&result;

    while (*++input)
    {
        switch (*input)
        {
            case L':' : input++;
            case L'C' : current++;
                        break;

            default   : *current = *current*10 + (*input - L'0');
        }
    }

    printf("RS = %ld\n", result.RS);
    printf("CS = %ld\n", result.CS);
    printf("RE = %ld\n", result.RE);
    printf("CE = %ld\n", result.CE);
}

P.S. немного модифицировал код, чтобы работало с wchar_t, спасибо за подсказку @avp.

P.S.S. L-ки перед символами ':', 'C', '0' вроде не обязательно ставить (по крайне мере работает как с ними так и без них), но я на всякий пожарный поставил.

▲ 1
// C++ STYLE !!!
using namespace std;

...

string x("R142C1543:R18999C24568");
replace(x.begin(), x.end(), 'R', ' '); // удалим все лишние символы :-)
replace(x.begin(), x.end(), 'C', ' ');
replace(x.begin(), x.end(), ':', ' ');

// более оптимальным является использование replace_if с lambda
// это уменьшает кол-во проходов по строчке для замены с ТРЕХ до ОДНОГО 
/*
replace_if(x.begin(), x.end(), [] (const char ch) -> bool {return (!isdigit (ch)); }, ' ');
// можно и без лямбды
// replace_if (x.begin(), x.end(), not1(ptr_fun(isdigit)), ' ');

*/
stringstream y(x);

long RS;    long CS;    long RE;    long CE;

y>>RS>>CS>>RE>>CE; // считаем числа за раз. Если ошиблись - запишет 0.
// итого - по строчке минимум пройдемся два раза - для замены, для считывания.

Код НЕ ПРОВЕРЯЛ.

Плюсы плюсатого кода:

  1. Код короткий и самоочевидный (ну, разве кроме лямбды).
  2. Можно легко прикрутить блоки обработки ошибок try-catch и никаких segfault'ов
  3. При наличии хорошего компилятора - код может соптимизироваться в нечто не сильно хуже, чем изначально простой вариант С кода.