LINUX.ORG.RU

Чем бы разбить строку с учетом кавычек?

 , , ,


0

1

Задача: нужно выделить в строке подстроки, ограничивающиеся символами-разделителями. Но есть некоторые тонкости:

- Символы-разделители могут быть разные. Примерный набор: " .,;:-?!/\".
- В строках могут встречаться кавычки. Все что заключено между двумя кавычками - это отдельная подстрока, и символы-разделители в ней остаются как есть (то есть, разбивки внутри таких подстрок делать не нужно, «схлопывать» символы-разделители тоже в таких подстроках ненужно).
- В исходной строке подстрок в кавычках может быть несколько.
- Если кавычек нечетное количество, выделяются подстроки в парных кавычках, и подстрокой считается часть строки со следующего знакосимвола после последней кавычки до конца строки.

Решение нужно с поддержкой кириллицы (UTF-8) для использования в опенсорчной кроссплатформенной Qt-only программе (минимум Linux, Windows, MacOS, FreeBSD, Android). Поэтому что-то наподобе re2c рассматриваю только в том случае, если есть готовое решение по интеграции библиотеки в код.


Примеры, как должно работать разбиение:

0. Строка: Один два, три
Один
два 
три

1. Строка: Один, два, три
Один
два 
три

2. Строка: Один-два, три
Один
два 
три

3. Строка: "Один два" три
Один два 
три

4. Строка: "Один-два" три
Один-два 
три

5. Строка: "Один-два" "три четыре
Один-два 
три четыре

6. Строка: "Один-два" три, "четыре
Один-два 
три 
четыре

7. Строка: "Один-два" три "четыре! пять"
Один-два 
три
четыре! пять

★★★★★

Ответ на: комментарий от futurama

Конечный автомат, который будет пробегать строку посимвольно и срабатывать по содержимому, как это в более крупном виде реализовано в SAX при парсинге XML?

Или конечный автомат, который строит эээ... скажем «лексическое дерево», для выделения и объединения лексем?

Или конечный автомат, который циклично преобразует строку регулярками?

Или еще что? Конечных автоматов можно напридумывать много. Какой конечный автомат имеется в виду?

Xintrea ★★★★★ ()

Можно сделать с использованием boost.tokenizer (с копированием нужных заголовков в проект для простоты). Я как раз делал, чтобы разделители схлопывались и незакрытые кавычки приводили к ошибке, последнее надо будет подправить здесь (ну и примитивную обработку escape-последовательностей можно удалить, если оно не надо).

xaizek ★★★★★ ()
Ответ на: комментарий от Co6aku

Ты же видишь, что не подходит. Почитай условия.

Xintrea ★★★★★ ()
Ответ на: комментарий от xaizek

Можно сделать с использованием boost.tokenizer

Вроде написал, что Qt-only

(с копированием нужных заголовков в проект для простоты).

Что имеешь в виду? Зачем копировать _заголовки_ в проект?

Xintrea ★★★★★ ()

Твои примеры 0-7 – это автоматика? Они должны так биться без участия юзера? Тогда плюсую наколеночный парсер на ДКА.

Кстати, как на счет:

"Один\" два"  ,   ""три "четыре! пять

Stil ★★★★★ ()
Ответ на: комментарий от Xintrea

Вроде написал, что Qt-only
Что имеешь в виду? Зачем копировать _заголовки_ в проект?

Чтобы было Qt-only. Многие Boost-библиотеки header-only и ничего кроме заголовков им не нужно.

xaizek ★★★★★ ()

В чём проблема сделать посимвольный разбор, вроде

function(str){
    var ret = [];
    var len = str.length;
    var buf = '';
    var quoted = 0;
    for(var i = 0; i < len; i++){
        if(str[i].match(/[\s\.\,\;\:\-\?\!\/\\]/)){
            if(quoted == 0){
                if(buf.length > 0){
                    ret.push(buf);
                    buf = '';
                }
            } else {
                buf += str[i];
            }
        } else if(str[i] == '"') {
            quoted = (quoted == 1 ? 0 : 1);
        } else {
            buf += str[i];
        }
    }
    if(buf.length > 0) {
        ret.push(buf);
    }
    return ret;
}
Но на Qt?

WDWTFWW ()
Ответ на: комментарий от xaizek

Чтобы было Qt-only. Многие Boost-библиотеки header-only и ничего кроме заголовков им не нужно.

То есть, у них исполняемый код в заголовках?

Xintrea ★★★★★ ()
Ответ на: комментарий от Xintrea

Там много inline функций. Так как куча всего на шаблонах, то иначе сделать и не получится в любом случае.

xaizek ★★★★★ ()
Ответ на: комментарий от WDWTFWW

Сделал вот так вот, на первый взгляд работает. Во всяком случае тесты в топике проходят правильно:

