Javascript和正则表达式:拆分字符串并保留分隔符


130

我有一个字符串:

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

我想用分隔符<br />和一个特殊字符分隔此字符串。

为此,我正在使用:

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

我得到了我所需要的,除了丢失了分隔符。这是示例:http : //jsfiddle.net/JwrZ6/1/

如何保留定界符?


如果您事先知道分隔符,为什么不这样做... var delim = "<br/>";
Andreas Wong

谢谢@SiGanteng,我知道预先定界符,但在我的示例中无法使用它。我需要将定界符保留为<br />,后跟特殊字符,因为有时我可以有一个<br />,其后没有特殊字符,并且不必拆分此字符。
米洛什

2
好问题,我有类似的情况,知道分隔符无济于事。我正在拆分“]&[”。因此,实际上我的定界符是“&”,但在该定界符上的划分不够精确,我需要在方括号内确定适当的划分。但是,我需要将这些括号放回拆分字符串中。两侧各1个。
PandaWood

Answers:


103

使用(正)lookahead,以便正则表达式断言特殊字符存在,但实际上并不与之匹配:

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

实际观看:

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


当我使用此代码时,它0在每个字符串的末尾添加一个
键盘战士,

2
在您提供的链接中,我找不到任何有关积极前瞻的信息。
Paul Chris Jones

@PaulJones内容在此期间已被移动。感谢您通知我,我修复了链接。
乔恩

178

我遇到了类似但略有不同的问题。无论如何,这是在三种情况下放置指示符的示例。

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

警告:第四个仅可用于分割单个字符。ConnorsFan提出了一种替代方案

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

3
我一直在寻找类似于第三个示例的内容,但这仅在元素仅是一个字符的情况下才有效-否则它将拆分为单个字符。最后,我必须走乏味的RegExp.exec路线。
Gordon

2
我不明白为什么每个人都在使用/ g
Sarsaparilla

1
如何将这个正则表达式“ 1、2、3” .split(/(?!,)/ g)== [“ 1,”,“ 2,”,“ 3”]用作完整单词?例如“ foo1,foo2,foo3”
Waltari

你真是个天才!在哪里可以找到解释其工作方式的文档?您不需要g结束
pery mimon

1
.match这些示例的非贪婪解决方案的翻译: "11、22、33".match(/.*?、|.+$/g)-> ["11、", "22、", "33"]。注意/g修饰符对于匹配至关重要。
Beni Cherniavsky-Paskin

57

如果将定界符包装在括号中,它将成为返回数组的一部分。

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

根据要保留的部分,更改要匹配的子组

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

您可以通过忽略字母string.split(/()&#?[a-z0-9] +; / gi);的大小写来改善表达式。

而且您可以匹配这样的预定义组:\dequals [0-9]\wequals [a-zA-Z0-9_]。这意味着您的表情可能看起来像这样。

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

JavaScriptKit上有一个很好的正则表达式参考


4
更好的是,我不知道我们只能保留定界符的一部分。实际上,我只需要保留特殊的字符,就可以这样:string.split(/ <br \/>(&#?[a-zA-Z0-9] +;)/ g);
米洛什

1
您可以通过忽略单词的大小写来优化表达。或匹配预定义的字符类。我将更新我的答案。
Torsten Walter

2
为什么这么低..它的完美和如此灵活
Tofandel

2
这无疑是最简单的方法,也是最易读的语法。
Timar Ivo Batis

4

在这里也回答了 JavaScript拆分正则表达式保留定界符

在正则表达式示例中使用(?= pattern)超前模式

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

这将为您带来以下结果。

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

也可以直接拆分

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

给出相同的结果

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

为什么不像乔恩的公认答案那样立即分裂?
戈登

@Gordon ... :)我可以那样做...更新代码...干杯
Fry

1

扩展功能使用子字符串或RegEx拆分字符串,并根据前面或后面的第二个参数放置分隔符。

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

考试:

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

1

我对jichi的答案进行了修改,并将其放在也支持多个字母的函数中。

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

jichi的答案3rd方法在此功能中不起作用,因此我采用了4th方法,并删除了空白区域以得到相同的结果。

编辑:第二种方法,除了数组以拆分char1或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);
    }
};

用法:

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

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

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

0

我一直在用这个:

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

除了不应该与混为一谈String.prototype,所以这里是一个函数版本:

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

因此,您可以执行以下操作:

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

最后,您将得到:

[
  "<br />&dagger; bbbb",
  "<br />&Dagger; cccc"
]
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.