¿Cómo dividir una cadena, pero también mantener los delimitadores?

Resuelto Daniel Rikowski asked hace 14 años • 24 respuestas

Tengo una cadena multilínea que está delimitada por un conjunto de delimitadores diferentes:

(Text1)(DelimiterA)(Text2)(DelimiterC)(Text3)(DelimiterB)(Text4)

Puedo dividir esta cadena en sus partes usando String.split, pero parece que no puedo obtener la cadena real, que coincide con la expresión regular delimitadora.

En otras palabras, esto es lo que obtengo:

  • Text1
  • Text2
  • Text3
  • Text4

Esto es lo que quiero

  • Text1
  • DelimiterA
  • Text2
  • DelimiterC
  • Text3
  • DelimiterB
  • Text4

¿Existe alguna forma JDK de dividir la cadena usando una expresión regular delimitadora pero también mantener los delimitadores?

Daniel Rikowski avatar Feb 05 '10 17:02 Daniel Rikowski
Aceptado

Puede utilizar la búsqueda hacia adelante y hacia atrás, que son características de las expresiones regulares.

System.out.println(Arrays.toString("a;b;c;d".split("(?<=;)")));
System.out.println(Arrays.toString("a;b;c;d".split("(?=;)")));
System.out.println(Arrays.toString("a;b;c;d".split("((?<=;)|(?=;))")));

Y obtendrás:

[a;, b;, c;, d]
[a, ;b, ;c, ;d]
[a, ;, b, ;, c, ;, d]

El último es el que quieres.

((?<=;)|(?=;))equivale a seleccionar un carácter vacío antes ;o después ;.

EDITAR: Los comentarios de Fabian Steeg sobre legibilidad son válidos. La legibilidad es siempre un problema con las expresiones regulares. Una cosa que hago para que las expresiones regulares sean más legibles es crear una variable cuyo nombre represente lo que hace la expresión regular. Incluso puedes poner marcadores de posición (por ejemplo %1$s) y usar Java String.formatpara reemplazar los marcadores de posición con la cadena real que necesitas usar; Por ejemplo:

static public final String WITH_DELIMITER = "((?<=%1$s)|(?=%1$s))";

public void someMethod() {
    final String[] aEach = "a;b;c;d".split(String.format(WITH_DELIMITER, ";"));
    ...
}
NawaMan avatar Feb 05 '2010 10:02 NawaMan

Desea utilizar opciones de búsqueda y dividir en coincidencias de ancho cero. Aquí hay unos ejemplos:

public class SplitNDump {
    static void dump(String[] arr) {
        for (String s : arr) {
            System.out.format("[%s]", s);
        }
        System.out.println();
    }
    public static void main(String[] args) {
        dump("1,234,567,890".split(","));
        // "[1][234][567][890]"
        dump("1,234,567,890".split("(?=,)"));   
        // "[1][,234][,567][,890]"
        dump("1,234,567,890".split("(?<=,)"));  
        // "[1,][234,][567,][890]"
        dump("1,234,567,890".split("(?<=,)|(?=,)"));
        // "[1][,][234][,][567][,][890]"

        dump(":a:bb::c:".split("(?=:)|(?<=:)"));
        // "[][:][a][:][bb][:][:][c][:]"
        dump(":a:bb::c:".split("(?=(?!^):)|(?<=:)"));
        // "[:][a][:][bb][:][:][c][:]"
        dump(":::a::::b  b::c:".split("(?=(?!^):)(?<!:)|(?!:)(?<=:)"));
        // "[:::][a][::::][b  b][::][c][:]"
        dump("a,bb:::c  d..e".split("(?!^)\\b"));
        // "[a][,][bb][:::][c][  ][d][..][e]"

        dump("ArrayIndexOutOfBoundsException".split("(?<=[a-z])(?=[A-Z])"));
        // "[Array][Index][Out][Of][Bounds][Exception]"
        dump("1234567890".split("(?<=\\G.{4})"));   
        // "[1234][5678][90]"

        // Split at the end of each run of letter
        dump("Boooyaaaah! Yippieeee!!".split("(?<=(?=(.)\\1(?!\\1))..)"));
        // "[Booo][yaaaa][h! Yipp][ieeee][!!]"
    }
}

Y sí, esa es una afirmación triplemente anidada en el último patrón.

Preguntas relacionadas

  • La división de Java se está comiendo a mis personajes.
  • ¿Puedes usar expresiones regulares coincidentes de ancho cero en la división de cadenas?
  • ¿Cómo convierto CamelCase en nombres legibles por humanos en Java?
  • Referencias retrospectivas en retrospectiva

Ver también

  • expresiones-regulares.info/Lookarounds
polygenelubricants avatar May 17 '2010 10:05 polygenelubricants