仅在指定字符的第一个实例上分割字符串


271

在我的代码中,我基于分割了一个字符串_并获取了数组中的第二项。

var element = $(this).attr('class');
var field = element.split('_')[1];

需要good_luck并为我提供luck。很棒!

但是,现在我有一堂课,看起来像good_luck_buddy。如何获得我的JavaScript忽略第二个_并给我luck_buddy

var field = element.split(new char [] {'_'}, 2);在ac#stackoverflow答案中找到了它,但是它不起作用。我在jsFiddle上尝试过...

Answers:


406

使用捕获括号

"good_luck_buddy".split(/_(.+)/)[1]
"luck_buddy"

它们被定义为

如果separator包含捕获括号,则在数组中返回匹配的结果。

因此,在这种情况下,我们希望在处进行分割_.+(即,分割分隔符是以开头的子字符串_),但还要让结果包含分隔符的某些部分(即之后的所有内容_)。

在此示例中,我们的分隔符(match _(.+))为_luck_buddy,捕获的组(分隔符内)为lucky_buddy。如果没有捕获括号,则luck_buddy(matching .+)不会包含在结果数组中,因为在这种情况下split,分隔符不包含在结果中很简单。


21
您甚至不需要(?),只需使用/_(.+)/在第一个_之后捕获另外1个字符
标记

3
十分优雅。奇迹般有效。谢谢。
2011年

12
明确地说,此解决方案起作用的原因是,第一个解决方案之后的所有内容都在_捕获组内匹配,并因此被添加到令牌列表中。
艾伦·摩尔

28
任何人都知道为什么我会得到一个额外的空字符串元素:in:"Aspect Ratio: 16:9".split(/:(.+)/)out:["Aspect Ratio", " 16:9", ""]
katy lavallee 2014年

4
@katylavallee-这可能会有所帮助:stackoverflow.com/questions/12836062/…由于分隔符为": 16:9",因此分隔符后没有任何内容,因此在末尾创建了空字符串。
德里克·朕会功夫,2014年

230

您需要什么正则表达式和数组?

myString = myString.substring(myString.indexOf('_')+1)

var myString= "hello_there_how_are_you"
myString = myString.substring(myString.indexOf('_')+1)
console.log(myString)


5
字符串!==字符串。javascript区分大小写。
kennebec

3
我认为这是最好的答案。也有可能在第二秒后_通过写入获得字符串:myString.substring( myString.indexOf('_', myString().indexOf('_') + 1) + 1 )
muratgozel

9
答案输出字符串的第二部分。如果您也需要第一部分怎么办?随着var str = "good_luck_buddy", res = str.split(/_(.+)/);你得到所有零件:console.log(res[0]); console.log(res[1]);
太阳

1
@PeterLeger let split = [ string.substring(0, string.indexOf(options.divider)), string.substring(string.indexOf(options.divider) + 1) ]那里有。同样在可变针的支持下
Steffan

这是天才!
–stickedoverflow

36

我不惜一切代价避免使用RegExp。这是您可以做的另一件事:

"good_luck_buddy".split('_').slice(1).join('_')

18
永远不会告诉一个担心RegExp的人。您需要自己找到门。到达那里后,您将永不回头。再过几年再问我,您会告诉mé有多棒。
克里斯蒂安·韦斯特贝克

3
@yonas服用红色药丸!
2015年

2
@yonas是的,服用红色药丸!即使是短字符串,它也能使您的生活更快:jsperf.com/split-by-first-冒号
朱利安·F·

15
哈!我4年前写了此评论。我现在肯定会参加RegExp!:)
yonas

2
@yonas你最好不要。RegExp在您需要时很棒。这里不是这样。检查更新的测试:jsperf.com/split-by-first-colon/2
metalim

11

用唯一的占位符替换第一个实例,然后从那里拆分。

"good_luck_buddy".replace(/\_/,'&').split('&')

["good","luck_buddy"]

当需要拆分的两侧时,此功能将更为有用。


2
这对字符串施加了不必要的约束。
Yan Foto 2016年

当以上所有答案均无效时,此答案对我有用。
GuitarViking '17年

1
@YanFoto您是说使用'&'吗?可能是任何东西。
sebjwallace

2
@sebjwallace不管您选择什么,都意味着您不能在字符串中包含该字符。例如,我认为“ fish&chips_are_great”给出[鱼,薯条,are_great]。

@Joe您可以使用任何东西代替'&'-这只是一个例子。如果需要,可以将_的第一个出现替换为¬。因此,“ fish&chips_are_great”将用替换_的第一个出现,以给出“ fish&chips_are_great”,然后将其除以¬以得到[“ fish&chips_are_great”]
sebjwallace

8

您可以使用如下正则表达式:

var arr = element.split(/_(.*)/)
您可以使用第二个参数来指定分割的限制。即:var field = element.split('_',1)[1];

6
这仅指定返回多少拆分项目,而不指定拆分次数。'good_luck_buddy'.split('_', 1);只返回['good']
Alex Vidal

谢谢对此做出了假设。更新了帖子以使用正则表达式。
Chandu

