Мне нужна помощь, чтобы заменить все символы \ 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 />
Спасибо за ваше время. С уважением.
Это невероятно просто в регулярном выражении .NET ... жаль, что это java :(
Я вам говорю, это совсем не просто :)




Я бы предложил (простой) парсер, а не регулярное выражение. Примерно так (плохой псевдокод):
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. Тем не менее, это не позволит коду быть просто кодом.
Достаточно верно. Но OP не определила механизм выхода для блока кода. «О, какие запутанные сети мы плетем, когда впервые практикуемся в языковом дизайне». Или что-то вроде того.
Чтобы все получилось, вам действительно нужно сделать три прохода:
Код выглядит * примерно так:
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, и вы сможете решить это.
Вы правы насчет стоимости написания лексера / парсера, но они также хорошо масштабируются со сложностью постановки задачи. И это может быть больше, чем это.
Как упоминалось в других плакатах, регулярные выражения - не лучший инструмент для работы, потому что они почти повсеместно реализованы как жадные алгоритмы. Это означает, что даже если вы пытались сопоставить блоки кода, используя что-то вроде:
(\[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), но Python выглядит хорошо (по крайней мере, в теории).
Это неплохой способ справиться с этой проблемой, но его будет нелегко обобщить, если проблема станет намного сложнее. +1 в любом случае.
Кажется, это делает это:
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).
Верно, но дело в том, что алгоритм можно изменить для правильной обработки произвольного вложения.
Это сложно, потому что если регулярные выражения хороши в поиске чего-либо, они не так хороши в сопоставлении всего, кроме чего-то ... Так что вам нужно использовать цикл, я сомневаюсь, что вы можете сделать это за один раз.
После поиска я нашел что-то близкое к решению 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());
}
}
Грубый быстрый тест, вам нужно больше (пустая строка, пустая строка, без тега кода, несколько и т. д.).
Я немного удивлен глубиной обсуждения этого, казалось бы, простого вопроса. Голосовать за.