นิพจน์ปกติเพื่อรับสตริงระหว่างสองสตริงใน Javascript


166

ฉันพบโพสต์ที่คล้ายกันมาก แต่ฉันไม่สามารถรับนิพจน์ปกติของฉันได้ที่นี่

ฉันพยายามเขียนนิพจน์ทั่วไปซึ่งส่งคืนสตริงซึ่งอยู่ระหว่างสองสตริงอื่น ตัวอย่างเช่นฉันต้องการรับสตริงที่อยู่ระหว่างสตริง "cow" และ "milk"

วัวของฉันให้นมเสมอ

จะกลับมา

"ให้เสมอ"

นี่คือการแสดงออกที่ฉันได้ปะติดปะต่อเข้าด้วยกัน:

(?=cow).*(?=milk)

อย่างไรก็ตามสิ่งนี้จะส่งคืนสตริง "ให้เสมอวัว"


6
ฉันสะดุดคำถามเก่านี้และต้องการชี้แจงว่าทำไม testRE จึงเป็นอาร์เรย์ test.match ส่งกลับอาร์เรย์ที่มีดัชนีแรกเป็นการจับคู่ทั้งหมด (ซึ่งคือสตริงที่ตรงกับนมวัว (. *)) จากนั้นสตริงที่ติดกับทั้งหมดเช่น (. *) หากมีวงเล็บชุดที่สองที่พวกเขาต้องการ จากนั้นอยู่ใน testRE [2]
Salketer

4
โซลูชันนี้จะไม่ทำงานหากคุณค้นหาสตริงที่มีการขึ้นบรรทัดใหม่ ในกรณีเช่นนี้คุณควรใช้ "STRING_ONE ([\\ s \\ S] *?) STRING_TWO" stackoverflow.com/questions/22531252/…
Michael.Lumley

เพียงสำหรับการอ้างอิงวิธีการจับคู่กับนักพัฒนา
MD.millailla.org/en/docs/Web/JavaScript/Reference/ ......

คำตอบ:


183

