chuỗi phân tách chỉ trong trường hợp đầu tiên của ký tự được chỉ định


271

Trong mã của tôi, tôi tách một chuỗi dựa trên _và lấy mục thứ hai trong mảng.

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

Đưa good_luckvà cung cấp cho tôi luck. Hoạt động tuyệt vời!

Nhưng, bây giờ tôi có một lớp trông như thế good_luck_buddy. Làm cách nào để có được javascript của tôi để bỏ qua cái thứ hai _và đưa cho tôi luck_buddy?

Tôi đã tìm thấy điều này var field = element.split(new char [] {'_'}, 2);trong câu trả lời ac # stackoverflow nhưng nó không hoạt động. Tôi đã thử nó tại jsFiddle ...

Câu trả lời:


406

Sử dụng dấu ngoặc đơn :

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

Chúng được định nghĩa là

Nếu separatorchứa dấu ngoặc đơn, kết quả khớp được trả về trong mảng.

Vì vậy, trong trường hợp này, chúng tôi muốn phân tách tại _.+(tức là dấu phân tách là một chuỗi con bắt đầu bằng _) nhưng cũng để kết quả chứa một phần của dấu tách của chúng tôi (tức là mọi thứ sau _).

Trong ví dụ này, dấu phân cách của chúng ta (khớp _(.+)) là _luck_buddyvà nhóm bị bắt (trong dấu phân cách) là lucky_buddy. Nếu không có dấu ngoặc đơn, luck_buddykết hợp (khớp .+) sẽ không được bao gồm trong mảng kết quả vì đây là trường hợp đơn giản splitmà các dấu tách không được đưa vào kết quả.


21
Bạn thậm chí không cần (?), Chỉ cần sử dụng /_(.+)/ để chụp thêm 1 ký tự sau lần đầu tiên _
Đánh dấu

3
Rất thanh lịch. Hoạt động như một lá bùa. Cảm ơn bạn.
Ofeargall

12
Nói rõ hơn, lý do giải pháp này hoạt động là vì mọi thứ sau lần đầu tiên _được khớp trong một nhóm bắt giữ và được thêm vào danh sách mã thông báo vì lý do đó.
Alan Moore

28
Bất cứ ai cũng biết lý do tại sao tôi nhận được một phần tử chuỗi trống bổ sung với điều này: in: "Aspect Ratio: 16:9".split(/:(.+)/)out:["Aspect Ratio", " 16:9", ""]
katy lavallee

4
@katylavallee - Điều này có thể giúp: stackoverflow.com/questions/12836062/NH Vì dải phân cách là ": 16:9", không có gì sau dấu phân cách, do đó tạo ra chuỗi trống ở cuối.
Derek 朕 會

232

Bạn cần các biểu thức và mảng thông thường để làm gì?

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

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


5
chuỗi! == Chuỗi. javascript là trường hợp nhạy cảm.
kennebec

3
Tôi nghĩ rằng đây là câu trả lời tốt nhất. cũng có thể lấy chuỗi sau giây _bằng cách viết:myString.substring( myString.indexOf('_', myString().indexOf('_') + 1) + 1 )
muratgozel

9
Câu trả lời xuất ra phần thứ hai của chuỗi. Điều gì nếu bạn muốn phần đầu tiên, quá? Với var str = "good_luck_buddy", res = str.split(/_(.+)/);bạn có được tất cả các phần:console.log(res[0]); console.log(res[1]);
nhật

1
@PeterLeger hãy chia nhỏ = [ string.substring(0, string.indexOf(options.divider)), string.substring(string.indexOf(options.divider) + 1) ]Bạn đã có nó. Ngoài ra với sự hỗ trợ của kim biến
Steffan

Đây là thiên tài!
kẹt dòng chảy

36

Tôi tránh RegExp bằng mọi giá. Đây là một điều bạn có thể làm:

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

18
Một người sợ RegExp không bao giờ có thể nói RegExp tuyệt vời như thế nào. Bạn cần phải tự tìm cửa. Khi bạn ở đó, bạn sẽ không bao giờ nhìn lại. Hỏi lại tôi sau vài năm nữa và bạn sẽ nói với tôi rằng nó tuyệt vời như thế nào.
Christiaan Westerbeek

3
@yonas Uống thuốc đỏ!
frnhr

