¿Cómo dividir una cadena, pero también mantener los delimitadores?
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?
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.format
para 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, ";"));
...
}
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