lookahead ( (?=ส่วนนั้น) ไม่กินอินพุตใด ๆ มันคือการยืนยันความกว้างเป็นศูนย์ (เช่นเดียวกับการตรวจสอบขอบเขตและ lookbehinds)

คุณต้องการการแข่งขันปกติที่นี่เพื่อบริโภคcowส่วนนี้ ในการจับภาพส่วนระหว่างคุณใช้กลุ่มการจับภาพ (เพียงใส่ส่วนของรูปแบบที่คุณต้องการจับภายในวงเล็บ):

cow(.*)milk

ไม่จำเป็นต้องใช้ lookaheads เลย


26
เมื่อฉันทดสอบสิ่งนี้การแสดงออกของ Regex ที่ระบุจะรวมถึง "วัว" และ "นม" ...
TheCascadian

4
นี่เป็นขั้นตอนที่ขาดหายไป เมื่อคุณได้รับผลของการแข่งขันที่คุณจะต้องดึงข้อความที่ตรงกันของกลุ่มแรกที่มีการจับภาพไม่ได้ข้อความที่ตรงกันทั้งหมดด้วยmatched[1] matched[0]
Rory O'Kane

7
ใน Javascript คุณต้องการจริงที่จะใช้มากกว่า([\s\S]*?) (.*?)
Qian Chen

7
แม้ว่านี่จะเป็นเทคนิคที่มีประโยชน์ แต่ก็ลดลงเพราะ IMHO ไม่ใช่คำตอบที่ถูกต้องสำหรับคำถามเนื่องจากมี "วัว" และ "นม" ตามที่ระบุโดย @TheCascadian
Almir Campos

@AlmirCampos - หากฉันไม่เข้าใจผิดไม่มีทางที่จะทำการจับคู่นี้โดยไม่ต้องจับคู่ "วัว" และ "นม" (เนื่องจากคุณต้องการจับคู่สิ่งที่อยู่ระหว่างสอง) ปัญหาไม่ได้อยู่ใน RegEx แต่เป็นวิธีที่คุณจัดการในภายหลัง (ดังที่ Rory O'Kane พูดถึง) ไม่อย่างนั้นคุณสามารถจับคู่กับพื้นที่โดยรอบเท่านั้น - และนั่นจะให้ผลตอบแทนที่ผิดมากขึ้นใช่มั้ย
เกิด

69

นิพจน์ปกติเพื่อรับสตริงระหว่างสองสตริงใน JavaScript

วิธีการแก้ปัญหาที่สมบูรณ์แบบที่สุดที่จะทำงานในส่วนใหญ่ของกรณีใช้กลุ่มการจับภาพที่มีรูปแบบการจับคู่จุดขี้เกียจ อย่างไรก็ตามจุด.ใน regex JavaScript ไม่ตรงกับตัวละครแบ่งบรรทัดดังนั้นสิ่งที่จะทำงานในกรณี 100% เป็น[^]หรือ[\s\S]/ [\d\D]/ [\w\W]โครงสร้าง

ECMAScript 2018 และโซลูชันที่เข้ากันได้ที่ใหม่กว่า

ในสภาพแวดล้อมที่สนับสนุน JavaScript ECMAScript 2018 , sปรับปรุงช่วยให้.เพื่อให้ตรงกับถ่านใด ๆ รวมทั้งตัวอักษรเส้นแบ่งและเครื่องยนต์ regex สนับสนุน lookbehinds ของความยาวตัวแปร ดังนั้นคุณอาจใช้ regex เช่น

var result = s.match(/(?<=cow\s+).*?(?=\s+milk)/gs); // Returns multiple matches if any
// Or
var result = s.match(/(?<=cow\s*).*?(?=\s*milk)/gs); // Same but whitespaces are optional

ในทั้งสองกรณีตำแหน่งปัจจุบันจะถูกตรวจสอบcowด้วยการเว้นวรรค 1/0 หรือมากกว่าหลังจากcowนั้นจะมีการจับคู่และการใช้งาน 0+ ตัวอักษรให้น้อยที่สุดเท่าที่จะเป็นไปได้ (= เพิ่มในค่าการจับคู่) แล้วmilkตรวจสอบด้วย 1/0 หรือมากกว่าช่องว่างก่อนหน้าสตริงย่อยนี้)

สถานการณ์ที่ 1: อินพุตบรรทัดเดียว

สถานการณ์นี้และสถานการณ์อื่นทั้งหมดด้านล่างได้รับการสนับสนุนโดยสภาพแวดล้อม JavaScript ทั้งหมด ดูตัวอย่างการใช้งานที่ด้านล่างของคำตอบ

cow (.*?) milk

cowพบครั้งแรกแล้วพื้นที่นั้น ๆ 0+ ตัวอักษรอื่นที่ไม่ใช่ตัวอักษรเส้นแบ่งน้อยที่สุดเท่าที่*?เป็นปริมาณขี้เกียจจะถูกจับเข้าไปในกลุ่มที่ 1 และจากนั้นพื้นที่ที่มีmilkจะต้องปฏิบัติตาม (และผู้ที่จะถูกจับคู่และการบริโภคมากเกินไป )

สถานการณ์ที่ 2: ป้อนข้อมูลแบบหลายบรรทัด

cow ([\s\S]*?) milk

ที่นี่cowและมีการจับคู่ช่องว่างก่อนจากนั้นจะมีการจับคู่และตัวอักษรใด ๆ น้อยกว่า 0 ตัวที่เป็นไปได้และจับภาพไว้ในกลุ่มที่ 1 จากนั้นmilkจะจับคู่ช่องว่างด้วย

สถานการณ์ที่ 3: การจับคู่ที่ทับซ้อนกัน

หากคุณมีสายอักขระเหมือนกัน>>>15 text>>>67 text2>>>และคุณจำเป็นต้องได้รับการจับคู่ 2 ครั้งระหว่าง>>>+ number+ whitespaceและ>>>คุณไม่สามารถใช้งานได้/>>>\d+\s(.*?)>>>/gเนื่องจากจะพบการแข่งขันเพียง 1 รายการเนื่องจากข้อเท็จจริง>>>ก่อนหน้า67นี้จะถูกใช้ไปแล้วเมื่อค้นหาการจับคู่ครั้งแรก คุณสามารถใช้lookahead เชิงบวกเพื่อตรวจสอบว่ามีข้อความอยู่หรือไม่โดย "gobbling" (เช่นต่อท้ายการแข่งขัน):

/>>>\d+\s(.*?)(?=>>>)/g

ดูตัวอย่างผลการทดลอง regex ออนไลน์text1และtext2พบเนื้อหาของกลุ่ม 1

ยังเห็นวิธีการที่จะได้รับการแข่งขันที่ทับซ้อนกันเป็นไปได้ทั้งหมดสำหรับสตริง

ข้อควรพิจารณาด้านประสิทธิภาพ

รูปแบบการจับคู่จุดขี้เกียจ ( .*?) ภายในรูปแบบ regex อาจทำให้การเรียกใช้สคริปต์ช้าลงหากมีการป้อนข้อมูลที่ยาวมาก ในหลายกรณีเทคนิค unroll-the-loopช่วยในระดับที่มากขึ้น พยายามที่จะคว้าทุกอย่างจากcowและmilkจาก"Their\ncow\ngives\nmore\nmilk"เราเห็นว่าเราเพียงแค่ต้องตรงกับทุกบรรทัดที่ไม่ได้เริ่มต้นด้วยmilkดังนั้นแทนที่จะใช้cow\n([\s\S]*?)\nmilkเราสามารถใช้:

/cow\n(.*(?:\n(?!milk$).*)*)\nmilk/gm

ดูตัวอย่างของ regex (ถ้ามีให้\r\nใช้/cow\r?\n(.*(?:\r?\n(?!milk$).*)*)\r?\nmilk/gm) ด้วยชุดทดสอบขนาดเล็กนี้ประสิทธิภาพที่เพิ่มขึ้นนั้นเล็กน้อย แต่ด้วยข้อความที่มีขนาดใหญ่มากคุณจะรู้สึกถึงความแตกต่าง (โดยเฉพาะอย่างยิ่งถ้าเส้นยาว

ตัวอย่างการใช้ regex ใน JavaScript:

//Single/First match expected: use no global modifier and access match[1]
console.log("My cow always gives milk".match(/cow (.*?) milk/)[1]);
// Multiple matches: get multiple matches with a global modifier and
// trim the results if length of leading/trailing delimiters is known
var s = "My cow always gives milk, thier cow also gives milk";
console.log(s.match(/cow (.*?) milk/g).map(function(x) {return x.substr(4,x.length-9);}));
//or use RegExp#exec inside a loop to collect all the Group 1 contents
var result = [], m, rx = /cow (.*?) milk/g;
while ((m=rx.exec(s)) !== null) {
  result.push(m[1]);
}
console.log(result);

ใช้String#matchAllวิธีการที่ทันสมัย

const s = "My cow always gives milk, thier cow also gives milk";
const matches = s.matchAll(/cow (.*?) milk/g);
console.log(Array.from(matches, x => x[1]));


51

ต่อไปนี้เป็น regex ที่จะจับสิ่งที่อยู่ระหว่างวัวและนม (ไม่มีพื้นที่ / ชั้นนำ):

srctext = "My cow always gives milk.";
var re = /(.*cow\s+)(.*)(\s+milk.*)/;
var newtext = srctext.replace(re, "$2");

ตัวอย่าง: http://jsfiddle.net/entropo/tkP74/


17
  • คุณต้องจับ .*
  • คุณสามารถ (แต่ไม่จำเป็นต้องทำ) ทำแบบไม่เป็นความ.*จริง
  • ไม่มีความจำเป็นสำหรับ lookahead

    > /cow(.*?)milk/i.exec('My cow always gives milk');
    ["cow always gives milk", " always gives "]

ในกรณีนี้โดยเฉพาะอย่างยิ่งถ้ามันโลภมันจะถึงจุดสิ้นสุดและย้อนกลับ (สันนิษฐาน)
Ben

9

คำตอบที่เลือกไม่ทำงานสำหรับฉัน ... อืมม ...

เพียงเพิ่มช่องว่างหลังวัวและ / หรือก่อนนมเพื่อตัดช่องว่างจาก "ให้เสมอ"

/(?<=cow ).*(?= milk)/

ป้อนคำอธิบายรูปภาพที่นี่


คุณไม่จำเป็นต้องแสดงความคิดเห็นกับคำตอบของคุณเองเพียงแค่แก้ไข
โคดี้ G

?<=Javascript ไม่สนับสนุนLook Look
มาร์คคาร์เพนเตอร์จูเนียร์

@ MarkCarpenterJr หากคุณทดสอบผ่านregextester.comคุณจะได้รับคำใบ้นั้น ดูเหมือนว่าไซต์นั้นใช้กฎจากข้อกำหนดที่เก่ากว่า Lookbehind ได้รับการสนับสนุนแล้ว ดูstackoverflow.com/questions/30118815/…และรูปแบบทำงานได้ดีกับเบราว์เซอร์สมัยใหม่โดยไม่มีข้อผิดพลาด ลองใช้เครื่องมือตรวจสอบนี้แทนregex101.com
duduwe

@ CodyG.ah ใช่ เข้าใจแล้ว
duduwe

8

ฉันสามารถรับสิ่งที่ฉันต้องการโดยใช้วิธีการแก้ปัญหาของ Martinho Fernandes ด้านล่าง รหัสคือ:

var test = "My cow always gives milk";

var testRE = test.match("cow(.*)milk");
alert(testRE[1]);

คุณจะสังเกตเห็นว่าฉันกำลังแจ้งเตือนตัวแปร testRE เป็นอาร์เรย์ นี่เป็นเพราะ testRE กลับมาเป็นอาร์เรย์ด้วยเหตุผลบางอย่าง ผลลัพธ์จาก:

My cow always gives milk

เปลี่ยนเป็น:

always gives

1
ขอบคุณฉันเพิ่มซอ ( jsfiddle.net/MoscaPt/g5Lngjx8/2 ) สำหรับมัน / Johan
Mosca Pt

4

เพียงใช้นิพจน์ทั่วไปต่อไปนี้:

(?<=My cow\s).*?(?=\smilk)

?<=Javascript ไม่สนับสนุนLook Look จะเป็นวิธีที่จะทำแม้ว่า
มาร์คคาร์เพนเตอร์จูเนียร์

รองรับจาวาสคริปต์ ไม่รองรับ Safari และ Mozilla (ยัง) เฉพาะใน Chrome และ Opera
Paul Strupeikis

3

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

const text = 'My cow always gives milk'
const start = `cow`;
const end = `milk`;
const middleText = text.split(start)[1].split(end)[0]
console.log(middleText) // prints "always gives"

2
ใช้งานได้สำหรับฉัน! คำตอบที่ยอดเยี่ยมเพราะมันง่ายจริงๆ! :)
Andrew Irwin


0

เมธอด match () ค้นหาสตริงสำหรับการจับคู่และส่งคืนอ็อบเจ็กต์ Array

// Original string
var str = "My cow always gives milk";

// Using index [0] would return<br/>
// "**cow always gives milk**"
str.match(/cow(.*)milk/)**[0]**


// Using index **[1]** would return
// "**always gives**"
str.match(/cow(.*)milk/)[1]

0

งาน

แยกสตริงย่อยระหว่างสองสตริง (ไม่รวมสองสตริงนี้)

สารละลาย

let allText = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum";
let textBefore = "five centuries,";
let textAfter = "electronic typesetting";
var regExp = new RegExp(`(?<=${textBefore}\\s)(.+?)(?=\\s+${textAfter})`, "g");
var results = regExp.exec(allText);
if (results && results.length > 1) {
    console.log(results[0]);
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.