2
@yonas Vâng, uống thuốc đỏ! Nó sẽ làm cho cuộc sống của bạn nhanh hơn, ngay cả đối với các chuỗi ngắn: jsperf.com/split-by-first-colon
Julian F. Weinert

15
Hà! Tôi đã viết bình luận này hơn 4 năm trước. Tôi chắc chắn đang ở trên tàu với RegExp ngay bây giờ! :)
yonas

3
@yonas bạn tốt hơn không. RegExp là tuyệt vời khi bạn cần nó . Không phải trường hợp ở đây. Kiểm tra kiểm tra cập nhật: jsperf.com/split-by-first-colon/2
metalim

11

Thay thế phiên bản đầu tiên bằng một trình giữ chỗ duy nhất sau đó phân tách từ đó.

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

["good","luck_buddy"]

Điều này hữu ích hơn khi cả hai bên của sự phân chia là cần thiết.


3
Điều này đặt một ràng buộc không cần thiết trên chuỗi.
Yan Foto

Câu trả lời này có hiệu quả với tôi khi tất cả các câu trả lời trên không có.
GuitarViking 21/07/17

1
@YanFoto bạn có nghĩa là bằng cách sử dụng '&'? Nó có thể là bất cứ điều gì.
sebjwallace

2
@sebjwallace Bất kể bạn chọn gì, điều đó có nghĩa là bạn không thể có ký tự đó trong chuỗi. Ví dụ: "fish & Chips_are_great" cho [cá, khoai tây chiên, are_great] tôi nghĩ vậy.
Joe

@Joe Bạn có thể sử dụng bất cứ thứ gì thay vì '&' - đó chỉ là một ví dụ. Bạn có thể thay thế lần xuất hiện đầu tiên của _ bằng ¬ nếu bạn muốn. Vì vậy, "cá & chips_are_great" sẽ thay thế sự xuất hiện đầu tiên của _ với ¬ để cung cấp cho "cá & chips¬are_great", sau đó chia bởi ¬ để có được [ "cá & khoai tây chiên", "are_great"]
sebjwallace

8

Bạn có thể sử dụng biểu thức chính quy như:

var arr = element.split(/_(.*)/)
Bạn có thể sử dụng tham số thứ hai chỉ định giới hạn của phần tách. tức là: var field = Element.split ('_', 1) [1];

6
Điều đó chỉ xác định có bao nhiêu mục được chia được trả lại, không phải bao nhiêu lần phân chia. 'good_luck_buddy'.split('_', 1);trở lại chỉ['good']
Alex Vidal

Cảm ơn đã đưa ra một giả định về điều đó. Cập nhật bài viết để sử dụng một biểu thức thông thường.
Chandu

Được (:?.*)cho là một nhóm không bắt giữ? Nếu vậy, nó sẽ như vậy, (?:.*)nhưng nếu bạn sửa nó, bạn sẽ thấy nó không còn hoạt động nữa. (:?.*)phù hợp với một tùy chọn :theo sau bằng 0 hoặc nhiều hơn bất kỳ ký tự nào. Giải pháp này kết thúc hoạt động với cùng lý do @ MarkF's: mọi thứ sau lần đầu tiên _được thêm vào danh sách mã thông báo vì nó được khớp trong một nhóm bắt giữ. (Ngoài ra, công cụ gsửa đổi không có hiệu lực khi được sử dụng trong regex chia nhỏ.)
Alan Moore

Cảm ơn, đã không nhận ra nó. Đã cập nhật Regex và dùng thử qua vài cảnh ...
Chandu

1
Nó không hoạt động trong eg8 và tôi chuyển trở lại indexOf và chuỗi con
Igor Alekseev

6

Giải pháp này hiệu quả với tôi

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"

HOẶC LÀ

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
Tuy nhiên, điều này không hoạt động nếu bộ chia có nhiều hơn 1 ký tự.
haykam

5

Ngày nay String.prototype.splitthực sự cho phép bạn giới hạn số lượng chia tách.

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

...

giới hạn tùy chọn

Một số nguyên không âm giới hạn số lượng phân chia. Nếu được cung cấp, phân tách chuỗi tại mỗi lần xuất hiện của dấu phân cách đã chỉ định, nhưng dừng khi các mục nhập giới hạn đã được đặt trong mảng. Bất kỳ văn bản còn sót lại không được bao gồm trong mảng.