本来(:?.*)应该是一个不吸引人的团体?如果是这样,应该是(?:.*),但是如果您更正它,它将发现它不再起作用。 (:?.*)匹配一个可选:字符,后跟零个或多个任何字符。该解决方案最终以@MarkF的相同原因工作:第一个之后的所有内容都_添加到令牌列表中,因为它在捕获组中匹配。(此外,g在分割正则表达式中使用时,修饰符也无效。)
艾伦·摩尔

谢谢,没意识到。更新了正则表达式,并尝试了几种方案...
Chandu

1
它在ie8中不起作用,我切换回indexOf和子字符串
Igor Alekseev,2012年

5

如今String.prototype.split确实确实允许您限制拆分次数。

str.split([separator[, limit]])

...

限制可选

一个非负整数,用于限制拆分数。如果提供,则在每次出现指定的分隔符时拆分字符串,但是在将限制条目放置在数组中时停止。数组中完全不包含任何剩余的文本。

如果在达到限制之前已到达字符串的末尾,则数组中的条目可能少于限制。如果limit为0,则不执行拆分。

警告

它可能无法按您期望的方式工作。我希望它只会忽略其余的定界符,但是,当达到限制时,它将再次拆分剩余的字符串,并从返回结果中删除拆分后的部分。

let str = 'A_B_C_D_E'
const limit_2 = str.split('_', 2)
limit_2
(2) ["A", "B"]
const limit_3 = str.split('_', 3)
limit_3
(3) ["A", "B", "C"]

我希望:

let str = 'A_B_C_D_E'
const limit_2 = str.split('_', 2)
limit_2
(2) ["A", "B_C_D_E"]
const limit_3 = str.split('_', 3)
limit_3
(3) ["A", "B", "C_D_E"]

同样在这里。好像PHP分为“第一”和“其余”。
BananaAcid

5

这个解决方案对我有用

var str = "good_luck_buddy";
var index = str.indexOf('_');
var arr = [str.slice(0, index), str.slice(index + 1)];

//arr[0] = "good"
//arr[1] = "luck_buddy"

要么

var str = "good_luck_buddy";
var index = str.indexOf('_');
var [first, second] = [str.slice(0, index), str.slice(index + 1)];

//first = "good"
//second = "luck_buddy"

但是,如果拆分器的字符数超过1,则此方法不起作用。
haykam

4

String.split不幸的是,Javascript 无法限制实际的拆分次数。它有第二个参数,它指定要返回多少实际拆分项目,这在您的情况下没有用。解决方案是拆分字符串,将第一个项目移开,然后重新加入其余项目:

var element = $(this).attr('class');
var parts = element.split('_');

parts.shift(); // removes the first item from the array
var field = parts.join('_');

我看到split函数没有帮助,但是使用正则表达式似乎可以达到目的。它应该指定您本地引用的是Split函数本身。
Dan Hanly '02

1
有趣的是,此解决方案将问题简化为更具可读性/可管理性的解决方案。在我将全名转换为姓氏和姓氏的情况下(是的,我们的要求强制采用这种逻辑),此解决方案效果最好,并且比其他解决方案更具可读性。谢谢
Sukima

这不再是真的:)
Kraken

3

我需要字符串的两个部分,因此,正则表达式在后面帮助我。

const full_name = 'Maria do Bairro';
const [first_name, last_name] = full_name.split(/(?<=^[^ ]+) /);
console.log(first_name);
console.log(last_name);


3

借助解构分配,可以提高可读性:

let [first, ...rest] = "good_luck_buddy".split('_')
rest = rest.join('_')

2

最快的解决方案?

我运行了一些基准测试,此解决方案获得了巨大的成功:1

str.slice(str.indexOf(delim) + delim.length)

// as function
function gobbleStart(str, delim) {
    return str.slice(str.indexOf(delim) + delim.length);
}

// as polyfill
String.prototype.gobbleStart = function(delim) {
    return this.slice(this.indexOf(delim) + delim.length);
};

与其他解决方案的性能比较

唯一接近的竞争者是同一行代码,不同之处substr在于使用代替slice

我尝试过涉及splitRegExp的其他解决方案对性能造成了很大的影响,但速度却慢了两个数量级。使用join上的结果split,当然,增加了一个额外的性能损失。

他们为什么慢一些?每当必须创建新对象或数组时,JS都必须从OS请求大块内存。这个过程很慢。

如果您正在追逐基准测试,则以下是一些一般准则:

  • 为对象{}或数组[](如split创建的对象)分配新的动态内存会花费很多性能。
  • RegExp 搜索比字符串搜索更为复杂,因此速度较慢。
  • 如果已经有一个数组,则对数组进行解构的速度与显式索引它们的速度差不多,并且看起来很棒。

超出首次实例

这是一个解决方案,可分割并包括第n个实例。它的速度不尽如人意,但是在OP的问题上,gobble(element, '_', 1)它仍然比a RegExpsplit解决方案快2倍以上,并且可以执行更多操作:

