Разделение строки двоеточием, которое НЕ содержится в скобках () - PullRequest
0 голосов
/ 23 апреля 2011

Я работаю над игровым движком HTML5 / JavaScript, и я столкнулся со сценарием, которого у меня никогда не было, и не могу понять, как я могу это осуществить.

Проще говоря, я хочу разбить строку на массив по символу - при условии, что этот символ не находится в скобках.

По сути, в файлах XML для таких вещей, как элементы / плитки, я храню "триггеры ", то есть операторы, дающие правила для операций, которые будет выполнять код.Различные параметры одного триггера разделяются двоеточием (:), и для элемента может быть несколько триггеров, причем каждый триггер разделяется запятой.Вот пример:

<response trigger="npc:self:dialog:1:3">No, thank you.</response>

(Это в основном говорит: если выбран этот ответ, сделайте NPC, который задал начальный цикл вопроса, к конкретному сообщению определенного преобразования)

Двигаясь дальше: мне понадобилась возможность инкапсулировать триггеры обратного вызова в скобках параметров с определенными триггерами.Вот пример:

<response trigger="shop:open:1:(npc:self:dialog:1:4)">Yes, please.</response>

(Это в основном говорит: откройте определенный магазин, и когда магазин закроется, перейдите к определенному разговору / сообщению для говорящего NPC)

Идея состоит в том, что когда магазин закрыт, я могу вызвать 4-й параметр этого триггера (который сам является триггером).Как я уверен, вы уже догадались, проблема здесь в том, что если я разделю начальную строку триггера на основе «:», то он разбивает триггер обратного вызова как другие (грязные) параметры основного триггера.Я не хочу этогоТакже я не хочу делать что-то вроде разделения вторичных триггеров другим персонажем (по причинам генерации позже, и потому что я представляю, что будут времена, когда я захочу вкладывать много триггеров на более глубокие уровни, и я не хочу использоватьразные символы. Я знаю обходные пути, но я хотел бы узнать, как правильно разделить символ, который не содержится в других конкретных символах.

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

Есть идеи?

Я очень ценю любую помощь, которую может оказать любой из вас.:)

Ответы [ 3 ]

1 голос
/ 23 апреля 2011

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

В любом случае, вместо создания некоторого регулярного выражения в стиле барокко, рассмотрите очень простой синтаксический анализатор: отсканируйте до следующего вхождения либо ":", либо "(", и сделайте что-нибудь со следующим токеном. Повторите. Это будет легко делать с рекурсивным спуском, и будет выглядеть примерно так:

parse(string)
   if string is empty: return
   scan to delimiter, put delimiter index into d, token string into t
   put t into a table for processing later
   case on d:
      string[d] == ":": parseColonToken(string[d+1:])
      string[d] == "(": parseParentString(strin[d+1:])
   end
end

(Очевидно, это псевдокод. Возьмите string[n:] как "подстроку string от индекса n до конца.)

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

0 голосов
/ 23 апреля 2011

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

var parts1 = "shop:open:1:(npc:self:dialog:1:4)".split(/:(?=\()/);
// parts1 now looks like ["shop:open:1", "(npc:self:dialog:1:4)"]
var parts2 = "shop:open:1".split(/:(?=\()/);
// parts2 now looks like ["shop:open:1"]

А потом:

var cmd = null;
var arg = null;
if(parts.length > 0) {
    cmd = parts[0].split(':');
    arg = (parts[1] || '').replace(/[()]/g, '').split(':');
}

Возможно, вы сможете втиснуть больше этого в одно регулярное выражение (и, возможно, все это в зависимости от того, какие нерегулярные функции поддерживает ваш целевой механизм регулярных выражений), но нет особого смысла, и ясность - лучшая цель для вашего кода, чем "короткая". Любой, кто посмотрит на вышесказанное, сможет понять, что он делает, если у него на руках приличная ссылка на регулярное выражение JavaScript .

Если вам приходится работать с более сложными выражениями с кавычками и экранированием и т. Д., То вы можете попробовать модифицировать синтаксический анализатор CSV , чтобы сделать то, что вам нужно.

0 голосов
/ 23 апреля 2011

Есть веская причина, по которой вам не удалось найти регулярное выражение для вашей проблемы:

Язык, который вы описываете, не является регулярным , т. Е. , с которым невозможно разобратьрегулярное выражение .

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

Язык вложенных скобок не зависит от контекста [1], поэтому написать рекурсивный синтаксический анализатор просто.

[1] http://en.wikipedia.org/wiki/Context-free_language

ДОПОЛНЕНИЕ: вам не нужен рекурсивный анализатор, достаточно простого счетчика для уровня вложенности скобок:

// Pseudo code
int depth = 0;
List<int> breakIndices;
for int index = 0 .. input.length-1:
  switch(input[index])
    ':': if (depth==0) breakIndices.add(index); break;
    '(': depth++; break;
    ')': depth--; break;
    default: break;
// Now, all indices of the colons you need are in the breakIndices list.
...