pisahkan string hanya pada instance pertama dari karakter tertentu

272

Dalam kode saya, saya membagi string berdasarkan _dan ambil item kedua dalam array.

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

Membawa good_luckdan memberi saya luck. Bagus sekali!

Tapi, sekarang aku punya kelas yang mirip good_luck_buddy. Bagaimana caranya agar javascript saya mengabaikan yang kedua _dan memberi saya luck_buddy?

Saya menemukan ini var field = element.split(new char [] {'_'}, 2);dalam jawaban ac # stackoverflow tetapi tidak berhasil. Saya mencobanya di jsFiddle ...

Ofeargall
sumber

Jawaban:

408

Gunakan tanda kurung :

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

Mereka didefinisikan sebagai

Jika separatorberisi tanda kurung, hasil yang cocok dikembalikan dalam array.

Jadi dalam hal ini kita ingin membelah _.+(yaitu pemisah terbagi menjadi sub-string yang dimulai dengan _) tetapi juga membiarkan hasilnya berisi beberapa bagian dari pemisah kita (yaitu semuanya setelah _).

Dalam contoh ini pemisah kami (matching _(.+)) adalah _luck_buddydan grup yang ditangkap (dalam separator) adalah lucky_buddy. Tanpa tanda kurung penangkapan luck_buddy(pencocokan .+) tidak akan dimasukkan dalam array hasil seperti halnya dengan sederhana splitbahwa pemisah tidak termasuk dalam hasil.

Menandai
sumber
21
Anda bahkan tidak perlu (?), Cukup gunakan /_(.+)/ untuk mengambil 1 karakter lagi setelah _ pertama
Tandai
3
Sangat elegan. Bekerja seperti pesona. Terima kasih.
Ofeargall
12
Untuk memperjelas, alasan solusi ini bekerja adalah karena semuanya setelah yang pertama _dicocokkan di dalam grup penangkap, dan ditambahkan ke daftar token karena alasan itu.
Alan Moore
28
Ada yang tahu mengapa saya mendapatkan elemen string ekstra kosong dengan ini: in: "Aspect Ratio: 16:9".split(/:(.+)/)out:["Aspect Ratio", " 16:9", ""]
katy lavallee
4
@katylavallee - Ini mungkin membantu: stackoverflow.com/questions/12836062/... Karena separator adalah ": 16:9", tidak ada apa-apa setelah separator, sehingga menciptakan string kosong di akhir.
Derek 朕 會 功夫
232

Untuk apa Anda perlu ekspresi dan array reguler?

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

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

kennebec
sumber
5
string! == String. javascript peka huruf besar-kecil.
kennebec
3
Saya pikir ini adalah jawaban terbaik. Anda juga bisa mendapatkan string setelah detik _dengan menulis:myString.substring( myString.indexOf('_', myString().indexOf('_') + 1) + 1 )
muratgozel
9
Jawabannya menghasilkan bagian kedua dari string. Bagaimana jika Anda menginginkan bagian pertama juga? Dengan var str = "good_luck_buddy", res = str.split(/_(.+)/);Anda mendapatkan semua bagian:console.log(res[0]); console.log(res[1]);
Sun
1
@PeterLeger let split = [ string.substring(0, string.indexOf(options.divider)), string.substring(string.indexOf(options.divider) + 1) ]Itu dia. Juga dengan dukungan jarum variabel
Steffan
Ini Genius!
stuckedoverflow
36

Saya menghindari RegExp di semua biaya. Inilah hal lain yang dapat Anda lakukan:

