สตริงการแยกเท่านั้นในอินสแตนซ์แรกของตัวละครที่ระบุ


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

คำตอบ:


406

ใช้วงเล็บที่จับภาพ :

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

พวกเขาถูกกำหนดเป็น

หากseparatorมีวงเล็บที่จับได้ผลลัพธ์ที่ตรงกันจะถูกส่งกลับในอาร์เรย์

ดังนั้นในกรณีนี้เราต้องการแยกที่_.+(เช่นตัวแยกแบ่งเป็นสตริงย่อยที่ขึ้นต้นด้วย_) แต่ให้ผลลัพธ์มีส่วนหนึ่งของตัวแยกของเรา (เช่นทุกอย่างหลังจาก_)

ในตัวอย่างนี้คั่นเรา (จับคู่_(.+)) เป็น_luck_buddyและกลุ่มที่ถูกจับ (ภายในคั่น) lucky_buddyเป็น หากไม่มีวงเล็บจับluck_buddy(การจับคู่.+) จะไม่ถูกรวมไว้ในอาร์เรย์ผลลัพธ์เนื่องจากเป็นกรณีที่ง่ายsplitที่ตัวคั่นไม่รวมอยู่ในผลลัพธ์


21
คุณไม่จำเป็นต้องใช้ (?) เพียงใช้ /_(.+)/ เพื่อจับตัวละครเพิ่มอีก 1 ตัวหลังจากตัวอักษรแรก _
ทำเครื่องหมาย

3
สง่างามมาก. ทำงานเหมือนจับใจ ขอบคุณ.
Ofeargall

12
เพียงเพื่อให้ชัดเจนเหตุผลที่การแก้ปัญหานี้ทำงานเพราะทุกอย่างหลังจากที่แรก_ถูกจับคู่ในกลุ่มการจับภาพและได้รับการเพิ่มลงในรายการโทเค็นด้วยเหตุผลนั้น
อลันมัวร์

28
ใครรู้ว่าทำไมฉันถึงได้รับองค์ประกอบสตริงที่ว่างเปล่าด้วยสิ่งนี้ใน: "Aspect Ratio: 16:9".split(/:(.+)/)ออก:["Aspect Ratio", " 16:9", ""]
katy lavallee

4
@katylavallee - สิ่งนี้อาจช่วยได้: stackoverflow.com/questions/12836062/…เนื่องจากตัวคั่น": 16:9"คือไม่มีอะไรหลังจากตัวแยกดังนั้นจึงสร้างสตริงว่างที่ท้าย
Derek 朕會功夫

231

คุณต้องการนิพจน์และอาร์เรย์ปกติอะไร

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

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


5
string! == สตริง จาวาสคริปต์เป็นกรณี ๆ ไป
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 ให้ 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 จะไม่สามารถบอกได้ว่า RegExp นั้นยอดเยี่ยมเพียงใด คุณต้องไปหาประตูด้วยตัวเอง เมื่อคุณอยู่ที่นั่นคุณจะไม่หันกลับมามอง ถามฉันอีกครั้งในอีกไม่กี่ปีข้างหน้าและคุณจะบอกฉันว่ามันยอดเยี่ยมแค่ไหน
Christiaan Westerbeek

3
@yonas ทานยาเม็ดสีแดง!
frnhr

2
@yonas ใช่ใช้ยาเม็ดสีแดง! มันจะทำให้ชีวิตของคุณเร็วขึ้นแม้จะเป็นสายสั้น ๆ : jsperf.com/split-by-first-colon
Julian F. Weinert

15
ฮา! ฉันเขียนความคิดเห็นนี้เมื่อ 4 ปีที่แล้ว ฉันอยู่บนเครื่องกับ RegExp อย่างแน่นอนตอนนี้! :)
yonas

2
@yonas คุณดีกว่าทำไม่ได้ นิพจน์ทั่วไปเป็นที่น่ากลัวเมื่อคุณต้องการมัน ไม่ใช่กรณีที่นี่ ตรวจสอบการทดสอบที่อัปเดตแล้ว: jsperf.com/split-by-first-colon/2
metalim

