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

Posted

技术标签:

【中文标题】仅在指定字符的第一个实例上拆分字符串【英文标题】:split string only on first instance of specified character 【发布时间】:2011-06-04 05:05:07 【问题描述】:

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

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

接受good_luck 并为我提供luck。效果很好!

但是,现在我有一个看起来像 good_luck_buddy 的类。如何让我的 javascript 忽略第二个 _ 并给我 luck_buddy

我在 c# *** 答案中找到了这个var field = element.split(new char [] '_', 2);,但它不起作用。我在 jsFiddle 上试了一下...

【问题讨论】:

【参考方案1】:

使用capturing parentheses:

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

它们被定义为

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

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

在这个例子中,我们的分隔符(匹配_(.+))是_luck_buddy,捕获的组(在分隔符内)是lucky_buddy。如果没有捕获括号,luck_buddy(匹配 .+)将不会包含在结果数组中,因为简单的split 就是这种情况,结果中不包含分隔符。

【讨论】:

你甚至不需要 (?),只需使用 /_(.+)/ 在第一个 _ 之后再捕获 1 个字符 非常优雅。奇迹般有效。谢谢。 要明确一点,这个解决方案之所以有效,是因为第一个 _ 之后的所有内容都在捕获组中匹配,并因此被添加到令牌列表中。 任何人都知道为什么我会得到一个额外的空字符串元素:in:"Aspect Ratio: 16:9".split(/:(.+)/) out:["Aspect Ratio", " 16:9", ""] @katylavallee - 这可能会有所帮助:***.com/questions/12836062/… 由于分隔符是 ": 16:9",因此分隔符后面没有任何内容,因此在末尾创建了空字符串。【参考方案2】:

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

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

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

【讨论】:

字符串!==字符串。 javascript 区分大小写。 我认为这是最好的答案。也可以在第二个 _ 之后获取字符串:myString.substring( myString.indexOf('_', myString().indexOf('_') + 1) + 1 ) 答案输出字符串的第二部分。如果你也想要第一部分怎么办?使用var str = "good_luck_buddy", res = str.split(/_(.+)/);您可以获得所有部分:console.log(res[0]); console.log(res[1]); @PeterLeger let split = [ string.substring(0, string.indexOf(options.divider)), string.substring(string.indexOf(options.divider) + 1) ] 你有它。还支持可变针 这是天才!【参考方案3】:

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

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

【讨论】:

害怕RegExp的人永远不会被告知RegExp有多棒。你需要自己找到门。一旦你到了那里,你就永远不会回头。几年后再问我,你会告诉我它有多棒。 @yonas 服用红色药丸! @yonas 是的,吃红色药丸!它会让你的生活更快,即使是短字符串:jsperf.com/split-by-first-colon 哈!我在 4 多年前写了这个评论。我现在绝对支持 RegExp! :) @yonas 你最好不要。当您需要它时,RegExp 非常棒。不是这里的情况。检查更新的测试:jsperf.com/split-by-first-colon/2【参考方案4】:

在解构赋值的帮助下,它可以更具可读性:

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

【讨论】:

ES6 的最佳答案 嗯,完美程度取决于rest 有多少分隔符。 超级好用且非常灵活。【参考方案5】:

获取字符串中第一个键和剩余部分的简单 ES6 方法是:

 const [key, ...rest] = "good_luck_buddy".split('_')
 const value = rest.join('_')
 console.log(key, value) // good, luck_buddy

【讨论】:

【参考方案6】:

现在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 正在分裂为“第一”和“休息”。【参考方案7】:

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

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

["good","luck_buddy"]

这在需要拆分两边时更有用。

【讨论】:

这对字符串施加了不必要的约束。 当上述所有答案都不起作用时,这个答案对我有用。 @YanFoto 你的意思是使用'&'?可以是任何东西。 @sebjwallace 无论您选择什么,这意味着您不能在字符串中包含该字符。例如。我认为“fish&chips_are_great”给出了[fish,chips,are_great]。 @Joe 你可以使用任何东西来代替'&'——这只是一个例子。如果需要,您可以将第一次出现的 _ 替换为 ¬。因此,“fish&chips_are_great”会将第一次出现的 _ 替换为 ¬ 以给出“fish&chips¬are_great”,然后除以 ¬ 以获得 ["fish&chips","are_great"]【参考方案8】:

这个解决方案对我有用

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 个,这将不起作用。【参考方案9】:

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

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

【讨论】:

只指定返回多少拆分项,不指定拆分多少次。 'good_luck_buddy'.split('_', 1); 只返回 ['good'] 感谢对此做出了假设。更新帖子以使用正则表达式。 (:?.*) 应该是非捕获组吗?如果是这样,它应该是(?:.*),但如果你更正它,你会发现它不再起作用了。 (:?.*) 匹配可选的:,后跟零个或多个任意字符。该解决方案最终以与@MarkF 相同的原因起作用:第一个_ 之后的所有内容都被添加到令牌列表中,因为它在捕获组中匹配。 (此外,g 修饰符在用于拆分正则表达式时无效。) 谢谢,没意识到。更新了正则表达式并尝试了几个场景...... 它在 ie8 中不起作用,我切换回 indexOf 和 substring【参考方案10】:

我需要字符串的两个部分,所以,regex lookbehind 帮我解决这个问题。

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

【讨论】:

最佳答案在这里!【参考方案11】:

非正则表达式解决方案