Mảng có thể chứa ít mục hơn giới hạn nếu đạt đến cuối chuỗi trước khi đạt đến giới hạn. Nếu giới hạn là 0, không có sự phân tách nào được thực hiện.

báo trước

Nó có thể không hoạt động theo cách bạn mong đợi. Tôi đã hy vọng nó sẽ bỏ qua phần còn lại của các dấu phân cách, nhưng thay vào đó, khi đạt đến giới hạn, nó sẽ tách chuỗi còn lại một lần nữa, bỏ qua phần sau khi tách khỏi kết quả trả về.

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

Tôi đã hy vọng:

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

Tương tự ở đây. Có vẻ như PHP đang phân tách thành "đầu tiên" và "phần còn lại".
BananaAcid

4

String.splitThật không may , Javascript không có cách nào giới hạn số lần chia thực tế. Nó có một đối số thứ hai chỉ định có bao nhiêu mục phân chia thực tế được trả về, điều này không hữu ích trong trường hợp của bạn. Giải pháp sẽ là tách chuỗi, chuyển mục đầu tiên ra, sau đó nối lại các mục còn lại ::

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

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

Tôi thấy rằng chức năng phân tách không giúp ích gì, nhưng sử dụng regex dường như để đạt được điều này. Nó sẽ xác định rằng bạn đang đề cập đến chính chức năng Split.
Dan Hanly

1
Thật thú vị, Giải pháp này chắt lọc vấn đề xuống một giải pháp dễ đọc / dễ quản lý hơn. Trong trường hợp của tôi chuyển đổi tên đầy đủ thành tên đầu tiên và cuối cùng (vâng, yêu cầu của chúng tôi buộc logic này), giải pháp này hoạt động tốt nhất và dễ đọc hơn những cái khác. Cảm ơn
Sukima

Điều này không còn đúng nữa :)
Kraken

3

Tôi cần hai phần của chuỗi, vì vậy, regex lookbehind giúp tôi với điều này.

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


3

Với sự giúp đỡ của việc hủy bỏ nhiệm vụ, nó có thể dễ đọc hơn:

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

2

Giải pháp nhanh nhất?

Tôi đã chạy một số điểm chuẩn và giải pháp này đã thắng rất nhiều: 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);
};

So sánh hiệu suất với các giải pháp khác

Ứng cử viên gần gũi duy nhất là cùng một dòng mã, ngoại trừ sử dụng substrthay vì slice.

Các giải pháp khác mà tôi đã thử liên quan splithoặc RegExpđã đạt được hiệu suất lớn và chậm hơn khoảng 2 bậc . Tất nhiên, sử dụng joinkết quả của splitviệc thêm hình phạt hiệu suất.

Tại sao họ chậm hơn? Bất cứ khi nào một đối tượng hoặc mảng mới phải được tạo, JS phải yêu cầu một đoạn bộ nhớ từ HĐH. Quá trình này rất chậm.

Dưới đây là một số hướng dẫn chung, trong trường hợp bạn đang theo đuổi điểm chuẩn:

  • Phân bổ bộ nhớ động mới cho các đối tượng {}hoặc mảng [](như cái được splittạo) sẽ tốn rất nhiều hiệu năng.
  • RegExp tìm kiếm phức tạp hơn và do đó chậm hơn tìm kiếm chuỗi.
  • Nếu bạn đã có một mảng, việc phá hủy các mảng sẽ nhanh như việc lập chỉ mục chúng một cách rõ ràng và trông thật tuyệt vời.

Loại bỏ vượt quá phiên sơ thẩm

Đây là một giải pháp sẽ cắt ra và bao gồm cả thể hiện thứ n. Nó không hoàn toàn nhanh như vậy, nhưng theo câu hỏi của OP, gobble(element, '_', 1)vẫn nhanh hơn> 2 lần so với một giải pháp RegExphoặc splitcó thể làm được nhiều hơn:

