javascript - regexp - ¿Por qué string.split con una expresión regular que contiene un grupo de captura devuelve una matriz que termina con una cadena vacía?
probar expresiones regulares (4)
De la especificación ECMAScript 2015 ( String.prototype.split
):
Si separador es una expresión regular que contiene paréntesis de captura, cada vez que el separador coincida con los resultados (incluidos los resultados no definidos ) de los paréntesis de captura se empalman en la matriz de salida. Por ejemplo,
"A<B>bold</B>and<CODE>coded</CODE>".split(/<(//)?([^<>]+)>/)
evalúa a la matriz:
["A", undefined, "B", "bold", "/", "B", "and", undefined, "CODE", "coded", "/", "CODE", ""]
Al igual que en su ejemplo de ejemplo, la matriz de salida aquí contiene una cadena vacía al final, que es la parte de la cadena de entrada que pasa por "coded"
que no es capturada por el patrón separador (que captura "/"
y "CODE"
).
No es obvio, pero tiene sentido porque, de lo contrario, las capturas del separador terminarían al final de la matriz dividida, donde en realidad no separarían nada.
Me gustaría dividir una cadena de entrada en el primer signo de dos puntos que todavía tiene caracteres después de ella en la misma línea.
Para esto, estoy usando la expresión regular /:(.+)/
Así que dada la cadena
aaa:
bbb:ccc
Esperaría una salida de
["aaa:/nbbb", "ccc"]
Y dada la cuerda
aaa:bbb:ccc
Esperaría una salida de
["aaa", "bbb:ccc"]
Sin embargo, cuando ejecuto estos comandos, me sale
["aaa:/nbbb", "ccc", ""]
["aaa", "bbb:ccc", ""]
Como salida.
De alguna manera, javascript está agregando una cadena vacía al final de la matriz.
He comprobado la documentación para String.split
y, aunque sí menciona que si ejecuta string.split
en una cadena vacía con un separador especificado, obtendrá una matriz con 1 cadena vacía (y no una matriz vacía). No hace mención de que siempre hay una cadena vacía en la salida, o una advertencia de que puede obtener este resultado si comete un error común o algo así.
Comprendería si mi cadena de entrada tuviera dos puntos al final o algo así; luego se divide en los dos puntos y el resto de la coincidencia es una cadena vacía. Ese es el problema que se menciona en Dividir una cadena con una expresión regular para hacer que sea una matriz sin un elemento vacío , pero no tengo este problema, ya que mi cadena de entrada no termina con mi separador.
Sé que una solución rápida en mi caso será simplemente limitar la cantidad de coincidencias, a través de "aaa:bbb:ccc".split(/:(.+)/, 2)
, pero sigo "aaa:bbb:ccc".split(/:(.+)/, 2)
curiosidad:
¿Por qué esta llamada string.split
devuelve una matriz que termina con una cadena vacía?
Interesante. Aprendí mucho de esta pregunta. Déjame compartir lo que aprendí.
El punto no coincide con la nueva línea.
Si lo pensamos, la intención es dividir la cadena en función de :
seguido de uno o más números de caracteres. Si ese es el caso, la salida debería haber sido
[''aaa'', ''/nbbb:ccc'', '''']
¿Correcto? Porque el .+
Coincide con avidez. Por lo tanto, debería haberse dividido en :/nbbb:ccc
, donde :
coincide con :
y .+
/nbbb:ccc
. Pero la salida real que obtuviste fue
[ ''aaa:/nbbb'', ''ccc'', '''' ]
Esto es porque .
no coincide con los terminadores de línea. Citando a MDN ,
(El punto, el punto decimal) coincide con cualquier carácter único, excepto los terminadores de línea : / n, / r, / u2028 o / u2029.
Entonces, :/n
no coincide :(.+)
. Por eso no se rompe allí. Si realmente pretendía que coincidiera con la nueva línea , use [^]
o [/s/S]
.
Por ejemplo,
console.log(data.split(/:([/s/S]+)/));
// [ ''aaa:/nbbb'', ''ccc'', '''' ]
console.log(data.split(/:([/s/S]+)/));
// [ ''aaa'', ''/nbbb:ccc'', '''' ]
console.log(data.split(/:([^]+)/));
// [ ''aaa'', ''/nbbb:ccc'', '''' ]
Ahora para responder a su pregunta real, ¿por qué hay una cadena vacía al final de la división? Cuando cortas una línea grande, ¿cuántas líneas obtienes? Dos pequeñas líneas. Así que cada vez que hagas un corte, debería haber dos objetos. En su caso, aaa:/nbbb
es el primer corte, el lugar real en el que se produjo el corte es :ccc
, y como la cadena termina allí, se incluye una cadena vacía para indicar que ese es el final de la cadena.
Mi expresión regular siempre genera un elemento adicional al final de la matriz devuelta por string.prototype.split (). Así que simplemente trunca la matriz cada vez. Parece mejor que Array.filter cuando siempre es el último elemento que se elimina. Estoy analizando las transformaciones CSS / SVG, divididas entre paréntesis izquierdo y derecho. Cualquiera de estos trabajos: //(|/)/
o /[/(/)]/
.
Por ejemplo:
arr = "rotate(90 46 88) scale(1.2 1.2)".split(//(|/)/);
arr.length--;
O si quieres ponerte elegante y meterlo en una sola línea:
(arr = "rotate(90 46 88) scale(1.2 1.2)".split(//(|/)/)).length--;
El resultado es: ["rotate","90 46 88","scale","1.2 1.2"]
Si cambiamos la expresión regular a /:.+/
y realizamos una división en ella, obtendrá:
["aaa", ""]
Esto tiene sentido ya que la expresión regular coincide con :bbb:ccc
. Y le da el mismo resultado, si tuviera que dividir manualmente esa cadena.
>>> ''aaa:bbb:ccc''.split('':bbb:ccc'')
[''aaa'', '''']
Agregar el grupo de captura solo guarda el bbb:ccc
, pero no debería cambiar el comportamiento de división original.