"good_luck_buddy".split('_').slice(1).join('_')
yonas
sumber
18
Orang yang takut pada RegExp tidak akan pernah tahu seberapa hebatnya RegExp. Anda perlu menemukan pintu sendiri. Setelah Anda di sana, Anda tidak akan pernah melihat ke belakang. Tanya saya lagi dalam beberapa tahun dan Anda akan memberi tahu saya betapa hebatnya itu.
Christiaan Westerbeek
3
@yonas Ambil pil merah!
frnhr
2
@yonas Ya, ambil pil merah! Ini akan membuat hidup Anda lebih cepat, bahkan untuk string pendek: jsperf.com/split-by-first-colon
Julian F. Weinert
15
Ha! Saya menulis komentar ini 4+ tahun yang lalu. Saya pasti bergabung dengan RegExp sekarang! :)
yonas
3
@Ya, kamu sebaiknya tidak. RegExp luar biasa ketika Anda membutuhkannya . Tidak demikian di sini. Periksa tes yang diperbarui: jsperf.com/split-by-first-colon/2
metalim
11

Ganti instance pertama dengan placeholder unik lalu pisahkan dari sana.

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

["good","luck_buddy"]

Ini lebih berguna ketika kedua sisi dari perpecahan diperlukan.

sebjwallace
sumber
3
Ini menempatkan kendala yang tidak perlu pada string.
Yan Foto
Jawaban ini bekerja untuk saya ketika semua jawaban di atas tidak.
GuitarViking
1
@YanFoto yang Anda maksud dengan menggunakan '&'? Itu bisa apa saja.
sebjwallace
2
@sebjwallace Apa pun yang Anda pilih, itu artinya Anda tidak dapat memiliki karakter itu di string. Misalnya "fish & chips_are_great" memberi [fish, chips, are_great] saya pikir.
Joe
@ Jo, Anda bisa menggunakan apa saja alih-alih '&' - itu hanya contoh. Anda bisa mengganti kemunculan pertama _ dengan ¬ jika Anda mau. Jadi "fish & chips_are_great" akan menggantikan kemunculan pertama _ dengan ¬ untuk memberikan "fish & chips¬are_great" kemudian dibagi ¬ untuk mendapatkan ["fish & chips", "are_great"]
sebjwallace
8

Anda dapat menggunakan ekspresi reguler seperti:

var arr = element.split(/_(.*)/)
Anda dapat menggunakan parameter kedua yang menentukan batas pemisahan. yaitu: var field = element.split ('_', 1) [1];
Chandu
sumber
6
Itu hanya menentukan berapa banyak item yang dipecah dikembalikan, bukan berapa kali terbelah. 'good_luck_buddy'.split('_', 1);kembali hanya['good']
Alex Vidal
Terima kasih membuat asumsi tentang itu. Memperbarui posting untuk menggunakan ekspresi reguler.
Chandu
Apakah (:?.*)seharusnya kelompok yang tidak menangkap? Jika demikian, seharusnya begitu (?:.*), tetapi jika Anda memperbaikinya Anda akan menemukannya tidak berfungsi lagi. (:?.*)cocok dengan opsional :diikuti oleh nol atau lebih dari karakter apa pun. Solusi ini akhirnya bekerja karena alasan yang sama dengan @ MarkF: semuanya setelah yang pertama _ditambahkan ke daftar token karena cocok dengan grup penangkap. (Juga, gpengubah tidak berpengaruh ketika digunakan dalam split regex.)
Alan Moore
Terima kasih, tidak menyadarinya. Memperbarui Regex dan mencobanya dalam beberapa skenario ...
Chandu
1
Itu tidak bekerja di ie8 dan saya beralih kembali ke indexOf dan substring
Igor Alekseev
6

Solusi ini berhasil untuk saya

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"

ATAU

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"
Darren Lee
sumber
1
Ini tidak berfungsi jika splitter memiliki lebih dari 1 karakter.
haykam
5

Saat String.prototype.splitini memang memungkinkan Anda untuk membatasi jumlah split.

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

...

batas opsional

Bilangan bulat non-negatif membatasi jumlah pemisahan. Jika disediakan, pisahkan string pada setiap kemunculan pemisah yang ditentukan, tetapi berhenti ketika entri batas telah ditempatkan dalam larik. Teks sisa tidak termasuk dalam array sama sekali.

