Antes de Java 8 cuando dividimos en una cadena vacía como
String[] tokens = "abc".split("");
El mecanismo de división se dividiría en lugares marcados con |
|a|b|c|
porque ""
existe un espacio vacío antes y después de cada carácter. Entonces, como resultado, generaría al principio esta matriz
["", "a", "b", "c", ""]
y luego eliminará las cadenas vacías finales (porque no proporcionamos explícitamente un valor negativo al limit
argumento) para que finalmente regrese
["", "a", "b", "c"]
En Java 8, el mecanismo de división parece haber cambiado. Ahora cuando usamos
"abc".split("")
obtendremos una ["a", "b", "c"]
matriz en lugar de, ["", "a", "b", "c"]
por lo que parece que las cadenas vacías al inicio también se eliminan. Pero esta teoría falla porque, por ejemplo,
"abc".split("a")
devuelve una matriz con una cadena vacía al inicio ["", "bc"]
.
¿Alguien puede explicar qué está pasando aquí y cómo han cambiado las reglas de división en Java 8?
s.split("(?!^)")
parece funcionar.split("")
en lugar de crípticos (para las personas que no usan expresiones regulares)split("(?!^)")
osplit("(?<!^)")
o algunos otros expresiones regulares.Respuestas:
El comportamiento de
String.split
(qué llamadasPattern.split
) cambia entre Java 7 y Java 8.Documentación
Comparando entre la documentación de
Pattern.split
en Java 7 y Java 8 , se observa la siguiente cláusula se añade:La misma cláusula también se agrega
String.split
en Java 8 , en comparación con Java 7 .Implementación de referencia
Comparemos el código de
Pattern.split
la implementación de referencia en Java 7 y Java 8. El código se recupera de grepcode, para la versión 7u40-b43 y 8-b132.Java 7
Java 8
La adición del siguiente código en Java 8 excluye la coincidencia de longitud cero al comienzo de la cadena de entrada, lo que explica el comportamiento anterior.
Mantener la compatibilidad
Siguiendo el comportamiento en Java 8 y superior
Para que se
split
comporte de manera uniforme en todas las versiones y sea compatible con el comportamiento en Java 8:(?!\A)
al final de la expresión regular y envuelva la expresión regular original en el grupo que no captura(?:...)
(si es necesario).(?!\A)
comprueba que la cadena no termine al principio de la cadena, lo que implica que la coincidencia es una coincidencia vacía al principio de la cadena.Siguiendo el comportamiento en Java 7 y anteriores
No existe una solución general para hacer
split
compatible con versiones anteriores de Java 7 y versiones anteriores, salvo reemplazar todas las instancias desplit
para que apunten a su propia implementación personalizada.fuente
split("")
código para que sea coherente en las diferentes versiones de Java?(?!^)
al final de la expresión regular y envolviendo la expresión regular original en un grupo que no captura(?:...)
(si es necesario), pero no puedo pensar en ninguna forma de hacerlo compatible con versiones anteriores (siga el comportamiento anterior en Java 7 y anteriores)."(?!^)"
? ¿En qué escenarios será diferente""
? (¡Soy terrible en regex!: - /).Pattern.MULTILINE
bandera, mientras que\A
siempre coincide al principio de la cadena independientemente de las banderas.Esto se ha especificado en la documentación de
split(String regex, limit)
.En
"abc".split("")
obtuvo una coincidencia de ancho cero al principio, por lo que la subcadena vacía principal no se incluye en la matriz resultante.Sin embargo, en su segundo fragmento, cuando dividió
"a"
, obtuvo una coincidencia de ancho positiva (1 en este caso), por lo que la subcadena principal vacía se incluye como se esperaba.(Se eliminó el código fuente irrelevante)
fuente
Hubo un ligero cambio en los documentos
split()
de Java 7 a Java 8. Específicamente, se agregó la siguiente declaración:(énfasis mío)
La división de cadena vacía genera una coincidencia de ancho cero al principio, por lo que no se incluye una cadena vacía al comienzo de la matriz resultante de acuerdo con lo especificado anteriormente. Por el contrario, su segundo ejemplo que se divide
"a"
genera una coincidencia de ancho positiva al comienzo de la cadena, por lo que de hecho se incluye una cadena vacía al comienzo de la matriz resultante.fuente
"some-string".split("")
es un caso bastante raro..split("")
no es la única forma de dividir sin hacer coincidir nada. Usamos una expresión regular de búsqueda anticipada positiva que en jdk7, que también coincidió al principio y produjo un elemento de cabeza vacío que ahora se ha ido. github.com/spray/spray/commit/…