QStringList FindScreen::textDelimiterDecompose(QString text)
{
  QStringList list;
  int len=text.length();
  QString buf;
  bool quoted=false;

  QString delimiter;
  delimiter.append(" ");
  delimiter.append("'");
  delimiter.append('.');
  delimiter.append(',');
  delimiter.append(';');
  delimiter.append(':');
  delimiter.append('-');
  delimiter.append('?');
  delimiter.append('!');

  for(int i=0; i<len; i++)
  {
    // Если обнаружен разделитель
    if(delimiter.contains( text[i] ))
    {
      if( !quoted )
      {
        if(buf.length() > 0)
        {
          list.append(buf);
          buf = "";
        }
      }
      else
        buf += text[i];
    }
    else if(text[i] == '"')
    {
      quoted = (quoted == true ? false : true);
    }
    else
    {
      buf += text[i];
    }
  }

  if(buf.length() > 0)
    list.append(buf);

  qDebug() << "Find split list:" << list;

  return list;
}

Xintrea ★★★★★ ()
Последнее исправление: Xintrea (всего исправлений: 1)
Ответ на: комментарий от Xintrea

QStringList QString::split(const QRegExp & rx, SplitBehavior behavior = KeepEmptyParts) const и правильная регулярка не прокатывает?

Blastbit ()
Ответ на: комментарий от fluorite
  QString delimiter;
  delimiter.append(" ");
  delimiter.append("'");
  delimiter.append('.');
  delimiter.append(',');
  delimiter.append(';');
  delimiter.append(':');
  delimiter.append('-');
  delimiter.append('?');
  delimiter.append('!');

А это не смутило?

anonymous ()
Ответ на: комментарий от anonymous

Второй комментарий про const и инициализацию строкой. Я бы конечно, initializer_list какой-нибудь предпочёл, если у QString'а есть такой { ' ', ',' блабла }

fluorite ★★★★★ ()
Ответ на: комментарий от Xintrea

По примеру Stil ещё выходит, что там ещё случай с кавычками надо дополнить, ибо если после закрывающей сразу окажется ещё одна, то так обе проигнорируются.

WDWTFWW ()

Код на re2c:

#include <stdio.h>

typedef unsigned char YYCTYPE;

static void lex(const YYCTYPE *YYCURSOR)
{
    printf("\n%s\n", YYCURSOR);
    const YYCTYPE *YYMARKER, *word;
    for (;;) {
        word = YYCURSOR;
    /*!re2c
        re2c:yyfill:enable = 0;

        end = "\x00";
        word = ["] [^\x00"]* ["]? | [^\x00 .,;:?!/\\"-]+;

        end  { return; }
        word { printf("> %.*s\n", (int)(YYCURSOR - word), word); continue; }
        *    { continue; }
    */}
}

int main(int argc, char **argv)
{
    for (int i = 1; i < argc; ++i) {
        lex((YYCTYPE*)argv[i]);
    }
    return 0;
}

Собираем и запускаем (если надо проверять строку на корректность кодировки, то UTF-8 в re2c включается опцией -8):

re2c -W 1.re -o1.cpp
g++ -O2 -Wall 1.cpp -o1
./1 \
    '"Один-два" три "четыре! пять"' \
    '"Один-два" три, "четыре' \
    '"Один-два" "три четыре' \
    '"Один-два" три' \
    '"Один два" три' \
    'Один-два, три' \
    'Один, два, три' \
    'Один два, три' \
    'Один "\"два", три'

Вывод (эскейп кавычки намеренно не обрабатывается, как показывает последний пример; легко переделать чтоб обрабатывался):

"Один-два" три "четыре! пять"
> "Один-два"
> три
> "четыре! пять"

"Один-два" три, "четыре
> "Один-два"
> три
> "четыре

"Один-два" "три четыре
> "Один-два"
> "три четыре

"Один-два" три
> "Один-два"
> три

"Один два" три
> "Один два"
> три

Один-два, три
> Один
> два
> три

Один, два, три
> Один
> два
> три

Один два, три
> Один
> два
> три

Один "\"два", три
> Один
> "\"
> два
> ", три

re2c - не библиотека, а компилятор регулярных выражений в C. Поэтому портабельность выражается в том, чтобы собрать re2c под нужную платформу. На линуксе, бсд и макоси он или запакетирован, или собирается без проблем; под виндовс надо собирать мингвом или самостоятельно возиться с MSVC.

anonymous ()
Ответ на: комментарий от anonymous

А это не смутило?

Мне так удобнее. Я так лучше вижу обрабатываемые символы, чем это будет записано в строке. Скорость тут неважна, памяти тоже копейки тратятся.

Xintrea ★★★★★ ()
Ответ на: комментарий от WDWTFWW

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

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

Xintrea ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.