Что такое простое решение для замещающих заменяемых записей?

Здесь проблема, которую я пытаюсь решить: пользователь может отображать пользовательский заголовок с 4 символами для чего-то на веб-сайте, например. NEWS. Я хочу добавить поддержку способа, которым пользователь может указывать значки из Font Awesome.

Я думал об использовании скобок, например. пользователь будет писать [camera]pic, и это будет переведено на <i class="icon-camera"></i>pic для отображения с соответствующим значком шрифта Awesome. Однако мне также хотелось бы избежать разметки, чтобы (например, f[[x] был напечатан как f[x], а не f[<i class="icon-x"></i>. Текстовые заполнители никогда не будут вложенными, но они могут быть смежными (например, [star][star][star][star]). Я изо всех сил пытался решить эту проблему с помощью регулярных выражений * и пришел к выводу, что регулярные выражения, вероятно, не являются подходящим решением проблемы.

Есть ли простое решение этой проблемы, которое может быть реализовано в Javascript и Ruby? Альтернативно, существует ли еще один простой способ представить эти типы заполнителей текста, которые соответствуют моим последовательным и эскалационным требованиям (например, ${camera}pic)? Или я должен разобрать его вручную, по одному символу за раз?


* Что касается регулярных выражений, которые я пробовал: \[(\w+)\] прост, но соответствует, когда он не должен на f[[x]. (\A|[^\[])\[(\w+)\] проходит f[[x], но не выполняется для каждого другого заполнителя в [x][y][z].

Вот мои тестовые примеры. Предполагая простое преобразование замены заполнителя текста заполнителя с префиксом $, тогда:

describe '#to_var' do
   it { helper.to_var('abcd').should == 'abcd' }
   it { helper.to_var('[foo][bar][baz]').should == '$foo$bar$baz' }
   it { helper.to_var('[[x]').should == '[x]' }
   it { helper.to_var('<[[x]>').should == '<[x]>' }
   it { helper.to_var('<[x]>').should == '<$x>' }   
end

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

 icon_code_regex =  %r(
    (\A # beginning of string
     | # or
     [^\[]) # not a left bracket
    \[ # literal left bracket
    (\w+) # the good stuff
    \] # literal right bracket
  )x

str.gsub(icon_code_regex, '\1$\2').gsub('[[', '[')

который не работает в случае [foo][bar][baz].

Ответ 1

Решение для Javascript:

var str = 'abcd [foo][bar][baz] [[x] <[[x]> <[x]>';

str = str.replace( /(\[)?\[(\w+)\]/g, function ( match, escaped, icon ) {
    return escaped ? '[' + icon + ']' : '$' + icon;
});

// "abcd $foo$bar$baz [x] <[x]> <$x>"

Ответ 2

Решение для Ruby

Вы можете использовать look-behind, чтобы не подстроить подстроку [word] в [[word]:

(?<!\[)\[(\w+)\]

Проверяющий (?<!\[) просто проверяет [ не появляется перед строкой, которую мы хотим сопоставить.

DEMO

Решение для JS

Для обходного пути JS, поскольку он не имеет внешнего вида:

// Match one non-opening-bracket character, then the [word],
// but the closing bracket is not consumed, for the case
// of consecutive [word1][word2]
var regex = /(^|[^\[])\[(\w+)(?=\])/g;
var arr;

var output = "";
var lastAppend = 0;

while ((arr = regex.exec(inputString)) !== null) {
    // Append substring from index of lastAppend to right before opening [
    // lastAppend will either be at beginning of the string (0)
    // OR right after closing ] from previous match
    output += inputString.substring(lastAppend, arr.index + arr[1].length);
    output += "$" + arr[2];

    // Need to offset by 1 to skip the closing ] (not consumed)
    lastAppend = regex.lastIndex + 1;
}

output += inputString.substring(lastAppend);

Это довольно уродливо. Я не уверен, есть ли более элегантный способ.

Ответ 3

Просто для демонстрации, вот более элегантное решение JS, которое использует replace:

var output = str.replace(/\[(\w+)\]/g, function(match, icon, offset) {
    if (offset>0 && str.charAt(offset-1) == '[') // if previous existed and was [
        return match.slice(1); // return the match but without opening [
    // else
    return '<i class="icon-' + icon + '" />'; // or whatever you want to do
});