2008-11-30 14 views
8

Necesito ayuda para reemplazar todos los \ n (nueva línea) Caracters para
en una Cadena, pero no esos \ n en el interior [código] [/ código] etiquetas. Mi cerebro se está quemando, no puedo resolver esto mi propia :(Regex para reemplazar todo n en una Cadena, pero no en la etiqueta [code] [/ code]

Ejemplo:

test test test 
test test test 
test 
test 

[code]some 
test 
code 
[/code] 

more text 

debe ser:..

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

Gracias por su tiempo Saludos

+1

Estoy un poco sorprendido por la profundidad de la discusión que ha generado esta pregunta aparentemente simple. Upvote. – dmckee

+0

Esto es increíblemente fácil en .NET regex ... lástima que sea java :( –

+0

Te lo digo, es cualquier cosa menos simple :) –

Respuesta

7

Sugeriría un analizador (simple), y no una expresión regular. Algo como esto (pseudocódigo incorrecto):

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"; 
    } 
} 
6

Has etiquetado la pregunta regex, pero esta puede no ser la mejor herramienta para el trabajo.

Puede ser mejor utilizar técnicas básicas de compilación de compiladores (es decir, un lexer que alimenta un analizador de máquina de estado simple).

Su léxico identificaría cinco fichas: ("[code]", '\ n', "[/ code]", EOF,: todas las demás cadenas :) y su máquina de estados se parece a:

 
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 

EDITAR: Veo otro cartel que habla de la posible necesidad de anidar los bloques. Esta máquina de estado no manejará eso. Para los bloques de anidación, use un analizador sintáctico decente recursivo (no del todo simple, pero aún lo suficientemente fácil y extensible).

EDITAR: Axeman observa que este diseño excluye el uso de "[/ code]" en el código. Un mecanismo de escape puede usarse para vencer esto. Algo así como agregar '\' a sus tokens y agregar:

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

a la máquina de estado.

Se aplican los argumentos habituales a favor de los lexers y analizadores generados por máquina.

+0

Eso no es tan malo, pero no permite que el código use la cadena " [/ code] ", o tenga este valor en los comentarios. Sin embargo, algunos de nosotros también nos acostumbramos a escribir '' en JavaScript. Aún así, no permitiría que el código sea solo el código. – Axeman

+0

Cierto. Pero el OP no ha identificado un mecanismo de escape para el bloque de código. "Oh, qué enmarañada red tejimos, cuando practicamos por primera vez el diseño del lenguaje". O algo así. – dmckee

1

para hacerlo bien, que realmente necesita para hacer tres pases:

  1. Encuentra [Código] bloques y sustituirlos por un único token + índice (salvando el bloque original), por ejemplo, "foo [código ] abc [/ code] bar [code] efg [/ code] "se convierte en" foo TOKEN-1 barTOKEN-2 "
  2. Haga su reemplazo de línea nueva.
  3. Escanee tokens de escape y restaure el bloque original.

El código es algo como *:

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); 

Esa es la manera rápida y sucia. Hay formas más eficientes (otros han mencionado analizadores/lexers), pero a menos que esté procesando millones de líneas y su código esté vinculado a la CPU (en lugar de enlazado con E/S, como la mayoría de las aplicaciones web) y haya confirmado con un generador de perfiles este es el cuello de botella, probablemente no valen la pena.

* No lo he ejecutado, esto es todo de la memoria. Simplemente marque el API y podrá resolverlo.

+0

Tiene razón sobre el costo de escribir un analizador/analizador múltiple, pero también se ajusta bien a la complejidad del enunciado del problema. Y eso podría ser más grande que esto. – dmckee

2

Según lo mencionado por otros carteles, las expresiones regulares no son la mejor herramienta para el trabajo porque se implementan casi universalmente como algoritmos codiciosos. Esto significa que incluso si se trató de emparejar bloques de código usando algo como:

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

Entonces la expresión coincidirá con todo desde el primer [code] etiqueta a la última [/code] etiqueta, lo que claramente no es lo que desea. Si bien hay formas de evitar esto, las expresiones regulares resultantes suelen ser frágiles, poco intuitivas y francamente feas. Algo así como el siguiente código python funcionaría mucho mejor.

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) 

Nota el código anterior era no probado, es sólo una idea aproximada de lo que tiene que hacer.

+0

Para este caso de uso, claramente no habrá bloques [de código] anidados, por lo que un cuantificador reacio se encargará de eso. por ejemplo, "\ [code \]. *? \ [\\ code]" se detendrá tan pronto como se encuentre con "[/ code]" – noah

+0

Estás un poco equivocado en la descripción de la expresión regular (como @noah señaló), pero el Python se ve bien (al menos en teoría). – strager

+0

No es una mala manera de manejar este problema, pero no se generalizará fácilmente si el problema se vuelve mucho más complicado. +1 de todos modos. – dmckee

3

Esto parece hacerlo:

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); 
} 

No es una expresión regular sencilla, pero no creo que puede hacer lo que quiera con una expresión regular sencilla. No con el manejo de elementos anidados, etc.

+0

Bueno, pero dudo que las etiquetas [de código] se puedan anidar (en BBCode estándar, al menos). – PhiLho

+1

Es cierto, pero el punto es que el algoritmo se puede modificar para manejar el anidamiento arbitrario correctamente. – cletus

1

Es difícil porque si las expresiones regulares son buenas para encontrar algo, no son tan buenas para hacer coincidir todo excepto algo ... Así que tienes que usar un bucle, dudo que puedas hacer eso de una vez.

Después de buscar, encontré algo cerca de la solución de cletus, excepto que supongo que el bloqueo de código no puede anidarse, lo que lleva a un código más simple: elija lo que se adapte a sus necesidades.

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()); 
    } 
} 

prueba rápida crudo, se necesita más (null, cadena vacía, ninguna etiqueta de código, múltiple, etc.).