Обратный токенизатор смещения

У меня есть строка для токенизации. Его форма HHmmssff, где H, m, s, f — цифры.

Предполагается, что он должен быть разделен на четыре двузначных числа, но мне нужно, чтобы он также принимал сокращенные формы, например sff, поэтому он интерпретирует его как 00000sff. Я хотел использовать offset_separator boost::tokenizer, но, похоже, он работает только с положительными смещениями, и я хотел бы, чтобы он работал как бы в обратном порядке.

Хорошо, одна идея состоит в том, чтобы дополнить строку нулями слева, но, возможно, сообщество придумает что-то супер умное. ;)

Изменить. Только что вступили в силу дополнительные требования.

Основная потребность в более разумном решении заключалась в том, чтобы обрабатывать все случаи, такие как f, ssff, mssff и т. д., но также принимать более полное обозначение времени, например HH:mm:ss:ff с его сокращенными формами, например. s:ff или даже s: (предполагается, что это интерпретируется как s:00).

В случае, когда строка заканчивается на :, я, очевидно, также могу дополнить ее двумя нулями, затем удалить все разделители, оставив только цифры, и проанализировать полученную строку с духом.

Но кажется, что было бы немного проще, если бы был способ заставить токенизатор смещения возвращаться от конца строки (смещения -2, -4, -6, -8) и лексически приводить числа к ints.


person macbirdie    schedule 13.11.2008    source источник
comment
одна идея состоит в том, чтобы дополнить строку нулями слева, просто, быстро, работает. Зачем искать что-то еще?   -  person The Archetypal Paul    schedule 13.11.2008
comment
Не хочу быть помешанным на производительности, но это решение включает в себя некоторое копирование строк (ввод - это const и std::string).   -  person macbirdie    schedule 13.11.2008
comment
не должно быть проблемой, std::string обычно достаточно умен, чтобы копировать при записи, поэтому производительность будет снижаться только в том случае, если вы действительно добавляете.   -  person Evan Teran    schedule 13.11.2008
comment
Я бы действительно рекомендовал регулярное выражение. они намного более гибкие   -  person Johannes Schaub - litb    schedule 13.11.2008


Ответы (3)


Я продолжаю проповедовать нотацию BNF. Если вы можете записать грамматику, определяющую вашу проблему, вы можете легко преобразовать ее в анализатор Boost.Spirit, который сделает это за вас.

TimeString := LongNotation | ShortNotation

LongNotation := Hours Minutes Seconds Fractions

Hours := digit digit
Minutes := digit digit
Seconds := digit digit
Fraction := digit digit

ShortNotation := ShortSeconds Fraction
ShortSeconds := digit

Изменить: дополнительное ограничение

VerboseNotation = [ [ [ Hours ':' ] Minutes ':' ] Seconds ':' ]  Fraction
person xtofl    schedule 13.11.2008
comment
Но это касается только одного случая sff, а не ssff или mssff или даже 'f'. На данный момент я дополняю строку и анализирую ее духом. - person macbirdie; 13.11.2008
comment
Вы можете потребовать от требований к продукту, чтобы они полностью указывали свои потребности в форме BNF. Если они не могут, вы можете помочь им, если они могут, вы закончили. - person xtofl; 13.11.2008

В ответ на комментарий «Не хочу быть помешанным на производительности, но это решение включает некоторое копирование строк (ввод — const & std::string)».

Если вы действительно так заботитесь о производительности, что не можете использовать большую старую библиотеку, такую ​​как регулярное выражение, не рискуете парсером BNF, не хотите предполагать, что std::string::substr избежит копирования с выделением ( и, следовательно, не может использовать строковые функции STL) и даже не может копировать строковые символы в буфер и левый блок с символами «0»:

void parse(const string &s) {
    string::const_iterator current = s.begin();
    int HH = 0;
    int mm = 0;
    int ss = 0;
    int ff = 0;
    switch(s.size()) {
        case 8:
            HH = (*(current++) - '0') * 10;
        case 7:
            HH += (*(current++) - '0');
        case 6:
            mm = (*(current++) - '0') * 10;
        // ... you get the idea.
        case 1:
            ff += (*current - '0');
        case 0: break;
        default: throw logic_error("invalid date");
        // except that this code goes so badly wrong if the input isn't
        // valid that there's not much point objecting to the length...
   }
}

Но в принципе, просто 0-инициализация этих переменных int почти такая же работа, как копирование строки в буфер char с заполнением, поэтому я не ожидаю увидеть какой-либо существенной разницы в производительности. Поэтому я на самом деле не рекомендую это решение в реальной жизни, просто как упражнение в преждевременной оптимизации.

person Steve Jessop    schedule 13.11.2008
comment
На самом деле я использую синтаксический анализатор BNF и std::string, поэтому производительность не является ТАКОЙ ОГРОМНОЙ проблемой (в конце концов, дух - это в основном библиотека шаблонов), и я фактически создаю замену для кода, похожего на ваш пример, чтобы сделать его более общий и, гм, хороший. ;) - person macbirdie; 13.11.2008
comment
Я просто хотел посмотреть, возможно ли действительно чистое решение. - person macbirdie; 13.11.2008
comment
Достаточно справедливо - вопрос, подразумеваемый этим одним комментарием, - это совсем другое дело, чем вопрос, который вы действительно задали, о простом коде, а не об ужасном воздействии байтов. Я просто обожаю такие хаки с переключателями, потому что они пугают квадратиков, которые боятся провалиться ;-) - person Steve Jessop; 13.11.2008

На ум приходят регулярные выражения. Что-то вроде "^0*?(\\d?\\d?)(\\d?\\d?)(\\d?\\d?)(\\d?\\d?)$" с boost::regex. Подсовпадения предоставят вам числовые значения. Не должно быть сложно адаптироваться к вашему другому формату с двоеточиями между числами (см. ответ sep61.myopenid.com). boost::regex — один из самых быстрых парсеров регулярных выражений.

person Johannes Schaub - litb    schedule 13.11.2008