เท่าที่ฉันรู้ไม่มีสิ่งเช่นชื่อกลุ่มจับภาพใน JavaScript วิธีอื่นในการรับฟังก์ชั่นที่คล้ายกันคืออะไร
เท่าที่ฉันรู้ไม่มีสิ่งเช่นชื่อกลุ่มจับภาพใน JavaScript วิธีอื่นในการรับฟังก์ชั่นที่คล้ายกันคืออะไร
คำตอบ:
ECMAScript 2018 แนะนำกลุ่มการจับภาพชื่อไว้ใน JavaScript regexes
ตัวอย่าง:
const auth = 'Bearer AUTHORIZATION_TOKEN'
const { groups: { token } } = /Bearer (?<token>[^ $]*)/.exec(auth)
console.log(token) // "Prints AUTHORIZATION_TOKEN"
หากคุณต้องการสนับสนุนเบราว์เซอร์รุ่นเก่าคุณสามารถทำทุกอย่างด้วยกลุ่มการจับภาพปกติ (ตัวเลข) ที่คุณสามารถทำได้ด้วยกลุ่มการจับกุมที่ระบุชื่อคุณเพียงแค่ต้องติดตามตัวเลข - ซึ่งอาจยุ่งยากหากลำดับการจับกลุ่มใน การเปลี่ยนแปลง regex
มีข้อดีเพียง "โครงสร้าง" เพียงสองอย่างของกลุ่มจับชื่อที่ฉันสามารถนึกได้:
ในบางรสชาติของ regex (. NET และ JGSoft เท่าที่ฉันรู้) คุณสามารถใช้ชื่อเดียวกันสำหรับกลุ่มต่าง ๆ ใน regex ของคุณ ( ดูที่นี่สำหรับตัวอย่างที่มีความสำคัญ ) แต่รสชาติของ regex ส่วนใหญ่ไม่รองรับฟังก์ชั่นนี้อยู่ดี
หากคุณจำเป็นต้องอ้างถึงกลุ่มการจับภาพหมายเลขในสถานการณ์ที่พวกเขาล้อมรอบด้วยตัวเลขคุณจะได้รับปัญหา สมมติว่าคุณต้องการที่จะเพิ่มศูนย์การหลักและดังนั้นจึงต้องการแทนที่ด้วย(\d)
$10
ใน JavaScript นี้การทำงานจะ (ตราบเท่าที่คุณมีน้อยกว่า 10 จับภาพกลุ่มใน regex ของคุณ) แต่ Perl จะคิดว่าคุณกำลังมองหาจำนวน backreference 10
แทนหมายเลขตามด้วย1
0
ใน Perl คุณสามารถใช้${1}0
ในกรณีนี้
นอกเหนือจากนั้นกลุ่มจับภาพที่มีชื่อเป็นเพียง "น้ำตาลทราย" ช่วยในการใช้กลุ่มที่จับภาพเฉพาะเมื่อคุณต้องการจริงๆและใช้กลุ่มที่ไม่ได้รับการบันทึก(?:...)
ในสถานการณ์อื่น ๆ ทั้งหมด
ปัญหาที่ใหญ่กว่า (ในความคิดของฉัน) กับ JavaScript คือมันไม่สนับสนุน regexes verbose ซึ่งจะทำให้การสร้างการแสดงออกปกติที่ซับซ้อนอ่านง่ายขึ้นมากขึ้น
ห้องสมุด XRegExp ของ Steve Levithanแก้ปัญหาเหล่านี้ได้
คุณสามารถใช้XRegExpซึ่งเป็นการนำนิพจน์ทั่วไปที่เพิ่มขยายขยายได้ข้ามเบราว์เซอร์รวมถึงการสนับสนุนไวยากรณ์เพิ่มเติมแฟล็กและเมธอด:
s
, เพื่อให้ dot จับคู่อักขระทั้งหมด (โหมด dotall หรือ singleline), และx
, สำหรับระยะห่างและข้อคิดเห็น (โหมดขยาย)อีกวิธีที่เป็นไปได้: สร้างวัตถุที่มีชื่อกลุ่มและดัชนี
var regex = new RegExp("(.*) (.*)");
var regexGroups = { FirstName: 1, LastName: 2 };
จากนั้นใช้ปุ่มวัตถุเพื่ออ้างอิงกลุ่ม:
var m = regex.exec("John Smith");
var f = m[regexGroups.FirstName];
สิ่งนี้ช่วยปรับปรุงความสามารถในการอ่าน / คุณภาพของรหัสโดยใช้ผลลัพธ์ของ regex แต่ไม่สามารถอ่านได้ของ regex
ใน ES6 คุณสามารถใช้การทำลายอาร์เรย์เพื่อจับกลุ่มของคุณ:
let text = '27 months';
let regex = /(\d+)\s*(days?|months?|years?)/;
let [, count, unit] = regex.exec(text) || [];
// count === '27'
// unit === 'months'
หมายเหตุ:
let
ข้ามค่าแรกของอาร์เรย์ผลลัพธ์ซึ่งเป็นสตริงที่ตรงกันทั้งหมด|| []
หลังจากที่.exec()
จะป้องกันไม่ให้เกิดข้อผิดพลาด destructuring เมื่อไม่มีการแข่งขัน (เพราะ.exec()
จะกลับมาnull
)String.prototype.match
ส่งคืนอาร์เรย์ด้วย: สตริงที่ตรงกันทั้งหมดที่ตำแหน่ง 0 จากนั้นกลุ่มใด ๆ หลังจากนั้น เครื่องหมายจุลภาคแรกบอกว่า "ข้ามองค์ประกอบที่ตำแหน่ง 0"
RegExp.prototype.exec
มากกว่าString.prototype.match
ในสถานที่ที่สตริงอาจจะเป็นหรือnull
undefined
อัปเดต: ในที่สุดมันก็ทำให้เป็น JavaScript (ECMAScript 2018)!
กลุ่มจับภาพที่มีชื่อสามารถทำให้เป็น JavaScript ได้ในไม่ช้า
ข้อเสนอสำหรับมันอยู่ในขั้นตอนที่ 3 แล้ว
กลุ่มการดักจับสามารถให้ชื่อภายในวงเล็บเหลี่ยมโดยใช้(?<name>...)
ไวยากรณ์สำหรับชื่อตัวระบุใด ๆ /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u
การแสดงออกปกติสำหรับวันที่แล้วสามารถเขียนเป็น แต่ละชื่อต้องไม่ซ้ำกันและปฏิบัติตามหลักไวยากรณ์สำหรับ ECMAScript IdentifierName
กลุ่มที่มีชื่อสามารถเข้าถึงได้จากคุณสมบัติของคุณสมบัติกลุ่มของผลลัพธ์นิพจน์ทั่วไป หมายเลขอ้างอิงไปยังกลุ่มจะถูกสร้างขึ้นเช่นเดียวกับกลุ่มที่ไม่มีชื่อ ตัวอย่างเช่น:
let re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
let result = re.exec('2015-01-02');
// result.groups.year === '2015';
// result.groups.month === '01';
// result.groups.day === '02';
// result[0] === '2015-01-02';
// result[1] === '2015';
// result[2] === '01';
// result[3] === '02';
let {year, month, day} = ((result) => ((result) ? result.groups : {}))(re.exec('2015-01-02'));
การตั้งชื่อกลุ่มที่ถูกจับภาพนั้นมีสิ่งหนึ่งที่: ความสับสนน้อยลงด้วยการแสดงออกปกติที่ซับซ้อน
มันขึ้นอยู่กับกรณีการใช้งานของคุณ แต่การพิมพ์ regex ของคุณอาจช่วยได้
หรือคุณอาจลองกำหนดค่าคงที่เพื่ออ้างถึงกลุ่มที่ถูกจับ
ความคิดเห็นอาจช่วยให้ผู้อื่นที่อ่านรหัสของคุณทราบว่าคุณได้ทำอะไรไปแล้ว
สำหรับส่วนที่เหลือฉันต้องเห็นด้วยกับคำตอบของ Tims
มีไลบรารี node.js ชื่อ named-regexpที่คุณสามารถใช้ในโปรเจ็กต์ node.js ของคุณ (เปิดในเบราว์เซอร์โดยการบรรจุไลบรารีด้วยเบราว์เซอร์หรือสคริปต์บรรจุภัณฑ์อื่น ๆ ) อย่างไรก็ตามไลบรารีไม่สามารถใช้กับนิพจน์ทั่วไปที่มีกลุ่มการดักจับที่ไม่มีชื่อ
หากคุณนับวงเล็บปีกกาเปิดในนิพจน์ปกติของคุณคุณสามารถสร้างการแมประหว่างกลุ่มจับภาพที่มีชื่อกับกลุ่มจับภาพหมายเลขใน regex ของคุณและสามารถผสมและจับคู่ได้อย่างอิสระ คุณต้องลบชื่อกลุ่มก่อนใช้ regex ฉันเขียนสามฟังก์ชั่นที่แสดงว่า ดูส่วนสำคัญนี้: https://gist.github.com/gbirke/2cc2370135b665eee3ef
ดังที่Tim Pietzckerกล่าวว่า ECMAScript 2018 แนะนำกลุ่มการจับภาพชื่อใน JavaScript regexes แต่สิ่งที่ฉันไม่พบในคำตอบข้างต้นคือวิธีใช้กลุ่มที่มีชื่อใน regex นั้นเอง
\k<name>
คุณสามารถใช้กลุ่มจับชื่อกับรูปแบบนี้: ตัวอย่างเช่น
var regexObj = /(?<year>\d{4})-(?<day>\d{2})-(?<month>\d{2}) year is \k<year>/
และดังที่Forivinกล่าวว่าคุณสามารถใช้กลุ่มที่ถูกจับในผลลัพธ์วัตถุดังนี้:
let result = regexObj.exec('2019-28-06 year is 2019');
// result.groups.year === '2019';
// result.groups.month === '06';
// result.groups.day === '28';
var regexObj = /(?<year>\d{4})-(?<day>\d{2})-(?<month>\d{2}) year is \k<year>/mgi;
function check(){
var inp = document.getElementById("tinput").value;
let result = regexObj.exec(inp);
document.getElementById("year").innerHTML = result.groups.year;
document.getElementById("month").innerHTML = result.groups.month;
document.getElementById("day").innerHTML = result.groups.day;
}
td, th{
border: solid 2px #ccc;
}
<input id="tinput" type="text" value="2019-28-06 year is 2019"/>
<br/>
<br/>
<span>Pattern: "(?<year>\d{4})-(?<day>\d{2})-(?<month>\d{2}) year is \k<year>";
<br/>
<br/>
<button onclick="check()">Check!</button>
<br/>
<br/>
<table>
<thead>
<tr>
<th>
<span>Year</span>
</th>
<th>
<span>Month</span>
</th>
<th>
<span>Day</span>
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<span id="year"></span>
</td>
<td>
<span id="month"></span>
</td>
<td>
<span id="day"></span>
</td>
</tr>
</tbody>
</table>
ในขณะที่คุณไม่สามารถทำเช่นนี้กับวานิลลา JavaScript, บางทีคุณอาจจะใช้บางArray.prototype
ฟังก์ชั่นเช่นArray.prototype.reduce
การเปิดการแข่งขันการจัดทำดัชนีเป็นคนตั้งชื่อโดยใช้บางมายากล
เห็นได้ชัดว่าการแก้ปัญหาต่อไปนี้จะต้องเกิดขึ้นในการแข่งขัน:
// @text Contains the text to match
// @regex A regular expression object (f.e. /.+/)
// @matchNames An array of literal strings where each item
// is the name of each group
function namedRegexMatch(text, regex, matchNames) {
var matches = regex.exec(text);
return matches.reduce(function(result, match, index) {
if (index > 0)
// This substraction is required because we count
// match indexes from 1, because 0 is the entire matched string
result[matchNames[index - 1]] = match;
return result;
}, {});
}
var myString = "Hello Alex, I am John";
var namedMatches = namedRegexMatch(
myString,
/Hello ([a-z]+), I am ([a-z]+)/i,
["firstPersonName", "secondPersonName"]
);
alert(JSON.stringify(namedMatches));
var assocArray = Regex("hello alex, I am dennis", "hello ({hisName}.+), I am ({yourName}.+)");
RegExp
วัตถุโดยการเพิ่มฟังก์ชั่นต้นแบบของมัน
ไม่มี ECMAScript 2018 ใช่ไหม
เป้าหมายของฉันคือการทำให้มันทำงานคล้ายกับสิ่งที่เราคุ้นเคยกับกลุ่มที่มีชื่อ ในขณะที่ ECMAScript 2018 คุณสามารถวางไว้?<groupname>
ภายในกลุ่มเพื่อระบุกลุ่มที่มีชื่อในโซลูชันของฉันสำหรับจาวาสคริปต์รุ่นเก่าคุณสามารถวางไว้(?!=<groupname>)
ภายในกลุ่มเพื่อทำสิ่งเดียวกัน ดังนั้นมันจึงเป็นวงเล็บพิเศษและอีกอัน!=
หนึ่ง ค่อนข้างใกล้!
ฉันห่อมันทั้งหมดไว้ในฟังก์ชั่นต้นแบบสตริง
คุณสมบัติ
คำแนะนำ
(?!={groupname})
ในแต่ละกลุ่มที่คุณต้องการตั้งชื่อ()
โดยใส่?:
จุดเริ่มต้นของกลุ่มนั้น สิ่งเหล่านี้จะไม่ถูกตั้งชื่อarrays.js
// @@pattern - includes injections of (?!={groupname}) for each group
// @@returns - an object with a property for each group having the group's match as the value
String.prototype.matchWithGroups = function (pattern) {
var matches = this.match(pattern);
return pattern
// get the pattern as a string
.toString()
// suss out the groups
.match(/<(.+?)>/g)
// remove the braces
.map(function(group) {
return group.match(/<(.+)>/)[1];
})
// create an object with a property for each group having the group's match as the value
.reduce(function(acc, curr, index, arr) {
acc[curr] = matches[index + 1];
return acc;
}, {});
};
การใช้
function testRegGroups() {
var s = '123 Main St';
var pattern = /((?!=<house number>)\d+)\s((?!=<street name>)\w+)\s((?!=<street type>)\w+)/;
var o = s.matchWithGroups(pattern); // {'house number':"123", 'street name':"Main", 'street type':"St"}
var j = JSON.stringify(o);
var housenum = o['house number']; // 123
}
ผลของการ
{
"house number": "123",
"street name": "Main",
"street type": "St"
}