Boost.Tokenizer для кавычек и скобок - PullRequest
2 голосов
/ 21 февраля 2012

Я хотел бы разбить строку на токены, используя Boost.Tokenize. Требуется, чтобы текст в кавычках или скобках был одним целым токеном. Точнее, мне нужно разбить строку как

"one (two),three" four (five "six".seven ) eight(nine, ten)

в токены типа

one (two),three
four
(five "six".seven )
eight
(nine, ten)

или, может быть

one (two),three
four
(
five "six".seven
)
eight
(
nine, ten
)

Я знаю способ маркировать текст в кавычках, но я понятия не имею, как в то же время маркировать текст в парентезе. Возможно, нужно реализовать TokenizerFunction.
Как разбить строку, как я описал?

1 Ответ

1 голос
/ 22 февраля 2012

TokenizerFunction - это функтор, который имеет два метода, ни один из которых не должен быть очень сложным для реализации. Первый - reset, который предназначен для сброса любого состояния, которое может иметь функтор, а другой - operator(), который принимает три параметра. Первые два являются итераторами, а третий является полученным токеном.

Алгоритм ниже прост. Во-первых, мы пропускаем любые пробелы. Мы ожидаем, что первый непробельный символ будет одного из трех видов. Если это кавычка или левая скобка, то мы ищем, пока не найдем соответствующий закрывающий разделитель, и вернем то, что находим в качестве токена, следя за тем, чтобы кавычки должны были быть удалены, но скобки, очевидно, должны остаться. Если первый символ является чем-то другим, то мы ищем следующий разделитель и возвращаем его вместо этого.

template <
  typename Iter = std::string::const_iterator,
  typename Type = std::string
  >
struct QuoteParenTokenizer
{
  void reset() { }

  bool operator()(Iter& next, Iter end, Type& tok) const
  {
    while (next != end && *next == ' ')
      ++next;
    if (next == end)
      return false; // nothing left to read

    switch (*next) {
      case '"': {
        ++next; // skip token start
        Item const quote = std::find(next, end, '"');
        if (quote == end)
          return false; // unterminated token
        tok.assign(next, quote);
        next = quote;
        ++next;
        break;
      }
      case '(': {
        Iter paren = std::find(next, end, ')');
        if (paren == end)
          return false; // unterminated token
        ++paren; // include the parenthesis
        tok.assign(next, paren);
        next = paren;
        break;
      }
      default: {
        Iter const first = next;
        while (next != end && *next != ' ' && *next != '"' && *next != '(')
          ++next;
        tok.assign(first, next);
      }
    }
    return true;
  }
};

Вы бы создали его как tokenizer<QuoteParenTokenizer<> >. Если у вас другой тип итератора или другой тип токена, вам необходимо указать их в параметрах шаблона как tokenizer , так и QuoteParenTokenizer.

Вы можете стать хитрее, если вам нужно обработать экранированные символы-разделители. Все будет сложнее, если вам понадобятся выражения в скобках для вложения.

Остерегайтесь того, что на данный момент приведенный выше код не был проверен.

...