Регулярное выражение для замены всех \ n в строке, но не внутри тега [code] [/ code]

Мне нужна помощь, чтобы заменить все символы \ n (новая строка) для
в строке, но не те \ n внутри тегов [code] [/ code]. У меня мозг горит, я не могу решить это самостоятельно :(

Пример:

test test test
test test test
test
test

[code]some
test
code
[/code]

more text

Должно быть:

test test test<br />
test test test<br />
test<br />
test<br />
<br />
[code]some
test
code
[/code]<br />
<br />
more text<br />

Спасибо за ваше время. С уважением.

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

dmckee --- ex-moderator kitten 30.11.2008 22:40

Это невероятно просто в регулярном выражении .NET ... жаль, что это java :(

Timothy Khouri 30.11.2008 23:18

Я вам говорю, это совсем не просто :)

Matías 30.11.2008 23:19
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
9
3
11 150
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

Ответ принят как подходящий

Я бы предложил (простой) парсер, а не регулярное выражение. Примерно так (плохой псевдокод):

stack elementStack;

foreach(char in string) {
    if (string-from-char == "[code]") {
        elementStack.push("code");
        string-from-char = "";
    }

    if (string-from-char == "[/code]") {
        elementStack.popTo("code");
        string-from-char = "";
    }

    if (char == "\n" && !elementStack.contains("code")) {
        char = "<br/>\n";
    }
}

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

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

Ваш лексер определит пять токенов: ("[code]", '\ n', "[/ code]", EOF,: все остальные строки :), и ваш конечный автомат будет выглядеть так:

state    token    action
------------------------
begin    :none:   --> out
out      [code]   OUTPUT(token), --> in
out      \n       OUTPUT(break), OUTPUT(token)
out      *        OUTPUT(token)
in       [/code]  OUTPUT(token), --> out
in       *        OUTPUT(token)
*        EOF      --> end

Обновлено: Я вижу, что другой плакат обсуждает возможную необходимость вложения блоков. Этот конечный автомат с этим не справится. Для вложенных блоков используйте рекурсивный приличный парсер (не такой уж простой, но все же достаточно простой и расширяемый).

Обновлено: Axeman отмечает, что этот дизайн исключает использование «[/ code]» в коде. Чтобы справиться с этим, можно использовать аварийный механизм. Что-то вроде добавьте '\' к своим токенам и добавьте:

state    token    action
------------------------
in       \        -->esc-in
esc-in   *        OUTPUT(token), -->in
out      \        -->esc-out
esc-out  *        OUTPUT(token), -->out

к государственной машине.

Применяются обычные аргументы в пользу машинных лексеров и синтаксических анализаторов.

Это неплохо, но это не позволяет коду использовать строку «[/ code]» или иметь это значение в комментариях. Однако некоторые из нас также привыкли писать '</' + 'script>' в JavaScript. Тем не менее, это не позволит коду быть просто кодом.

Axeman 30.11.2008 20:33

Достаточно верно. Но OP не определила механизм выхода для блока кода. «О, какие запутанные сети мы плетем, когда впервые практикуемся в языковом дизайне». Или что-то вроде того.

dmckee --- ex-moderator kitten 30.11.2008 22:38

Чтобы все получилось, вам действительно нужно сделать три прохода:

  1. Найдите блоки [code] и замените их уникальным токеном + индексом (сохраняя исходный блок), например, «foo [code] abc [/ code] bar [code] efg [/ code]» становится «foo TOKEN-1 barTOKEN» -2 "
  2. Сделайте замену новой строки.
  3. Просканируйте жетоны побега и восстановите исходный блок.

Код выглядит * примерно так:

Matcher m = escapePattern.matcher(input);
while(m.find()) {
    String key = nextKey();
    escaped.put(key,m.group());
    m.appendReplacement(output1,"TOKEN-"+key);
}
m.appendTail(output1);
Matcher m2 = newlinePatten.matcher(output1);
while(m2.find()) {
    m.appendReplacement(output2,newlineReplacement);
}
m2.appendTail(output2);
Matcher m3 = Pattern.compile("TOKEN-(\\d+)").matcher(output2); 
while(m3.find()) {
    m.appendReplacement(finalOutput,escaped.get(m3.group(1)));
}
m.appendTail(finalOutput);

Это быстрый и грязный способ. Существуют более эффективные способы (другие упоминали синтаксический анализатор / лексеры), но если вы не обрабатываете миллионы строк и ваш код привязан к ЦП (а не к вводу-выводу, как большинство веб-приложений), и вы подтвердили профилировщиком, что это узкое место, они, вероятно, того не стоят.

* Не запускал, это все по памяти. Просто проверьте API, и вы сможете решить это.

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

dmckee --- ex-moderator kitten 30.11.2008 07:24

Как упоминалось в других плакатах, регулярные выражения - не лучший инструмент для работы, потому что они почти повсеместно реализованы как жадные алгоритмы. Это означает, что даже если вы пытались сопоставить блоки кода, используя что-то вроде:

(\[code\].*\[/code\])

Тогда выражение будет соответствовать всему, от первого тега [code] до последнего тега [/code], что явно не то, что вам нужно. Хотя есть способы обойти это, получающиеся в результате регулярные выражения обычно бывают хрупкими, неинтуитивными и совершенно уродливыми. Что-то вроде следующего кода на Python будет работать намного лучше.

output = []
def add_brs(str):
    return str.replace('\n','<br/>\n')
# the first block will *not* have a matching [/code] tag
blocks = input.split('[code]')
output.push(add_brs(blocks[0]))
# for all the rest of the blocks, only add <br/> tags to
# the segment after the [/code] segment
for block in blocks[1:]:
    if len(block.split('[/code]'))!=1:
        raise ParseException('Too many or few [/code] tags')
    else:
        # the segment in the code block is pre, everything
        # after is post
        pre, post = block.split('[/code]')
        output.push(pre)
        output.push(add_brs(post))
# finally join all the processed segments together
output = "".join(output)

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

Для этого варианта использования явно не будет вложенных блоков [кода], поэтому неохотный квантификатор позаботится об этом. например, "[code]. *? [\\ code]" остановится, как только встретит "[/ code]"

noah 30.11.2008 07:12

Вы немного ошибаетесь в описании регулярного выражения (как указал @noah), но Python выглядит хорошо (по крайней мере, в теории).

strager 30.11.2008 07:41

Это неплохой способ справиться с этой проблемой, но его будет нелегко обобщить, если проблема станет намного сложнее. +1 в любом случае.

dmckee --- ex-moderator kitten 30.11.2008 07:46

Кажется, это делает это:

private final static String PATTERN = "\\*+";

public static void main(String args[]) {
    Pattern p = Pattern.compile("(.*?)(\\[/?code\\])", Pattern.DOTALL);
    String s = "test 1 ** [code]test 2**blah[/code] test3 ** blah [code] test * 4 [code] test 5 * [/code] * test 6[/code] asdf **";
    Matcher m = p.matcher(s);
    StringBuffer sb = new StringBuffer(); // note: it has to be a StringBuffer not a StringBuilder because of the Pattern API
    int codeDepth = 0;
    while (m.find()) {
        if (codeDepth == 0) {
            m.appendReplacement(sb, m.group(1).replaceAll(PATTERN, ""));
        } else {
            m.appendReplacement(sb, m.group(1));
        }
        if (m.group(2).equals("[code]")) {
            codeDepth++;
        } else {
            codeDepth--;
        }
        sb.append(m.group(2));
    }
    if (codeDepth == 0) {
        StringBuffer sb2 = new StringBuffer();
        m.appendTail(sb2);
        sb.append(sb2.toString().replaceAll(PATTERN, ""));
    } else {
        m.appendTail(sb);
    }
    System.out.printf("Original: %s%n", s);
    System.out.printf("Processed: %s%n", sb);
}

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

Хороший, но я сомневаюсь, что теги [code] могут быть вложенными (по крайней мере, в стандартном BBCode).

PhiLho 30.11.2008 12:36

Верно, но дело в том, что алгоритм можно изменить для правильной обработки произвольного вложения.

cletus 30.11.2008 13:57

Это сложно, потому что если регулярные выражения хороши в поиске чего-либо, они не так хороши в сопоставлении всего, кроме чего-то ... Так что вам нужно использовать цикл, я сомневаюсь, что вы можете сделать это за один раз.

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

import java.util.regex.*;

class Test
{
  static final String testString = "foo\nbar\n[code]\nprint'';\nprint{'c'};\n[/code]\nbar\nfoo";
  static final String replaceString = "<br>\n";
  public static void main(String args[])
  {
    Pattern p = Pattern.compile("(.+?)(\\[code\\].*?\\[/code\\])?", Pattern.DOTALL);
    Matcher m = p.matcher(testString);
    StringBuilder result = new StringBuilder();
    while (m.find()) 
    {
      result.append(m.group(1).replaceAll("\\n", replaceString));
      if (m.group(2) != null)
      {
        result.append(m.group(2));
      }
    }
    System.out.println(result.toString());
  }
}

Грубый быстрый тест, вам нужно больше (пустая строка, пустая строка, без тега кода, несколько и т. д.).

Другие вопросы по теме