/*
`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);
}

Với định nghĩa trên, gobble('path/to/file.txt', '/')sẽ đưa ra tên của tệp và gobble('prefix_category_item', '_', 1)sẽ loại bỏ tiền tố như giải pháp đầu tiên trong câu trả lời này.


  1. Các thử nghiệm đã được chạy trong Chrome 70.0.3538.110 trên macOSX 10.14.

Thôi nào ... Đó là năm 2019 ... Có phải mọi người ở ngoài đó vẫn thực sự vi mô đánh dấu loại này?
Victor Schröder

Tôi đồng ý. Mặc dù microbenchmarking hơi thú vị, bạn nên dựa vào trình biên dịch hoặc trình dịch để tối ưu hóa. Ai biết. Mb ai đó đang đọc cái này đang xây dựng trình biên dịch hoặc sử dụng ejs / nhúng và không thể sử dụng regex. Tuy nhiên, điều này có vẻ tốt hơn cho trường hợp cụ thể của tôi hơn là một regex. (Tôi sẽ xóa "giải pháp nhanh nhất")
TamusJRoyce

1

Giải pháp của Mark F là tuyệt vời nhưng nó không được hỗ trợ bởi các trình duyệt cũ. Giải pháp của Kennebec là tuyệt vời và được hỗ trợ bởi các trình duyệt cũ nhưng không hỗ trợ regex.

Vì vậy, nếu bạn đang tìm kiếm một giải pháp chỉ tách chuỗi của bạn một lần, được các trình duyệt cũ hỗ trợ và hỗ trợ regex, đây là giải pháp của tôi:

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

Đối với người mới bắt đầu như tôi chưa quen với Biểu thức chính quy, giải pháp khắc phục này đã có hiệu quả:

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

Phương thức lát () trích xuất một phần của chuỗi và trả về một chuỗi mới và phương thức indexOf () trả về vị trí xuất hiện đầu tiên của một giá trị được chỉ định trong chuỗi.


Đây không phải là một cách giải quyết, mà là một cách làm đúng đắn;)
Victor Schröder

1

Sử dụng chuỗi replace()phương pháp với một regex :

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

Regex này khớp 0 hoặc nhiều ký tự trước ký tự đầu tiên __chính nó. Trận đấu sau đó được thay thế bằng một chuỗi trống.


Phần document.body.innerHTMLở đây là hoàn toàn vô dụng.
Victor Schröder

@ VictorSchröder làm thế nào để bạn thấy đầu ra của đoạn trích mà không có document.body.innerHTML?
James T

2
document.bodyphụ thuộc vào DOM có mặt và nó sẽ không hoạt động trên môi trường JavaScript thuần túy. console.loglà đủ cho mục đích này hoặc chỉ đơn giản là để kết quả trong một biến để kiểm tra.
Victor Schröder

@ VictorSchröder Tôi không nghĩ nó sẽ gây ra nhiều nhầm lẫn, nhưng dù sao tôi cũng đã chỉnh sửa.
James T

0

Điều này làm việc với tôi trên Chrome + FF:

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

Nếu bạn cũng cần chìa khóa hãy thử điều này:

"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

Đây là một RegExp thực hiện thủ thuật.

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

Đầu tiên, nó buộc trận đấu bắt đầu lại từ đầu với '^'. Sau đó, nó khớp với bất kỳ số lượng ký tự nào không phải là '_', nói cách khác là tất cả các ký tự trước '_' đầu tiên.

Các '?' có nghĩa là số lượng ký tự tối thiểu làm cho toàn bộ mô hình khớp với '. *?' bởi vì nó được theo sau bởi '_', sau đó được đưa vào trận đấu như là nhân vật cuối cùng của nó.

Do đó, phần tách này () sử dụng một phần khớp như là 'bộ chia' của nó và loại bỏ nó khỏi kết quả. Vì vậy, nó loại bỏ mọi thứ cho đến và bao gồm cả '_' đầu tiên và cung cấp cho bạn phần còn lại là yếu tố thứ 2 của kết quả. Phần tử đầu tiên là "" đại diện cho phần trước phần phù hợp. Đó là "" vì trận đấu bắt đầu lại từ đầu.

Có những RegExps khác hoạt động giống như /_(.*)/ do Chandu đưa ra trong câu trả lời trước.

/ ^. *? _ / Có lợi ích là bạn có thể hiểu những gì nó làm mà không cần phải biết về vai trò đặc biệt mà các nhóm bắt giữ chơi với thay thế ().

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.