Javascript y expresiones regulares: dividir la cadena y mantener el separador

131

Tengo una cadena:

var string = "aaaaaa<br />&dagger; bbbb<br />&Dagger; cccc"

Y me gustaría dividir esta cadena con el delimitador <br />seguido de un carácter especial.

Para hacer eso, estoy usando esto:

string.split(/<br \/>&#?[a-zA-Z0-9]+;/g);

Estoy obteniendo lo que necesito, excepto que estoy perdiendo el delimitador. Aquí está el ejemplo: http://jsfiddle.net/JwrZ6/1/

¿Cómo puedo mantener el delimitador?

Milos
fuente
Si conoces el delimitador de antemano, ¿por qué no simplemente ... var delim = "<br/>";?
Andreas Wong
Gracias @SiGanteng, conozco el delimitador anterior pero no puedo hacerlo funcionar para mi ejemplo. Necesito mantener el delimitador para que sea <br /> seguido por el carácter especial porque a veces puedo tener un <br /> no seguido por el carácter especial y este no tiene que dividirse.
Miloš
2
Buena pregunta, tengo un caso similar en el que conocer el delimitador no ayuda. Me estoy dividiendo en "] y [". Así que realmente mi delimitador es "&", pero dividir eso no es lo suficientemente preciso, necesito obtener los corchetes a cada lado para determinar una división adecuada. Sin embargo, necesito esos soportes de nuevo en mis cadenas divididas. 1 en cada uno, a cada lado.
PandaWood

Respuestas:

104

Utilice la búsqueda anticipada (positiva) para que la expresión regular afirme que el carácter especial existe, pero en realidad no coincide:

string.split(/<br \/>(?=&#?[a-zA-Z0-9]+;)/g);

Véalo en acción:

var string = "aaaaaa<br />&dagger; bbbb<br />&Dagger; cccc";
console.log(string.split(/<br \/>(?=&#?[a-zA-Z0-9]+;)/g));

Jon
fuente
Cuando uso este código, agrega un 0al final de cada cadena
keyboard-warrior
2
No puedo encontrar nada sobre anticipación positiva en el enlace que diste.
Paul Chris Jones
@PaulJones el contenido se movió en el tiempo intermedio. Gracias por avisarme, arreglé el enlace.
Jon
179

Estaba teniendo un problema similar pero ligeramente diferente. De todos modos, aquí hay ejemplos de tres escenarios diferentes sobre dónde guardar el deliminador.

"1、2、3".split("、") == ["1", "2", "3"]
"1、2、3".split(/(、)/g) == ["1", "、", "2", "、", "3"]
"1、2、3".split(/(?=、)/g) == ["1", "、2", "、3"]
"1、2、3".split(/(?!、)/g) == ["1、", "2、", "3"]
"1、2、3".split(/(.*?、)/g) == ["", "1、", "", "2、", "3"]

Advertencia: el cuarto solo funcionará para dividir caracteres individuales. ConnorsFan presenta una alternativa :

// Split a path, but keep the slashes that follow directories
var str = 'Animation/rawr/javascript.js';
var tokens = str.match(/[^\/]+\/?|\//g);
jichi
fuente
3
Estaba buscando algo como el tercer ejemplo, pero esto solo funciona si los elementos son solo un carácter; de lo contrario, se dividirá en caracteres individuales. Tuve que ir a la tediosa ruta RegExp.exec al final.
Gordon
2
No entiendo por qué todo el mundo está usando / g
Zarzaparrilla
1
¿Cómo usaría esta expresión regular "1、2、3" .split (/ (?! 、) / G) == ["1 、", "2 、", "3"] para palabras completas? Por ejemplo "foo1, foo2, foo3"
Waltari
¡Eres un genio!. ¿Dónde encuentra la documentación que explica cómo funciona? no necesitas el gfinal
pery mimon
1
Traducción de la .matchsolución no codiciosa para estos ejemplos: "11、22、33".match(/.*?、|.+$/g)-> ["11、", "22、", "33"]. El /gmodificador de nota es crucial para el partido.
Beni Cherniavsky-Paskin
57

Si ajusta el delimitador en paréntesis, será parte de la matriz devuelta.

string.split(/(<br \/>&#?[a-zA-Z0-9]+);/g);
// returns ["aaaaaa", "<br />&dagger;", "bbbb", "<br />&Dagger;", "cccc"]

Dependiendo de qué parte desea mantener, cambie el subgrupo con el que coincida

string.split(/(<br \/>)&#?[a-zA-Z0-9]+;/g);
// returns ["aaaaaa", "<br />", "bbbb", "<br />", "cccc"]

Podría mejorar la expresión ignorando el caso de las letras string.split (/ () & #? [A-z0-9] +; / gi);

Y puede coincidir para grupos predefinidos como este: \diguales [0-9]y \wiguales [a-zA-Z0-9_]. Esto significa que su expresión podría verse así.

string.split(/<br \/>(&#?[a-z\d]+;)/gi);

Hay una buena referencia de expresiones regulares en JavaScriptKit .

Torsten Walter
fuente
44
Aún mejor, no sé si podemos mantener solo una parte del delimitador. De hecho, necesito mantener solo el carácter especial, puedo hacerlo con esto: string.split (/ <br \/> (& #? [A-zA-Z0-9] +;) / g);
Miloš
1
Puede optimizar su expresión ignorando el caso de las palabras. O coincide con una clase de personaje predefinida. Actualizaré mi respuesta.
Torsten Walter
2
¿Por qué es esto tan bajo? Es perfecto y tan flexible
Tofandel
2
Esta es sin duda la forma más fácil y la sintaxis más legible.
Timar Ivo Batis
4

respondió aquí también JavaScript Split Regular Expression mantener el delimitador

use el patrón de búsqueda anticipada (? = patrón) en el ejemplo de expresiones regulares

var string = '500x500-11*90~1+1';
string = string.replace(/(?=[$-/:-?{-~!"^_`\[\]])/gi, ",");
string = string.split(",");

Esto le dará el siguiente resultado.

[ '500x500', '-11', '*90', '~1', '+1' ]

También se puede dividir directamente

string = string.split(/(?=[$-/:-?{-~!"^_`\[\]])/gi);

dando el mismo resultado

[ '500x500', '-11', '*90', '~1', '+1' ]
Freír
fuente
¿Por qué no dividirse inmediatamente, como en la respuesta aceptada de Jon?
Gordon
@ Gordon ... :) Podría hacer eso ... actualicé el código ... Saludos
Fry
1

Una función de extensión divide la cadena con subcadena o RegEx y el delimitador se coloca de acuerdo con el segundo parámetro adelante o atrás.

    String.prototype.splitKeep = function (splitter, ahead) {
        var self = this;
        var result = [];
        if (splitter != '') {
            var matches = [];
            // Getting mached value and its index
            var replaceName = splitter instanceof RegExp ? "replace" : "replaceAll";
            var r = self[replaceName](splitter, function (m, i, e) {
                matches.push({ value: m, index: i });
                return getSubst(m);
            });
            // Finds split substrings
            var lastIndex = 0;
            for (var i = 0; i < matches.length; i++) {
                var m = matches[i];
                var nextIndex = ahead == true ? m.index : m.index + m.value.length;
                if (nextIndex != lastIndex) {
                    var part = self.substring(lastIndex, nextIndex);
                    result.push(part);
                    lastIndex = nextIndex;
                }
            };
            if (lastIndex < self.length) {
                var part = self.substring(lastIndex, self.length);
                result.push(part);
            };
            // Substitution of matched string
            function getSubst(value) {
                var substChar = value[0] == '0' ? '1' : '0';
                var subst = '';
                for (var i = 0; i < value.length; i++) {
                    subst += substChar;
                }
                return subst;
            };
        }
        else {
            result.add(self);
        };
        return result;
    };

La prueba:

    test('splitKeep', function () {
        // String
        deepEqual("1231451".splitKeep('1'), ["1", "231", "451"]);
        deepEqual("123145".splitKeep('1', true), ["123", "145"]);
        deepEqual("1231451".splitKeep('1', true), ["123", "145", "1"]);
        deepEqual("hello man how are you!".splitKeep(' '), ["hello ", "man ", "how ", "are ", "you!"]);
        deepEqual("hello man how are you!".splitKeep(' ', true), ["hello", " man", " how", " are", " you!"]);
        // Regex
        deepEqual("mhellommhellommmhello".splitKeep(/m+/g), ["m", "hellomm", "hellommm", "hello"]);
        deepEqual("mhellommhellommmhello".splitKeep(/m+/g, true), ["mhello", "mmhello", "mmmhello"]);
    });
Berezh
fuente
1

Hice una modificación a la respuesta de jichi y la puse en una función que también admite varias letras.

String.prototype.splitAndKeep = function(separator, method='seperate'){
    var str = this;
    if(method == 'seperate'){
        str = str.split(new RegExp(`(${separator})`, 'g'));
    }else if(method == 'infront'){
        str = str.split(new RegExp(`(?=${separator})`, 'g'));
    }else if(method == 'behind'){
        str = str.split(new RegExp(`(.*?${separator})`, 'g'));
        str = str.filter(function(el){return el !== "";});
    }
    return str;
};

El tercer método de las respuestas de jichi no funcionaría en esta función, así que tomé el cuarto método y eliminé los espacios vacíos para obtener el mismo resultado.

editar: segundo método que exceptúa una matriz para dividir char1 o char2

String.prototype.splitAndKeep = function(separator, method='seperate'){
    var str = this;
    function splitAndKeep(str, separator, method='seperate'){
        if(method == 'seperate'){
            str = str.split(new RegExp(`(${separator})`, 'g'));
        }else if(method == 'infront'){
            str = str.split(new RegExp(`(?=${separator})`, 'g'));
        }else if(method == 'behind'){
            str = str.split(new RegExp(`(.*?${separator})`, 'g'));
            str = str.filter(function(el){return el !== "";});
        }
        return str;
    }
    if(Array.isArray(separator)){
        var parts = splitAndKeep(str, separator[0], method);
        for(var i = 1; i < separator.length; i++){
            var partsTemp = parts;
            parts = [];
            for(var p = 0; p < partsTemp.length; p++){
                parts = parts.concat(splitAndKeep(partsTemp[p], separator[i], method));
            }
        }
        return parts;
    }else{
        return splitAndKeep(str, separator, method);
    }
};

uso:

str = "first1-second2-third3-last";

str.splitAndKeep(["1", "2", "3"]) == ["first", "1", "-second", "2", "-third", "3", "-last"];

str.splitAndKeep("-") == ["first1", "-", "second2", "-", "third3", "-", "last"];
SwiftNinjaPro
fuente
0

He estado usando esto:

String.prototype.splitBy = function (delimiter) {
  var 
    delimiterPATTERN = '(' + delimiter + ')', 
    delimiterRE = new RegExp(delimiterPATTERN, 'g');

  return this.split(delimiterRE).reduce((chunks, item) => {
    if (item.match(delimiterRE)){
      chunks.push(item)
    } else {
      chunks[chunks.length - 1] += item
    };
    return chunks
  }, [])
}

Excepto que no deberías meterte String.prototype, así que aquí hay una versión de función:

var splitBy = function (text, delimiter) {
  var 
    delimiterPATTERN = '(' + delimiter + ')', 
    delimiterRE = new RegExp(delimiterPATTERN, 'g');

  return text.split(delimiterRE).reduce(function(chunks, item){
    if (item.match(delimiterRE)){
      chunks.push(item)
    } else {
      chunks[chunks.length - 1] += item
    };
    return chunks
  }, [])
}

Entonces podrías hacer:

var haystack = "aaaaaa<br />&dagger; bbbb<br />&Dagger; cccc"
var needle =  '<br \/>&#?[a-zA-Z0-9]+;';
var result = splitBy(haystack , needle)
console.log( JSON.stringify( result, null, 2) )

Y terminarás con:

[
  "<br />&dagger; bbbb",
  "<br />&Dagger; cccc"
]
vasilevich
fuente