Javascript et regex: séparer la chaîne et conserver le séparateur


132

J'ai une chaîne:

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

Et je voudrais diviser cette chaîne avec le délimiteur <br />suivi d'un caractère spécial.

Pour ce faire, j'utilise ceci:

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

J'obtiens ce dont j'ai besoin, sauf que je perds le délimiteur. Voici l'exemple: http://jsfiddle.net/JwrZ6/1/

Comment puis-je conserver le délimiteur?


si vous connaissez le délimiteur à l'avance, pourquoi ne pas simplement faire ... var delim = "<br/>";?
Andreas Wong

Merci @SiGanteng, je connais le délimiteur au préalable mais je ne peux pas le faire fonctionner pour mon exemple. J'ai besoin de garder le délimiteur pour être <br /> suivi du caractère spécial car parfois je peux avoir un <br /> non suivi du caractère spécial et celui-ci n'a pas besoin d'être divisé.
Miloš

2
Bonne question, j'ai un cas similaire où connaître le délimiteur n'aide pas. Je partage sur "] & [". Donc vraiment mon délimiteur est "&" mais le fractionnement n'est pas assez précis, j'ai besoin d'obtenir les parenthèses de chaque côté pour déterminer une séparation correcte. Cependant, j'ai besoin de ces crochets dans mes chaînes séparées. 1 de chaque, de chaque côté.
PandaWood

Réponses:


104

Utilisez la recherche anticipée (positive) pour que l'expression régulière affirme que le caractère spécial existe, mais ne lui correspond pas réellement:

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

Voyez-le en action:

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


Quand j'utilise ce code, il ajoute un 0à la fin de chaque chaîne
keyboard-warrior

2
Je ne trouve rien sur la recherche positive dans le lien que vous avez donné.
Paul Chris Jones

@PaulJones, le contenu a été déplacé dans l'intervalle. Merci de m'avoir fait savoir, j'ai corrigé le lien.
Jon

180

J'avais un problème similaire mais légèrement différent. Quoi qu'il en soit, voici des exemples de trois scénarios différents pour savoir où conserver le déliminateur.

"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"]

Attention: le quatrième ne fonctionnera que pour diviser des caractères uniques. ConnorsFan présente une alternative :

// Split a path, but keep the slashes that follow directories
var str = 'Animation/rawr/javascript.js';
var tokens = str.match(/[^\/]+\/?|\//g);

3
Je cherchais quelque chose comme le troisième exemple, mais cela ne fonctionne que si les éléments ne sont qu'un seul caractère - il sera divisé en caractères individuels sinon. J'ai dû emprunter la route fastidieuse RegExp.exec à la fin.
Gordon

2
Je ne comprends pas pourquoi tout le monde utilise / g
Sarsaparilla

1
Comment utiliserait cette expression rationnelle "1、2、3" .split (/ (?! 、) / G) == ["1 、", "2 、", "3"] pour des mots entiers? Par exemple "foo1, foo2, foo3,"
Waltari

Tu es un génie!. où trouve-t-on la documentation qui explique son fonctionnement? vous n'avez pas besoin de la gfin
pery mimon

1
Traduction de la .matchsolution non gourmande pour ces exemples: "11、22、33".match(/.*?、|.+$/g)-> ["11、", "22、", "33"]. Le /gmodificateur de note est crucial pour le match.
Beni Cherniavsky-Paskin le

57

Si vous entourez le délimiteur de parantheses, il fera partie du tableau renvoyé.

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

En fonction de la pièce que vous souhaitez conserver, changez le sous-groupe auquel vous correspondez

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

Vous pouvez améliorer l'expression en ignorant la casse des lettres string.split (/ () & #? [A-z0-9] +; / gi);

Et vous pouvez faire correspondre des groupes prédéfinis comme celui-ci: \dégal [0-9]et \wégal [a-zA-Z0-9_]. Cela signifie que votre expression pourrait ressembler à ceci.

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

Il existe une bonne référence d'expression régulière sur JavaScriptKit .


4
Mieux encore, je ne sais pas que nous ne pouvons conserver qu'une partie du délimiteur. En fait, je n'ai besoin de garder que le caractère spécial, je peux le faire avec ceci: string.split (/ <br \/> (& #? [A-zA-Z0-9] +;) / g);
Miloš

1
Vous pouvez optimiser votre expression en ignorant la casse des mots. Ou correspond à une classe de caractères prédéfinie. Je mettrai à jour ma réponse.
Torsten Walter

2
Pourquoi est-ce si bas ... C'est parfait et si flexible
Tofandel

2
C'est certainement le moyen le plus simple et la syntaxe la plus lisible.
Timar Ivo Batis

4

répondu ici aussi JavaScript Split Regular Expression garder le délimiteur

utiliser le modèle lookahead (? = pattern) dans l'exemple regex

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

cela vous donnera le résultat suivant.

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

Peut également être directement divisé

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

donnant le même résultat

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

Pourquoi ne pas se séparer immédiatement, comme dans la réponse acceptée de Jon?
Gordon

@Gordon ... :) Je pourrais juste faire ça ... mis à jour le code ... Cheers
Fry

2

J'ai apporté une modification à la réponse de jichi et l'ai mise dans une fonction qui prend également en charge plusieurs lettres.

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;
};

La 3ème méthode des réponses de jichi ne fonctionnait pas dans cette fonction, j'ai donc pris la 4ème méthode et supprimé les espaces vides pour obtenir le même résultat.

edit: deuxième méthode qui exclut un tableau pour séparer char1 ou 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);
    }
};

usage:

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

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

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

1

Une fonction d'extension divise la chaîne avec une sous-chaîne ou RegEx et le délimiteur est placé en fonction du deuxième paramètre en avant ou en arrière.

    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;
    };

Le test:

    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"]);
    });

0

J'utilise ceci:

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
  }, [])
}

Sauf que vous ne devriez pas jouer avec String.prototype, alors voici une version de fonction:

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
  }, [])
}

Vous pouvez donc faire:

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) )

Et vous vous retrouverez avec:

[
  "<br />&dagger; bbbb",
  "<br />&Dagger; cccc"
]
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.