Theo như tôi biết thì không có thứ gọi là các nhóm bắt giữ trong JavaScript. Cách khác để có được chức năng tương tự là gì?
Theo như tôi biết thì không có thứ gọi là các nhóm bắt giữ trong JavaScript. Cách khác để có được chức năng tương tự là gì?
Câu trả lời:
ECMAScript 2018 giới thiệu các nhóm bắt giữ có tên vào các biểu thức JavaScript.
Thí dụ:
const auth = 'Bearer AUTHORIZATION_TOKEN'
const { groups: { token } } = /Bearer (?<token>[^ $]*)/.exec(auth)
console.log(token) // "Prints AUTHORIZATION_TOKEN"
Nếu bạn cần hỗ trợ các trình duyệt cũ hơn, bạn có thể làm mọi thứ với các nhóm bắt bình thường (được đánh số) mà bạn có thể làm với các nhóm bắt có tên, bạn chỉ cần theo dõi các số - có thể là cồng kềnh nếu thứ tự bắt nhóm trong regex thay đổi.
Chỉ có hai lợi thế "cấu trúc" của các nhóm bắt giữ được đặt tên mà tôi có thể nghĩ đến:
Trong một số hương vị regex (.NET và JGSoft, theo như tôi biết), bạn có thể sử dụng cùng tên cho các nhóm khác nhau trong regex của bạn ( xem ở đây để biết ví dụ về vấn đề này ). Nhưng hầu hết các hương vị regex không hỗ trợ chức năng này.
Nếu bạn cần tham khảo các nhóm bắt được đánh số trong tình huống chúng được bao quanh bởi các chữ số, bạn có thể gặp vấn đề. Giả sử bạn muốn thêm số 0 vào một chữ số và do đó muốn thay thế (\d)
bằng $10
. Trong JavaScript, điều này sẽ hoạt động (miễn là bạn có ít hơn 10 nhóm bắt trong regex của bạn), nhưng Perl sẽ nghĩ rằng bạn đang tìm kiếm số phản hồi 10
thay vì số 1
, theo sau là a 0
. Trong Perl, bạn có thể sử dụng ${1}0
trong trường hợp này.
Ngoài ra, các nhóm bắt giữ được đặt tên chỉ là "đường cú pháp". Nó chỉ giúp sử dụng các nhóm bắt giữ khi bạn thực sự cần chúng và sử dụng các nhóm không bắt giữ (?:...)
trong tất cả các trường hợp khác.
Vấn đề lớn hơn (theo ý kiến của tôi) với JavaScript là nó không hỗ trợ các biểu thức dài dòng, điều này sẽ giúp việc tạo các biểu thức chính quy phức tạp dễ đọc trở nên dễ dàng hơn rất nhiều.
Thư viện XRegExp của Steve Levithan giải quyết những vấn đề này.
Bạn có thể sử dụng XRegExp , một triển khai trình duyệt thông thường tăng cường, có thể mở rộng, biểu thức chính, bao gồm hỗ trợ cho cú pháp, cờ và phương thức bổ sung:
s
, để tạo dấu chấm khớp với tất cả các ký tự (còn gọi là chế độ dotall hoặc singleline), và x
, cho khoảng cách tự do và nhận xét (còn gọi là chế độ mở rộng).Một giải pháp khả thi khác: tạo một đối tượng chứa tên nhóm và chỉ mục.
var regex = new RegExp("(.*) (.*)");
var regexGroups = { FirstName: 1, LastName: 2 };
Sau đó, sử dụng các khóa đối tượng để tham chiếu các nhóm:
var m = regex.exec("John Smith");
var f = m[regexGroups.FirstName];
Điều này cải thiện khả năng đọc / chất lượng của mã bằng cách sử dụng kết quả của regex, nhưng không phải là khả năng đọc của chính regex.
Trong ES6, bạn có thể sử dụng tính năng phá hủy mảng để bắt các nhóm của mình:
let text = '27 months';
let regex = /(\d+)\s*(days?|months?|years?)/;
let [, count, unit] = regex.exec(text) || [];
// count === '27'
// unit === 'months'
Để ý:
let
bỏ qua cuối cùng giá trị đầu tiên của mảng kết quả, đó là toàn bộ chuỗi khớp|| []
sau .exec()
sẽ ngăn lỗi phá hủy khi không có kết quả khớp (vì .exec()
sẽ trả về null
)String.prototype.match
trả về một mảng với: toàn bộ chuỗi khớp ở vị trí 0, sau đó bất kỳ nhóm nào sau đó. Dấu phẩy đầu tiên cho biết "bỏ qua phần tử ở vị trí 0"
RegExp.prototype.exec
hơn String.prototype.match
ở những nơi mà các chuỗi có thể null
hay undefined
.
Cập nhật: Cuối cùng nó đã biến nó thành JavaScript (ECMAScript 2018)!
Các nhóm bắt giữ được đặt tên có thể biến nó thành JavaScript rất sớm.
Đề xuất cho nó là ở giai đoạn 3 rồi.
Một nhóm chụp có thể được đặt tên trong dấu ngoặc nhọn bằng (?<name>...)
cú pháp, cho bất kỳ tên định danh nào. Biểu thức chính quy cho một ngày sau đó có thể được viết là /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u
. Mỗi tên phải là duy nhất và tuân theo ngữ pháp cho ECMAScript IdentifierName .
Các nhóm được đặt tên có thể được truy cập từ các thuộc tính của thuộc tính nhóm của kết quả biểu thức chính quy. Các tham chiếu được đánh số cho các nhóm cũng được tạo, giống như đối với các nhóm không được đặt tên. Ví dụ:
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'));
Các nhóm được đặt tên cung cấp một điều: ít nhầm lẫn với các biểu thức chính quy phức tạp.
Nó thực sự phụ thuộc vào trường hợp sử dụng của bạn nhưng có lẽ việc in ấn regex của bạn có thể giúp ích.
Hoặc bạn có thể thử và xác định các hằng số để tham khảo các nhóm đã bắt của bạn.
Nhận xét sau đó cũng có thể giúp hiển thị cho những người khác đọc mã của bạn, những gì bạn đã làm.
Đối với phần còn lại tôi phải đồng ý với câu trả lời của Tims.
Có một thư viện node.js có tên là regrec mà bạn có thể sử dụng trong các dự án node.js của mình (trên trình duyệt bằng cách đóng gói thư viện với browserify hoặc các tập lệnh đóng gói khác). Tuy nhiên, thư viện không thể được sử dụng với các biểu thức thông thường có chứa các nhóm bắt giữ không được đặt tên.
Nếu bạn đếm các dấu ngoặc bắt mở trong biểu thức chính quy, bạn có thể tạo ánh xạ giữa các nhóm bắt được đặt tên và các nhóm bắt được đánh số trong biểu thức chính quy của bạn và có thể trộn và kết hợp tự do. Bạn chỉ cần xóa tên nhóm trước khi sử dụng regex. Tôi đã viết ba chức năng chứng minh điều đó. Xem ý chính này: https://gist.github.com/gbirke/2cc2370135b665eee3ef
Như Tim Pietzcker đã nói ECMAScript 2018 giới thiệu các nhóm bắt giữ có tên vào các biểu thức JavaScript. Nhưng điều tôi không tìm thấy trong các câu trả lời ở trên là làm thế nào để sử dụng nhóm bị bắt có tên trong chính regex.
bạn có thể sử dụng nhóm được đặt tên với cú pháp này : \k<name>
. ví dụ
var regexObj = /(?<year>\d{4})-(?<day>\d{2})-(?<month>\d{2}) year is \k<year>/
và như Forivin đã nói, bạn có thể sử dụng nhóm bị bắt trong kết quả đối tượng như sau:
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>
Mặc dù bạn không thể làm điều này với JavaScript vanilla, nhưng có lẽ bạn có thể sử dụng một số Array.prototype
chức năng như Array.prototype.reduce
để biến các trận đấu được lập chỉ mục thành các trận đấu được đặt tên bằng một số phép thuật .
Rõ ràng, giải pháp sau đây sẽ cần các trận đấu xảy ra theo thứ tự:
// @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
đối tượng bằng cách thêm một hàm vào nguyên mẫu của nó.
Không có ECMAScript 2018?
Mục tiêu của tôi là làm cho nó hoạt động tương tự nhất có thể với những gì chúng ta đã quen với các nhóm được đặt tên. Trong khi trong ECMAScript 2018, bạn có thể đặt ?<groupname>
bên trong nhóm để chỉ ra một nhóm được đặt tên, trong giải pháp của tôi cho javascript cũ hơn, bạn có thể đặt (?!=<groupname>)
bên trong nhóm để làm điều tương tự. Vì vậy, đó là một bộ dấu ngoặc đơn bổ sung và thêm !=
. Khá gần!
Tôi gói tất cả vào một hàm nguyên mẫu chuỗi
Đặc trưng
Hướng dẫn
(?!={groupname})
bên trong mỗi nhóm bạn muốn đặt tên()
bằng cách đặt ?:
ở đầu nhóm đó. Chúng sẽ không được đặt tên.mảngays.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;
}, {});
};
sử dụng
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
}
kết quả của o
{
"house number": "123",
"street name": "Main",
"street type": "St"
}