Array mungkin mengandung lebih sedikit entri daripada batas jika ujung string tercapai sebelum batas tercapai. Jika batas 0, tidak ada pemisahan yang dilakukan.

peringatan

Mungkin tidak bekerja seperti yang Anda harapkan. Saya berharap itu hanya akan mengabaikan sisa pembatas, tetapi alih-alih, ketika mencapai batas, itu memisahkan string yang tersisa lagi, menghilangkan bagian setelah pemisahan dari hasil pengembalian.

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

Saya berharap untuk:

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"]
Kraken
sumber
Sama disini. Sepertinya PHP terbagi menjadi "pertama" dan "sisanya".
BananaAcid
4

String.splitSayangnya Javascript tidak memiliki cara untuk membatasi jumlah split yang sebenarnya. Ini memiliki argumen kedua yang menentukan berapa banyak item split aktual dikembalikan, yang tidak berguna dalam kasus Anda. Solusinya adalah dengan membagi string, menggeser item pertama, lalu bergabung kembali dengan item yang tersisa ::

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

parts.shift(); // removes the first item from the array
var field = parts.join('_');
Alex Vidal
sumber
Saya melihat bahwa fungsi split tidak membantu, tetapi menggunakan regex tampaknya mencapai ini. Ini harus menentukan bahwa Anda mengacu pada fungsi Split itu sendiri, secara asli.
Dan Hanly
1
Menarik, Solusi ini menyaring masalah menjadi solusi yang lebih mudah dibaca / dikelola. Dalam kasus saya mengubah nama lengkap menjadi pertama dan terakhir (ya persyaratan kami memaksa logika ini) solusi ini bekerja paling baik dan lebih mudah dibaca daripada yang lain. Terima kasih
Sukima
Ini tidak benar lagi :)
Kraken
3

Saya membutuhkan dua bagian string, jadi, lihat regex di belakang membantu saya dengan ini.

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

Édipo Costa Rebouças
sumber
3

Dengan bantuan penugasan yang merusak, itu bisa lebih mudah dibaca:

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

Solusi tercepat?

Saya menjalankan beberapa tolok ukur , dan solusi ini menang sangat besar: 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);
};

Perbandingan kinerja dengan solusi lain

Satu-satunya pesaing dekat adalah baris kode yang sama, kecuali menggunakan substrbukan slice.

Solusi lain yang saya coba libatkan splitatau RegExpmengalami kinerja besar dan sekitar 2 kali lipat lebih lambat. Menggunakan joinpada hasil split, tentu saja, menambah penalti kinerja tambahan.

Mengapa mereka lebih lambat? Setiap kali objek atau array baru harus dibuat, JS harus meminta sepotong memori dari OS. Proses ini sangat lambat.

Berikut adalah beberapa pedoman umum, jika Anda mengejar tolok ukur:

  • Alokasi memori dinamis baru untuk objek {}atau array [](seperti yang dibuat split) akan membutuhkan banyak biaya dalam kinerja.
  • RegExp pencarian lebih rumit dan karena itu lebih lambat daripada pencarian string.
  • Jika Anda sudah memiliki larik, merusak susunan array akan secepat pengindeksan mereka secara eksplisit, dan terlihat mengagumkan.

Menghapus di luar instance pertama

Inilah solusi yang akan mengiris hingga dan memasukkan instance ke-n. Ini tidak secepat, tetapi pada pertanyaan OP, gobble(element, '_', 1)masih> 2x lebih cepat dari solusi RegExpatau splitdan dapat melakukan lebih banyak:

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

Dengan definisi di atas, gobble('path/to/file.txt', '/')akan memberikan nama file, dan gobble('prefix_category_item', '_', 1)akan menghapus awalan seperti solusi pertama dalam jawaban ini.


  1. Pengujian dijalankan di Chrome 70.0.3538.110 di macOSX 10.14.
