Javascript und Regex: String teilen und das Trennzeichen behalten

130

Ich habe eine Zeichenfolge:

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

Und ich möchte diese Zeichenfolge mit dem Trennzeichen <br />gefolgt von einem Sonderzeichen teilen .

Um das zu tun, benutze ich Folgendes:

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

Ich bekomme, was ich brauche, außer dass ich das Trennzeichen verliere. Hier ist das Beispiel: http://jsfiddle.net/JwrZ6/1/

Wie kann ich das Trennzeichen behalten?

Miloš
quelle
Wenn Sie den Begrenzer vorher kennen, warum nicht einfach ... var delim = "<br/>";?
Andreas Wong
Vielen Dank @SiGanteng, ich kenne das vorherige Trennzeichen, aber ich kann es für mein Beispiel nicht zum Laufen bringen. Ich muss das Trennzeichen beibehalten, damit <br /> das Sonderzeichen folgt, da ich manchmal ein <br /> haben kann, dem das Sonderzeichen nicht folgt, und dieses muss nicht aufgeteilt werden.
Miloš
2
Gute Frage, ich habe einen ähnlichen Fall, in dem es nicht hilft, das Trennzeichen zu kennen. Ich teile auf "] & [". Mein Trennzeichen ist also wirklich "&", aber die Aufteilung ist nicht präzise genug. Ich muss die Klammern auf beiden Seiten erhalten, um eine korrekte Aufteilung zu bestimmen. Ich brauche diese Klammern jedoch wieder in meinen geteilten Zeichenfolgen. Jeweils 1 auf beiden Seiten.
PandaWood

Antworten:

103

Verwenden Sie (positives) Lookahead, damit der reguläre Ausdruck bestätigt, dass das Sonderzeichen vorhanden ist, aber nicht mit ihm übereinstimmt:

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

Sehen Sie es in Aktion:

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

Jon
quelle
Wenn ich diesen Code verwende, fügt er 0am Ende jeder Zeichenfolge ein hinzu
Keyboard-Warrior
2
Ich kann in dem von Ihnen angegebenen Link nichts über positive Lookaheads finden.
Paul Chris Jones
@PaulJones Der Inhalt wurde in der Zwischenzeit verschoben. Vielen Dank, dass Sie mich informiert haben. Ich habe den Link repariert.
Jon
178

Ich hatte ein ähnliches, aber leicht unterschiedliches Problem. Hier sind Beispiele für drei verschiedene Szenarien, in denen der Deliminator aufbewahrt werden soll.

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

Warnung: Die vierte Funktion funktioniert nur zum Teilen einzelner Zeichen. ConnorsFan bietet eine Alternative :

// Split a path, but keep the slashes that follow directories
var str = 'Animation/rawr/javascript.js';
var tokens = str.match(/[^\/]+\/?|\//g);
jichi
quelle
3
Ich habe nach so etwas wie dem dritten Beispiel gesucht, aber das funktioniert nur, wenn die Elemente nur ein Zeichen sind - sonst wird es in einzelne Zeichen aufgeteilt. Am Ende musste ich die mühsame Route RegExp.exec gehen .
Gordon
2
Ich verstehe nicht, warum jeder / g verwendet
Sarsaparilla
1
Wie würde dieser reguläre Ausdruck "1、2、3" .split (/ (?! 、) / g) == ["1 、", "2 、", "3"] für vollständige Wörter verwendet? Zum Beispiel "foo1, foo2, foo3"
Waltari
Du bist ein Genie!. Wo finden Sie die Dokumentation, die die Funktionsweise erklärt? Sie nicht die Notwendigkeit gein , das Ende
pery mimon
1
Übersetzung der .matchnicht gierigen Lösung für diese Beispiele: "11、22、33".match(/.*?、|.+$/g)-> ["11、", "22、", "33"]. Der /gNotenmodifikator ist entscheidend für die Übereinstimmung.
Beni Cherniavsky-Paskin
57

Wenn Sie das Trennzeichen in Klammern setzen, ist es Teil des zurückgegebenen Arrays.

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

Abhängig davon, welchen Teil Sie behalten möchten, ändern Sie die Untergruppe, mit der Sie übereinstimmen

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

Sie können den Ausdruck verbessern, indem Sie den Fall der Buchstaben string.split (/ () & #? [A-z0-9] +; / gi) ignorieren.

Und Sie können für vordefinierte Gruppen wie diese übereinstimmen: \dgleich [0-9]und \wgleich [a-zA-Z0-9_]. Dies bedeutet, dass Ihr Ausdruck so aussehen könnte.

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

Es gibt eine gute Regular Expression Referenz auf JavaScriptKit .

Torsten Walter
quelle
4
Noch besser, ich weiß nicht, dass wir nur einen Teil des Begrenzers behalten können. Tatsächlich muss ich nur das Sonderzeichen behalten, ich kann es damit machen: string.split (/ <br \/> (& #? [A-zA-Z0-9] +;) / g);
Miloš
1
Sie können Ihren Ausdruck optimieren, indem Sie den Fall von Wörtern ignorieren. Oder passen Sie zu einer vordefinierten Zeichenklasse. Ich werde meine Antwort aktualisieren.
Torsten Walter
2
Warum ist das so niedrig .. Es ist perfekt und so flexibel
Tofandel
2
Dies ist sicherlich der einfachste Weg und die am besten lesbare Syntax.
Timar Ivo Batis
4

beantwortete es hier auch JavaScript Split Regular Expression behalten das Trennzeichen

Verwenden Sie das Lookahead-Muster (? = Muster) im Regex-Beispiel

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

Dadurch erhalten Sie das folgende Ergebnis.

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

Kann auch direkt aufgeteilt werden

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

das gleiche Ergebnis geben

[ '500x500', '-11', '*90', '~1', '+1' ]
Braten
quelle
Warum nicht einfach sofort trennen, wie in Jons akzeptierter Antwort?
Gordon
@ Gordon ... :) Ich könnte das einfach tun ... den Code aktualisiert ... Cheers
Fry
1

Eine Erweiterungsfunktion teilt eine Zeichenfolge mit einem Teilstring oder RegEx und das Trennzeichen wird gemäß dem zweiten Parameter vor oder hinter dem Parameter gesetzt.

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

Der 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"]);
    });
Berezh
quelle
1

Ich habe Jichis Antwort geändert und in eine Funktion eingefügt, die auch mehrere Buchstaben unterstützt.

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

jichis Antworten Die dritte Methode würde in dieser Funktion nicht funktionieren, also habe ich die vierte Methode gewählt und die leeren Leerzeichen entfernt, um das gleiche Ergebnis zu erzielen.

edit: zweite Methode, die ein Array zum Teilen von char1 oder char2 ausnimmt

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

Verwendung:

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

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

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

Ich habe dies verwendet:

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

Abgesehen davon, dass Sie sich nicht damit anlegen sollten String.prototype, hier eine Funktionsversion:

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

So könnten Sie tun:

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

Und am Ende haben Sie:

[
  "<br />&dagger; bbbb",
  "<br />&Dagger; cccc"
]
Vasilevich
quelle