/*
`gobble`, given a positive, non-zero `limit`, deletes
characters from the beginning of `haystack` until `needle` has
been encountered and deleted `limit` times or no more instances
of `needle` exist; then it returns what remains. If `limit` is
zero or negative, delete from the beginning only until `-(limit)`
occurrences or less of `needle` remain.
*/
function gobble(haystack, needle, limit = 0) {
  let remain = limit;
  if (limit <= 0) { // set remain to count of delim - num to leave
    let i = 0;
    while (i < haystack.length) {
      const found = haystack.indexOf(needle, i);
      if (found === -1) {
        break;
      }
      remain++;
      i = found + needle.length;
    }
  }

  let i = 0;
  while (remain > 0) {
    const found = haystack.indexOf(needle, i);
    if (found === -1) {
      break;
    }
    remain--;
    i = found + needle.length;
  }
  return haystack.slice(i);
}

使用上面的定义,gobble('path/to/file.txt', '/')将给出文件名,并且gobble('prefix_category_item', '_', 1)将删除该前缀,就像该答案中的第一个解决方案一样。


  1. 测试是在macOSX 10.14的Chrome 70.0.3538.110中运行的。

来吧...这是2019年...人们真的还在对这种事情进行微基准测试吗?
维克多·施罗德

我同意。尽管微基准测试有点有趣,但是您应该依靠编译器或翻译器进行优化。mb有人读这正在构建一个编译器或使用ejs /嵌入式并且不能使用正则表达式。但是,对于我的特定情况,这看起来比正则表达式更好。(我将删除“最快的解决方案”)
TamusJRoyce,

1

Mark F的解决方案很棒,但是旧的浏览器不支持。Kennebec的解决方案很棒,并且受旧浏览器支持,但不支持正则表达式。

因此,如果您正在寻找一种仅将字符串拆分一次的解决方案,那是旧浏览器所支持的,并且支持正则表达式,那么这就是我的解决方案:

String.prototype.splitOnce = function(regex)
{
    var match = this.match(regex);
    if(match)
    {
        var match_i = this.indexOf(match[0]);
        
        return [this.substring(0, match_i),
        this.substring(match_i + match[0].length)];
    }
    else
    { return [this, ""]; }
}

var str = "something/////another thing///again";

alert(str.splitOnce(/\/+/)[1]);


1

对于像我这样不习惯使用正则表达式的初学者,此解决方法有效:

   var field = "Good_Luck_Buddy";
   var newString = field.slice( field.indexOf("_")+1 );

slice()方法提取字符串的一部分并返回新的字符串,indexOf()方法返回在字符串中首次发现指定值的位置。


这不是一个解决办法,但这样做的正确方法;)
维克多·施罗德

1

将字符串replace()方法与正则表达式一起使用:

var result = "good_luck_buddy".replace(/.*?_/, "");
console.log(result);

此正则表达式在第一个__本身之前匹配0个或多个字符。然后将匹配项替换为空字符串。


document.body.innerHTML这里的部分完全没有用。
维克多·施罗德

@VictorSchröder您如何期望不查看该代码段的输出document.body.innerHTML
詹姆斯·T

1
document.body取决于要显示的DOM,并且不能在纯JavaScript环境中使用。console.log足够用于此目的,或者只是将结果留在变量中进行检查。
维克多·施罗德

@VictorSchröder我认为这不会引起太多混乱,但是我还是进行了编辑。
James T

0

这在Chrome + FF上对我有用:

"foo=bar=beer".split(/^[^=]+=/)[1] // "bar=beer"
"foo==".split(/^[^=]+=/)[1] // "="
"foo=".split(/^[^=]+=/)[1] // ""
"foo".split(/^[^=]+=/)[1] // undefined

如果您还需要密钥,请尝试以下操作:

"foo=bar=beer".split(/^([^=]+)=/) // Array [ "", "foo", "bar=beer" ]
"foo==".split(/^([^=]+)=/) // [ "", "foo", "=" ]
"foo=".split(/^([^=]+)=/) // [ "", "foo", "" ]
"foo".split(/^([^=]+)=/) // [ "foo" ]

//[0] = ignored (holds the string when there's no =, empty otherwise)
//[1] = hold the key (if any)
//[2] = hold the value (if any)

0

这是一个完成此操作的RegExp。

'good_luck_buddy' . split(/^.*?_/)[1] 

首先,它强制匹配从'^'开始。然后它匹配任意数量的非'_'字符,换句话说,第一个'_'之前的所有字符。

'?' 表示使整个模式匹配的最小字符数由'。*?'匹配 因为它后面跟有“ _”,然后将其作为最后一个字符包含在匹配项中。

因此,此split()使用此类匹配部分作为其“分割器”,并将其从结果中删除。因此,它将删除直到并包括第一个“ _”的所有内容,并为您提供其余内容作为结果的第二个元素。第一个元素是“”,表示匹配的零件之前的零件。之所以是“”,是因为比赛是从头开始的。

还有其他RegExps可以像Chandu在先前的答案中给出的/_(.*)/一样工作。

/^.*?__/的好处是您可以了解它的功能,而不必了解捕获组在replace()中扮演的特殊角色。

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.