Chaim Leib Halbert
sumber
Ayo ... Ini 2019 ... Apakah orang-orang di luar sana benar-benar masih microbenchmarking hal semacam ini?
Victor Schröder
Saya setuju. Meskipun microbenchmarking sedikit menarik, Anda harus mengandalkan kompiler atau penerjemah untuk optimisasi. Siapa tahu. Mb seseorang membaca ini sedang membangun kompiler atau menggunakan ejs / tertanam dan tidak dapat menggunakan regex. Namun, ini terlihat lebih bagus untuk kasus spesifik saya daripada regex. (Saya akan menghapus "solusi tercepat")
TamusJRoyce
1

Solusi Mark F luar biasa tetapi tidak didukung oleh peramban lama. Solusi Kennebec luar biasa dan didukung oleh browser lama tetapi tidak mendukung regex.

Jadi, jika Anda mencari solusi yang memecah string Anda hanya sekali, yang didukung oleh browser lama dan mendukung regex, inilah solusi saya:

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

pmrotule
sumber
1

Untuk pemula seperti saya yang tidak terbiasa dengan Ekspresi Reguler, solusi pemecahan masalah ini berfungsi:

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

Metode slice () mengekstraksi bagian string dan mengembalikan string baru dan metode indexOf () mengembalikan posisi kemunculan yang ditemukan pertama dari nilai yang ditentukan dalam string.

MZulkarnain Jaranee
sumber
Ini bukan solusi, tetapi cara yang tepat untuk melakukannya;)
Victor Schröder
1

Gunakan replace()metode string dengan regex :

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

Regex ini cocok dengan 0 atau lebih karakter sebelum yang pertama _, dan karakter _itu sendiri. Pertandingan kemudian diganti dengan string kosong.

James T
sumber
Bagian di document.body.innerHTMLsini sama sekali tidak berguna.
Victor Schröder
@ VictorSchröder bagaimana Anda mengharapkan untuk melihat hasil potongan tanpa document.body.innerHTML?
James T
2
document.bodytergantung pada DOM untuk hadir dan tidak akan berfungsi pada lingkungan JavaScript murni. console.logsudah cukup untuk tujuan ini atau cukup meninggalkan hasilnya dalam variabel untuk diperiksa.
Victor Schröder
@ VictorSchröder Saya tidak berpikir itu akan menyebabkan banyak kebingungan, tapi saya tetap mengedit.
James T
0

Ini bekerja untuk saya di Chrome + FF:

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

Jika Anda juga membutuhkan kunci coba ini:

"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)
oriadam
sumber
0

Inilah satu RegExp yang melakukan trik.

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

Pertama itu memaksa pertandingan untuk memulai dari awal dengan '^'. Kemudian cocok dengan sejumlah karakter yang bukan '_', dengan kata lain semua karakter sebelum '_' pertama.

'?' berarti jumlah minimal karakter yang membuat seluruh kecocokan pola dicocokkan dengan '. *?' karena diikuti oleh '_', yang kemudian dimasukkan dalam pertandingan sebagai karakter terakhirnya.

Karenanya split ini () menggunakan bagian yang cocok sebagai 'splitter' dan menghapusnya dari hasil. Jadi itu menghapus semuanya sampai dan termasuk '_' pertama dan memberi Anda sisanya sebagai elemen ke-2 dari hasilnya. Elemen pertama adalah "" yang mewakili bagian sebelum bagian yang cocok. Itu "" karena pertandingan dimulai dari awal.

Ada RegExps lain yang berfungsi seperti /_(.*)/ yang diberikan oleh Chandu dalam jawaban sebelumnya.

/^.*?_/ memiliki manfaat yang bisa Anda pahami apa fungsinya tanpa harus tahu tentang peran khusus yang dimainkan kelompok dengan mengganti ().

Panu Logic
sumber