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 del delimitador.
En otras palabras, esto es lo que obtengo:
Text1
Text2
Text3
Text4
Esto es lo que quiero
Text1
DelimiterA
Text2
DelimiterC
Text3
DelimiterB
Text4
¿Hay alguna forma JDK de dividir la cadena usando una expresión regular del delimitador pero también mantener los delimitadores?
Respuestas:
Puede usar Lookahead y Lookbehind. Me gusta esto:
Y obtendrás:
El último es lo que quieres.
((?<=;)|(?=;))
es igual a seleccionar un caracter vacío antes;
o después;
.Espero que esto ayude.
EDITAR Los comentarios de Fabian Steeg sobre Legibilidad son válidos. La legibilidad es siempre el problema para RegEx. Una cosa que hago para ayudar a aliviar esto es crear una variable cuyo nombre represente lo que hace la expresión regular y usar el formato de Java String para ayudarlo. Me gusta esto:
Esto ayuda un poco. :-RE
fuente
split(";", true)
sería mucho más legible quesplit("((?<=;)|(?=;))")
.String.format(WITH_DELIMITER, ";");
ya que el formato es un método estático.[\\s,]+
) que desea que coincidan por completo. Las expresiones regulares requeridas se hacen aún más largas, ya que necesita una mirada negativa adicional {adelante, atrás} s para evitar que coincidan en el medio, por ejemplo.(?<=[\\s,]+)(?![\\s,])|(?<![\\s,])(?=[\\s,]+)
.Desea usar lookarounds y dividir en coincidencias de ancho cero. Aquí hay unos ejemplos:
Y sí, esa es una afirmación triplemente anidada en el último patrón.
Preguntas relacionadas
Ver también
fuente
Una solución muy ingenua, que no involucra expresiones regulares sería realizar un reemplazo de cadena en su delimitador a lo largo de las líneas de (suponiendo una coma para delimitador):
Donde puede reemplazar tilda (~) con un delimitador único apropiado.
Luego, si hace una división en su nuevo delimitador, creo que obtendrá el resultado deseado.
fuente
Realmente no me gusta la otra manera, donde obtienes un elemento vacío al frente y atrás. Por lo general, un delimitador no está al principio o al final de la cadena, por lo que a menudo terminas desperdiciando dos ranuras de matriz buenas.
Editar: casos límite fijos. La fuente comentada con casos de prueba se puede encontrar aquí: http://snippets.dzone.com/posts/show/6453
fuente
null
argumento es la forma correcta de hacerlo. Su manejo silencioso conduce a errores que aparecen más tarde.Llegué tarde, pero volviendo a la pregunta original, ¿por qué no solo usar lookarounds?
salida:
EDITAR: Lo que ves arriba es lo que aparece en la línea de comando cuando ejecuto ese código, pero ahora veo que es un poco confuso. Es difícil hacer un seguimiento de qué comas son parte del resultado y cuáles fueron agregadas por
Arrays.toString()
. El resaltado de sintaxis de SO tampoco ayuda. Con la esperanza de conseguir el resalte al trabajo con mí en vez de contra mí, así es como esas matrices se vería que les estaban declarando en el código fuente:Espero que sea más fácil de leer. Gracias por el aviso, @finnw.
fuente
Sé que esta es una pregunta muy antigua y la respuesta también ha sido aceptada. Pero aún así me gustaría presentar una respuesta muy simple a la pregunta original. Considera este código:
SALIDA:
Solo estoy usando el límite de palabras
\b
para delimitar las palabras, excepto cuando es el comienzo del texto.fuente
abcdef
conde
como delimitador, pero se puede resolver el problema con(?!^|$)(?:(?<=de)(?!de)|(?<!de)(?=de))
(?!^|$)
Eché un vistazo a las respuestas anteriores y, sinceramente, ninguna de ellas me parece satisfactoria. Lo que quiere hacer es esencialmente imitar la funcionalidad de división de Perl. Por qué Java no permite esto y tiene un método join () en algún lugar que está más allá de mí, pero estoy divagando. Ni siquiera necesitas una clase para esto realmente. Es solo una función. Ejecute este programa de muestra:
Algunas de las respuestas anteriores tienen una comprobación nula excesiva, que recientemente escribí una respuesta a una pregunta aquí:
https://stackoverflow.com/users/18393/cletus
De todos modos, el código:
fuente
Me gusta la idea de StringTokenizer porque es Enumerable.
Pero también es obsoleto y se reemplaza por String.split, que devuelve un String aburrido [] (y no incluye los delimitadores).
Así que implementé un StringTokenizerEx que es un Iterable y que requiere una expresión regular verdadera para dividir una cadena.
Una expresión regular verdadera significa que no es una 'secuencia de caracteres' repetida para formar el delimitador:
'o' solo coincidirá con 'o', y dividirá 'ooo' en tres delimitadores, con dos cadenas vacías dentro:
Pero la expresión regular o + devolverá el resultado esperado al dividir "aooob"
Para usar este StringTokenizerEx:
El código de esta clase está disponible en DZone Snippets .
Como es habitual para una respuesta de desafío de código (una clase autónoma con casos de prueba incluidos), cópiela y péguela (en un directorio 'src / test') y ejecútela . Su método main () ilustra los diferentes usos.
Nota: (edición de finales de 2009)
El artículo Reflexiones finales: Java Puzzler: Splitting Hairs hace un buen trabajo explicando el extraño comportamiento en
String.split()
.Josh Bloch incluso comentó en respuesta a ese artículo:
La guayaba de la biblioteca común de Google también contiene un divisor que es:
Por lo tanto, puede valer la pena echarle un vistazo. De su documentación preliminar inicial (pdf) :
fuente
Pase el tercer aurgument como "verdadero". También devolverá delimitadores.
fuente
Aquí hay una implementación simple y limpia que es consistente
Pattern#split
y funciona con patrones de longitud variable, que mirar hacia atrás no puede soportar, y es más fácil de usar. Es similar a la solución proporcionada por @cletus.No hago verificaciones nulas aquí,
Pattern#split
no lo hace, ¿por qué debería hacerlo? No me gustaif
el final, pero es necesario para mantener la coherencia con elPattern#split
. De lo contrario, agregaría incondicionalmente, dando como resultado una cadena vacía como el último elemento del resultado si la cadena de entrada termina con el patrón.Convierto a String [] por coherencia
Pattern#split
, uso ennew String[0]
lugar denew String[result.size()]
, vea aquí por qué.Aquí están mis pruebas:
fuente
Publicaré también mis versiones de trabajo (la primera es muy similar a Markus).
Y aquí hay una segunda solución y es un 50% más rápida que la primera:
fuente
Otra solución candidata usando una expresión regular. Conserva el orden de los tokens, coincide correctamente con varios tokens del mismo tipo en una fila. La desventaja es que la expresión regular es un poco desagradable.
Salida de muestra:
fuente
No conozco una función existente en la API de Java que haga esto (lo que no quiere decir que no exista), pero aquí está mi propia implementación (uno o más delimitadores se devolverán como un token único; si lo desea cada delimitador se devolverá como un token separado, necesitará un poco de adaptación):
fuente
Sugiero usar Pattern and Matcher, que casi seguramente logrará lo que desea. Su expresión regular deberá ser algo más complicada que la que está utilizando en String.split.
fuente
No creo que sea posible con
String#split
, pero puede usar unStringTokenizer
, aunque eso no le permitirá definir su delimitador como una expresión regular, sino solo como una clase de caracteres de un solo dígito:fuente
Si puede pagarlo, use el método de reemplazo de Java (destino de CharSequence, reemplazo de CharSequence) y complete otro delimitador para dividirlo. Ejemplo: quiero dividir la cadena "boo: and: foo" y mantener ':' en su cadena a la derecha.
Nota importante: ¡Esto solo funciona si no tienes más "newdelimiter" en tu String! Por lo tanto, no es una solución general. Pero si conoce un CharSequence del cual puede estar seguro de que nunca aparecerá en la Cadena, esta es una solución muy simple.
fuente
Respuesta rápida: use límites no físicos como \ b para dividir. Intentaré experimentar para ver si funciona (lo usé en PHP y JS).
Es posible y un tipo de trabajo, pero podría dividirse demasiado. En realidad, depende de la cadena que desea dividir y del resultado que necesita. Da más detalles, te ayudaremos mejor.
Otra forma es hacer su propia división, capturando el delimitador (suponiendo que sea variable) y luego agregándolo al resultado.
Mi prueba rápida:
Resultado:
Un poco demasiado... :-)
fuente
Tweaked Pattern.split () para incluir patrones coincidentes en la lista
Adicional
Fuente completa
fuente
Aquí hay una versión maravillosa basada en algunos de los códigos anteriores, en caso de que ayude. Es corto, de todos modos. Incluye condicionalmente la cabeza y la cola (si no están vacías). La última parte es un caso de demostración / prueba.
fuente
Sin embargo, es una solución extremadamente ingenua e ineficiente que funciona dividida dos veces en la cadena y luego concatena las dos matrices.
fuente
fuente
Scanner scanner = new Scanner("((A+B)*C-D)*E"); scanner.useDelimiter("((?<=[\\+\\*\\-\\/\\(\\)])|(?=[\\+\\*\\-\\/\\(\\)]))"); while (scanner.hasNext()) { System.out.print(" " + scanner.next()); }
Una de las sutilezas de esta pregunta implica la pregunta del "delimitador principal": si va a tener una matriz combinada de tokens y delimitadores, debe saber si comienza con un token o un delimitador. Por supuesto, podría suponer que se debe descartar una delimitación principal, pero esto parece una suposición injustificada. También es posible que desee saber si tiene un delimitador final o no. Esto establece dos banderas booleanas en consecuencia.
Escrito en Groovy pero una versión de Java debería ser bastante obvia:
fuente
No conozco demasiado bien Java, pero si no puede encontrar un método Split que lo haga, le sugiero que haga el suyo.
No es demasiado elegante, pero lo hará.
fuente