11

แทนที่อินสแตนซ์แรกด้วยตัวยึดตำแหน่งที่ไม่ซ้ำกันแล้วแยกออกจากที่นั่น

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

["good","luck_buddy"]

สิ่งนี้มีประโยชน์มากขึ้นเมื่อต้องการแยกทั้งสองด้าน


2
สิ่งนี้ทำให้ข้อ จำกัด ที่ไม่จำเป็นในสตริง
Yan Foto

คำตอบนี้ใช้ได้สำหรับฉันเมื่อคำตอบทั้งหมดข้างต้นไม่ได้
GuitarViking

1
@ YanFoto คุณหมายถึงโดยใช้ '&'? มันอาจเป็นอะไรก็ได้
sebjwallace

2
@sebjwallace ไม่ว่าคุณจะเลือกแบบใดก็หมายความว่าคุณไม่สามารถมีตัวอักษรนั้นในสตริงได้ เช่น "fish & chips_are_great" ให้ [ปลา, ชิป, are_great] ฉันคิดว่า
Joe

@ Joe คุณสามารถใช้อะไรก็ได้แทน '&' - มันเป็นเพียงตัวอย่าง คุณสามารถแทนที่ _ การเกิดขึ้นครั้งแรกของ _ ด้วย¬หากคุณต้องการ ดังนั้น "fish & chips_are_great" จะแทนที่การเกิดขึ้นครั้งแรกของ _ ด้วย¬เพื่อให้ "fish & chips¬are_great" แล้วแยกโดย¬เพื่อรับ ["fish & chips", "are_great"]
sebjwallace

8

คุณสามารถใช้นิพจน์ทั่วไปเช่น:

var arr = element.split(/_(.*)/)
คุณสามารถใช้พารามิเตอร์ตัวที่สองซึ่งระบุขีด จำกัด ของการแบ่ง ie: var field = element.split ('_', 1) [1];

6
สิ่งนี้ระบุเฉพาะจำนวนไอเท็มแยกที่ถูกส่งคืนไม่ใช่จำนวนครั้งที่แยก 'good_luck_buddy'.split('_', 1);ผลตอบแทนเพียง['good']
อเล็กซ์ Vidal

ขอบคุณทำสมมติฐานในที่ อัปเดตโพสต์เพื่อใช้นิพจน์ทั่วไป
Chandu

ถูก(:?.*)ควรจะเป็นกลุ่มที่ไม่ได้จับ? ถ้าเป็นเช่นนั้นมันควรจะเป็น(?:.*)แต่ถ้าคุณแก้ไขมันคุณจะพบว่ามันใช้งานไม่ได้ (:?.*)ตรงกับตัวเลือก:ตามด้วยศูนย์หรือมากกว่าของตัวละครใด ๆ การแก้ปัญหานี้จบลงด้วยการทำงานด้วยเหตุผลเดียวกันกับที่ @ MarkF ทำ: ทุกอย่างหลังจากที่_เพิ่มเข้าไปในรายการโทเค็นแรกเพราะมันถูกจับคู่ในกลุ่มการจับภาพ (นอกจากนี้gตัวดัดแปลงจะไม่มีผลเมื่อใช้ใน regex แบบแยก)
Alan Moore

ขอบคุณไม่ได้ตระหนักถึงมัน อัปเดต Regex และลองใช้กับสถานการณ์จำลองสองสามอย่าง ...
Chandu

1
มันใช้งานไม่ได้ใน ie8 และฉันเปลี่ยนกลับไปเป็น indexOf และ substring
Igor Alekseev

5

ทุกวันนี้String.prototype.splitอนุญาตให้คุณ จำกัด จำนวนการแบ่งได้อย่างแน่นอน