我跑了一些benchmarks,这个解决方案大获全胜: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

我尝试过的涉及 splitRegExps 的其他解决方案对性能造成了很大影响,并且速度慢了大约 2 个数量级。在split 的结果上使用join 当然会增加额外的性能损失。

为什么它们变慢了?每当必须创建新对象或数组时,JS 都必须从操作系统请求一块内存。这个过程很慢。

以下是一些通用指南,以防您追求基准:

为对象 或数组 [](就像 split 创建的那样)分配新的动态内存会大大降低性能。 RegExp 搜索更复杂,因此比字符串搜索要慢。 如果您已经有一个数组,解构数组的速度与显式索引它们的速度差不多,而且看起来很棒。

在第一个实例之外删除

这是一个解决方案,它可以分割到并包括第 n 个实例。它不是那么快,但在 OP 的问题上,gobble(element, '_', 1) 仍然比 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) 将像此答案中的第一个解决方案一样删除前缀。


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

【讨论】:

来吧……现在是 2019 年……人们真的还在做微基准测试吗? 我同意。尽管微基准测试有点有趣,但您应该依赖编译器或翻译器进行优化。谁知道呢。 Mb 有人阅读本文正在构建编译器或使用 ejs / 嵌入式并且不能使用正则表达式。但是,对于我的具体情况,这看起来比正则表达式更好。 (我会删除“最快的解决方案”) 我毫不怀疑 JIT 编译器会有所帮助。然而,这些测试表明,使用简单的字符串函数仍然可以提高性能(并且避免使用复杂、容易出错的正则表达式)。 与多个嵌套循环、分支和索引跟踪相比,一个简单的正则表达式到底有多复杂?宝贝,遇见洗澡水。你们将一起去旅行。 这是我一直在寻找的单线。至于正则表达式基准测试,您是否尝试将正则表达式本身移出测试循环进入(模块)全局变量?这通常会有所帮助,因此差异不会那么令人印象深刻。至于人们对事物进行微基准测试,很高兴拥有一个有效的、经过良好测试的快速版本的 split-with-a-limit,Javascript 明显缺乏,但其他语言通常在它们的标准库中。有时这些东西比简单更重要。【参考方案12】:

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

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

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

【讨论】:

我看到拆分功能没有帮助,但使用正则表达式似乎可以实现这一点。它应该指定您在本机地引用 Split 函数本身。 有趣,这个解决方案将问题提炼成一个更具可读性/可管理性的解决方案。在我将全名转换为第一个和最后一个的情况下(是的,我们的要求强制了这个逻辑)这个解决方案效果最好,并且比其他解决方案更具可读性。谢谢 这不再是真的 :)【参考方案13】:

使用字符串replace() 方法和regex:

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

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

【讨论】:

这里的document.body.innerhtml 部分完全没用。 @VictorSchröder 如果没有document.body.innerHTML,您希望如何看到 sn-p 的输出? document.body 取决于要存在的 DOM,它不适用于纯 JavaScript 环境。 console.log 足以达到此目的,或者干脆将结果留在变量中以供检查。 @VictorSchröder 我认为这不会造成太多混乱,但我还是编辑了。【参考方案14】:

这是一个可以解决问题的正则表达式。

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

首先它强制匹配从 以“^”开头。然后它匹配任何数字 不是'_'的字符,换句话说 第一个“_”之前的所有字符。

“?”表示最少的字符数 使整个模式匹配的是 由 '.*?' 匹配因为它被遵循 通过'_',然后将其包含在匹配中 作为它的最后一个字符。

因此这个 split() 使用了这样的匹配 部分作为它的“分离器”并将其从 结果。所以它删除了一切 直到并包括第一个 '_' 和 给你剩下的作为第二个元素 结果。第一个元素是“”代表 匹配部分之前的部分。它是 "" 因为匹配从头开始。

还有其他正则表达式可以作为 很像 Chandu 给出的 /_(.*)/ 在之前的回答中。

/^.*?_/ 有你的好处 可以理解它的作用 必须了解特殊角色 使用 replace() 捕获组。

【讨论】:

【参考方案15】:

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

【讨论】:

【参考方案16】:

对于像我这样不习惯正则表达式的初学者来说,这个变通解决方案很有效:

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

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

【讨论】:

这不是一种解决方法,而是一种正确的方法;)【参考方案17】:

如果您正在寻找更现代的方法:

let raw = "good_luck_buddy"

raw.split("_")
    .filter((part, index) => index !== 0)
    .join("_")

【讨论】:

【参考方案18】:

这应该很快

function splitOnFirst (str, sep) 
  const index = str.indexOf(sep);
  return index < 0 ? [str] : [str.slice(0, index), str.slice(index + sep.length)];


console.log(splitOnFirst('good_luck', '_')[1])
console.log(splitOnFirst('good_luck_buddy', '_')[1])

【讨论】:

嗯,我一直在找这个【参考方案19】:

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

【讨论】:

以上是关于仅在指定字符的第一个实例上拆分字符串的主要内容,如果未能解决你的问题,请参考以下文章

String.Split 仅在 C# 中的第一个分隔符上?

拆分字符串中的第一个逗号

仅按 golang 中的第一个元素拆分字符串

java - 拆分字符串后,数组中的第一个元素是啥?

如何在第一个逗号后拆分第一个竖线上的字符串?

如何在使用 opsworks 部署时仅在特定层中的第一个实例上运行命令?