str.split([separator[, 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('_');

ฉันเห็นว่าฟังก์ชั่นแยกไม่ได้ช่วย แต่ใช้ regex ดูเหมือนจะบรรลุสิ่งนี้ ควรระบุว่าคุณกำลังอ้างถึงฟังก์ชั่นแยกเองโดยกำเนิด
Dan Hanly

1
สิ่งที่น่าสนใจโซลูชันนี้จะช่วยแก้ปัญหานี้ให้กลายเป็นโซลูชันที่อ่าน / จัดการได้ง่ายขึ้น ในกรณีของฉันในการแปลงชื่อเต็มเป็นชื่อแรกและสุดท้าย (ใช่ข้อกำหนดของเราบังคับตรรกะนี้) โซลูชันนี้ทำงานได้ดีที่สุดและอ่านได้มากกว่าคนอื่น ๆ ขอบคุณ
Sukima

สิ่งนี้ไม่เป็นความจริงอีกต่อไป :)
Kraken

3

ฉันต้องการสตริงสองส่วนดังนั้น regex lookbehind ช่วยฉันด้วยสิ่งนี้

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

การเปรียบเทียบประสิทธิภาพกับโซลูชันอื่น ๆ

คู่แข่งเพียงอย่างใกล้ชิดเป็นบรรทัดเดียวกันของรหัสยกเว้นใช้แทนsubstrslice

โซลูชันอื่น ๆ ที่ฉันพยายามเกี่ยวข้องsplitหรือRegExpได้รับผลกระทบอย่างมากและมีขนาดประมาณ 2 คำสั่งที่ช้าลง การใช้joinกับผลลัพธ์ของsplitหลักสูตรเพิ่มการลงโทษประสิทธิภาพเพิ่มเติม

ทำไมพวกเขาถึงช้าลง เมื่อใดก็ตามที่วัตถุหรืออาร์เรย์ใหม่จะต้องมีการสร้าง JS ต้องขอหน่วยความจำจากระบบปฏิบัติการ กระบวนการนี้ช้ามาก

ต่อไปนี้เป็นแนวทางทั่วไปบางประการในกรณีที่คุณกำลังไล่ตามมาตรฐาน:

  • การจัดสรรหน่วยความจำแบบไดนามิกใหม่สำหรับวัตถุ{}หรืออาร์เรย์[](เช่นเดียวกับที่splitสร้าง) จะมีค่าใช้จ่ายจำนวนมากในประสิทธิภาพ
  • RegExp การค้นหามีความซับซ้อนและช้ากว่าการค้นหาสตริง
  • หากคุณมีอาร์เรย์อยู่แล้วการทำลายอาร์เรย์จะเร็วพอ ๆ กับการทำดัชนีอย่างชัดเจนและดูดีมาก

การลบที่นอกเหนือจากอินสแตนซ์แรก

ต่อไปนี้เป็นโซลูชันที่จะแบ่งส่วนและรวมอินสแตนซ์ที่ n มันไม่เร็วนัก แต่สำหรับคำถามของ OP gobble(element, '_', 1)นั้นยังคงเร็วกว่า 2x RegExpหรือมากกว่าและมีsplitวิธีแก้ไขมากกว่า:

/*
`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. การทดสอบรันใน Chrome 70.0.3538.110 บน macOSX 10.14

มาเลย ... มันคือปี 2019 ... ผู้คนออกไปที่นั่นยังคงทำเครื่องหมายขนาดเล็กของสิ่งนี้หรือไม่?
Victor Schröder

ฉันเห็นด้วย. แม้ว่า microbenchmarking นั้นน่าสนใจเล็กน้อย แต่คุณควรพึ่งพาคอมไพเลอร์หรือนักแปลเพื่อการปรับให้เหมาะสมที่ใครจะรู้ Mb ใครบางคนกำลังอ่านข้อความนี้กำลังสร้างคอมไพเลอร์หรือใช้ ejs / ฝังตัวและไม่สามารถใช้ regex อย่างไรก็ตามนี่ดูดีกว่าสำหรับกรณีเฉพาะของฉันมากกว่า regex (ฉันจะลบ "วิธีแก้ปัญหาที่เร็วที่สุด")
TamusJRoyce

1

โซลูชันของ Mark F นั้นยอดเยี่ยม แต่เบราว์เซอร์รุ่นเก่าไม่รองรับ โซลูชันของ Kennebec นั้นยอดเยี่ยมและรองรับเบราว์เซอร์รุ่นเก่า แต่ไม่รองรับ regex

ดังนั้นหากคุณกำลังมองหาโซลูชันที่แยกสตริงของคุณเพียงครั้งเดียวเบราว์เซอร์รุ่นเก่าและรองรับ regex นี่คือโซลูชันของฉัน:

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

สำหรับผู้เริ่มต้นเช่นฉันที่ไม่คุ้นเคยกับ Expression วิธีแก้ปัญหานี้ใช้งานได้:

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

slice () วิธีการแยกส่วนหนึ่งของสตริงและส่งกลับสตริงใหม่และวิธีการ indexOf () ส่งกลับตำแหน่งของการเกิดขึ้นครั้งแรกที่พบค่าที่ระบุในสตริง


นี่ไม่ใช่วิธีแก้ปัญหา แต่เป็นวิธีการที่เหมาะสมในการทำ;)
Victor Schröder

1

ใช้replace()วิธีการสตริงกับregex :

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

regex นี้ตรงกับ 0 ตัวอักษรขึ้นไปก่อนตัวแรก_และ_ตัวมันเอง การแข่งขันจะถูกแทนที่ด้วยสตริงว่าง


document.body.innerHTMLส่วนที่นี่จะไม่ได้ผลอย่างสมบูรณ์
Victor Schröder

@ VictorSchröderคุณคาดหวังว่าจะเห็นผลลัพธ์ของตัวอย่างได้document.body.innerHTMLอย่างไร?
James T

1
document.bodyขึ้นอยู่กับ DOM ที่มีอยู่และจะไม่ทำงานในสภาพแวดล้อม JavaScript ที่แท้จริง console.logก็เพียงพอแล้วสำหรับวัตถุประสงค์นี้หรือเพียงแค่ทิ้งผลลัพธ์ไว้ในตัวแปรสำหรับการตรวจสอบ
Victor Schröder

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

ก่อนอื่นบังคับให้การแข่งขันเริ่มต้นจากจุดเริ่มต้นด้วย '^' จากนั้นจะจับคู่อักขระจำนวนใด ๆ ที่ไม่ใช่ '_' หรืออีกนัยหนึ่งคืออักขระทั้งหมดก่อนหน้า '_' ตัวแรก

The '?' หมายถึงตัวอักษรจำนวนน้อยที่สุดที่ทำให้การจับคู่รูปแบบทั้งหมดตรงกันโดยใช้ '. *?' เพราะตามด้วย '_' ซึ่งจะรวมอยู่ในการแข่งขันเป็นอักขระตัวสุดท้าย

ดังนั้นการแยกนี้ () ใช้ส่วนการจับคู่เช่น 'splitter' และลบออกจากผลลัพธ์ ดังนั้นจึงลบทุกอย่างจนถึงและรวม '_' แรกและให้ส่วนที่เหลือเป็นองค์ประกอบที่สองของผลลัพธ์ องค์ประกอบแรกคือ "" แทนส่วนก่อนหน้าส่วนที่ตรงกัน มันคือ "" เพราะการแข่งขันเริ่มต้นตั้งแต่ต้น

มี RegExps อื่น ๆ ที่ทำงานได้ดีเช่นเดียวกับ /_(.*)/ ที่ Chandu ให้ไว้ในคำตอบก่อนหน้านี้

/^.*?_/ มีประโยชน์ที่คุณสามารถเข้าใจในสิ่งที่มันทำโดยไม่ต้องรู้เกี่ยวกับบทบาทพิเศษในการจับกลุ่มที่เล่นด้วยการแทนที่ ()

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.