Tìm kiếm Javascript theo biểu thức chính quy. Áp dụng biểu thức chính quy cho một chuỗi. Kiểm tra RegExp - kiểm tra email

Trong JavaScript, các biểu thức chính quy được biểu diễn bằng các đối tượng RegExp. Các đối tượng RegExp có thể được tạo bằng cách sử dụng hàm tạo RegExp(), nhưng chúng thường được tạo bằng cú pháp chữ đặc biệt. Giống như các chuỗi ký tự được chỉ định là các ký tự được đặt trong dấu ngoặc kép, các ký tự của biểu thức chính quy được chỉ định là các ký tự được đặt trong một cặp dấu gạch chéo / .

/pattern/flags new RegExp("pattern"[, tùy chọn tìm kiếm])

mẫu- một biểu thức chính quy để tìm kiếm (sẽ thay thế nhiều hơn ở phần sau) và các cờ - một chuỗi gồm bất kỳ tổ hợp ký tự nào g (tìm kiếm chung), i (viết thường không quan trọng) và m (tìm kiếm nhiều dòng). Phương pháp đầu tiên được sử dụng thường xuyên, phương pháp thứ hai - đôi khi. Ví dụ: hai cuộc gọi như vậy là tương đương.

Tùy chọn tìm kiếm

Khi tạo một biểu thức chính quy, chúng ta có thể chỉ định tùy chọn bổ sung tìm kiếm

Các ký tự trong biểu thức chính quy JavaScript

Biểu tượngThư tín
Ký tự chữ và sốTương ứng với chính họ
\0 Ký tự NUL (\u0000)
\tTab (\u0009)
\NNguồn cấp dữ liệu (\u000A)
\vTab dọc (\u000B)
\fDịch trang (\u000C)
\rVận chuyển trở lại (\u000D)
\xnnMột ký tự từ bộ Latin, được chỉ định bởi số thập lục phân nn; ví dụ: \x0A giống với \n
\uxxxxKý tự Unicode được chỉ định bởi số thập lục phân xxxx; ví dụ: \u0009 giống với \t
\cXKý tự điều khiển "X", ví dụ, chuỗi \cJ tương đương với ký tự dòng mới \n
\ Đối với các ký tự thông thường - làm cho chúng trở nên đặc biệt. Ví dụ: biểu thức /s/ chỉ tìm ký tự "s". Và nếu bạn đặt \ trước s thì /\s/ đã biểu thị một ký tự khoảng trắng, và ngược lại, nếu ký tự đặc biệt, chẳng hạn *, thì \ sẽ biến nó thành một ký tự “dấu hoa thị” thông thường. Ví dụ: /a*/ tìm kiếm 0 hoặc nhiều ký tự "a" liên tiếp. Để tìm a có dấu hoa thị "a*" - hãy đặt \ trước số đặc biệt. ký hiệu: /a\*/ .
^ Cho biết sự bắt đầu của dữ liệu đầu vào. Nếu cờ tìm kiếm nhiều dòng ("m") được đặt, nó cũng sẽ kích hoạt khi bắt đầu một dòng mới. Ví dụ: /^A/ sẽ không tìm thấy "A" trong "an A", nhưng sẽ tìm thấy chữ đầu tiên "A" trong "An A."
$ Cho biết sự kết thúc của dữ liệu đầu vào. Nếu cờ tìm kiếm nhiều dòng được đặt, nó cũng sẽ hoạt động ở cuối dòng. Ví dụ: /t$/ sẽ không tìm thấy "t" trong "eater", nhưng nó sẽ tìm thấy nó trong "eat".
* Cho biết sự lặp lại 0 lần trở lên. Ví dụ: /bo*/ sẽ tìm thấy "boooo" trong "A ghost booooed" và "b" trong "A bird warbled", nhưng sẽ không tìm thấy gì trong "A dê càu nhàu".
+ Biểu thị sự lặp lại 1 hoặc nhiều lần. Tương đương với (1,). Ví dụ: /a+/ sẽ khớp với "a" trong "candy" và tất cả "a" trong "caaaaaaandy".
? Chỉ ra rằng phần tử có thể có hoặc không có mặt. Ví dụ: /e?le?/ sẽ khớp với "el" trong "angel" và "le" trong "angle." Nếu được sử dụng ngay sau một trong các định lượng * , + , ? , hoặc () , sau đó chỉ định tìm kiếm "không tham lam" (lặp lại số lần tối thiểu có thể, đến phần tử tiếp theo gần nhất của mẫu), trái ngược với chế độ "tham lam" mặc định, trong đó số lần lặp lại là tối đa, ngay cả khi phần tử tiếp theo mẫu cũng phù hợp. Ngoài ra, ? được sử dụng trong bản xem trước, được mô tả trong bảng bên dưới (?=) , (?!) và (?:) .
. (Dấu thập phân) đại diện cho bất kỳ ký tự nào ngoài dòng mới: \n \r \u2028 hoặc \u2029. (bạn có thể sử dụng [\s\S] để tìm kiếm bất kỳ ký tự nào, kể cả dòng mới). Ví dụ: /.n/ sẽ khớp với "an" và "on" trong "nay, an apple is on the tree", nhưng không khớp với "nay".
(x)Tìm x và ghi nhớ. Đây được gọi là "khung bộ nhớ". Ví dụ: /(foo)/ sẽ tìm và ghi nhớ "foo" trong "foo bar." Chuỗi con tìm thấy được lưu trữ trong mảng kết quả tìm kiếm hoặc trong các thuộc tính được xác định trước của đối tượng RegExp: $1, ..., $9. Ngoài ra, các dấu ngoặc đơn kết hợp những gì có trong chúng thành một phần tử mẫu duy nhất. Ví dụ: (abc)* - lặp lại abc 0 hoặc nhiều lần.
(?:x)Tìm x nhưng không nhớ nó tìm thấy gì. Đây được gọi là "dấu ngoặc đơn bộ nhớ". Chuỗi con tìm thấy không được lưu trữ trong mảng kết quả và thuộc tính RegExp. Giống như tất cả các dấu ngoặc, chúng kết hợp những gì có trong chúng thành một mẫu con duy nhất.
x(?=y)Chỉ tìm thấy x nếu x được theo sau bởi y. Ví dụ: /Jack(?=Sprat)/ sẽ chỉ khớp với "Jack" nếu theo sau là "Sprat". /Jack(?=Sprat|Frost)/ sẽ chỉ khớp với "Jack" nếu theo sau là "Sprat" hoặc "Frost". Tuy nhiên, cả "Sprat" và "Frost" đều không xuất hiện trong kết quả tìm kiếm.
x(?!y)Chỉ tìm x nếu x không có y theo sau. Ví dụ: /\d+(?!\.)/ sẽ chỉ khớp với một số nếu nó không có dấu thập phân theo sau. /\d+(?!\.)/.exec("3.141") sẽ tìm thấy 141, nhưng không tìm thấy 3.141.
x|yTìm x hoặc y. Ví dụ: /green|red/ sẽ khớp với "green" trong "green apple" và "red" trong "red apple".
(N)Trong đó n là số nguyên dương. Tìm chính xác n lần lặp lại của phần tử trước đó. Ví dụ: /a(2)/ sẽ không tìm thấy chữ "a" trong "candy" nhưng sẽ tìm thấy cả chữ a trong "caandy" và hai chữ a đầu tiên trong "caaandy".
(N,)Trong đó n là số nguyên dương. Tìm n hoặc nhiều lần lặp lại của một phần tử. Ví dụ: /a(2,) sẽ không tìm thấy "a" trong "candy", nhưng sẽ tìm thấy tất cả "a" trong "caandy" và trong "caaaaaaandy".
(n,m)Trong đó n và m là số nguyên dương. Tìm từ n đến m lần lặp lại của phần tử.
Bộ ký tự. Tìm bất kỳ ký tự nào được liệt kê. Bạn có thể chỉ ra khoảng cách bằng cách sử dụng dấu gạch ngang. Ví dụ: - giống như . So khớp "b" trong "brisket" và "a" và "c" trong "ache".
[^xyz]Bất kỳ ký tự nào khác với ký tự được chỉ định trong bộ. Bạn cũng có thể chỉ định một khoảng. Ví dụ: [^abc] giống với [^a-c] . Tìm thấy "r" trong "thịt ức" và "h" trong "chặt".
[\b]Tìm ký tự backspace. (Đừng nhầm lẫn với \b .)
\bTìm ranh giới từ (tiếng Latin), chẳng hạn như khoảng trắng. (Đừng nhầm lẫn với [\b]). Ví dụ: /\bn\w/ sẽ khớp với "no" trong "noonday"; /\wy\b/ sẽ tìm thấy "ly" trong "có thể là hôm qua."
\BNó không chỉ ra một ranh giới từ. Ví dụ: /\w\Bn/ sẽ khớp với "on" trong "noonday" và /y\B\w/ sẽ khớp với "ye" trong "có thể là hôm qua".
\cXTrong đó X là một chữ cái từ A đến Z. Cho biết ký tự điều khiển trong chuỗi. Ví dụ: /\cM/ đại diện cho ký tự Ctrl-M.
\dtìm một số từ bất kỳ bảng chữ cái nào (bảng chữ cái của chúng tôi là Unicode). Sử dụng để chỉ tìm các số thông thường. Ví dụ: /\d/ hoặc // sẽ khớp với "2" trong "B2 là số bộ."
\DTìm ký tự không phải số (tất cả các bảng chữ cái). [^0-9] tương đương với số thông thường. Ví dụ: /\D/ hoặc /[^0-9]/ sẽ khớp với "B" trong "B2 là số bộ."
\STìm bất kỳ ký tự khoảng trắng nào, bao gồm dấu cách, tab, dòng mới và các ký tự khoảng trắng Unicode khác. Ví dụ: /\s\w*/ sẽ khớp với "bar" trong "foo bar."
\STìm bất kỳ ký tự nào ngoại trừ khoảng trắng. Ví dụ: /\S\w*/ sẽ khớp với "foo" trong "foo bar."
\vKý tự tab dọc.
\wTìm bất kỳ ký tự từ nào (bảng chữ cái Latinh), bao gồm các chữ cái, số và dấu gạch dưới. Tương đương. Ví dụ: /\w/ sẽ khớp với "a" trong "apple", "5" trong "$5,28" và "3" trong "3D".
\WTìm bất kỳ ký tự bằng lời không phải (tiếng Latin). Tương đương với [^A-Za-z0-9_] . Ví dụ: /\W/ và /[^$A-Za-z0-9_]/ sẽ khớp như nhau với "%" trong "50%."

Làm việc với biểu thức chính quy trong Javascript

Làm việc với các biểu thức chính quy trong Javascript được triển khai bằng các phương thức của lớp String

exec(regexp) - tìm tất cả các kết quả khớp (các mục trong mẫu thông thường) trong một chuỗi. Trả về một mảng (nếu khớp) và cập nhật thuộc tính regrec hoặc null nếu không tìm thấy gì. Với công cụ sửa đổi g - mỗi khi hàm này được gọi, nó sẽ trả về kết quả khớp tiếp theo sau khi tìm thấy kết quả trước đó - điều này được thực hiện bằng cách duy trì chỉ mục bù của tìm kiếm cuối cùng.

match(regexp) - tìm một phần của chuỗi bằng cách sử dụng mẫu. Nếu công cụ sửa đổi g được chỉ định thì match() trả về một mảng gồm tất cả các kết quả khớp hoặc null (chứ không phải một mảng trống). Nếu không có công cụ sửa đổi g, hàm này hoạt động như exec();

test(regexp) - hàm kiểm tra một chuỗi xem có khớp với mẫu không. Trả về true nếu có kết quả khớp và trả về sai nếu không có kết quả khớp.

tách (regexp) - Chia chuỗi được gọi thành một mảng các chuỗi con, sử dụng đối số làm dấu phân cách.

thay thế (regexp, mix) - phương thức trả về một chuỗi đã sửa đổi theo mẫu (biểu thức chính quy). Tham số đầu tiên của biểu thức chính quy cũng có thể là một chuỗi chứ không phải là biểu thức chính quy. Nếu không có từ bổ nghĩa g, phương thức trong dòng chỉ thay thế lần xuất hiện đầu tiên; với bổ ngữ g - xảy ra thay thế toàn cầu, I E. tất cả các lần xuất hiện trong một dòng nhất định đều bị thay đổi. mix - mẫu thay thế, có thể chấp nhận các giá trị của chuỗi, mẫu thay thế, hàm (tên hàm).

Các ký tự đặc biệt trong chuỗi thay thế

Thay thế thông qua chức năng

Nếu bạn chỉ định một hàm làm tham số thứ hai, nó sẽ được thực thi trên mọi kết quả khớp. Một hàm có thể tự động tạo và trả về chuỗi thay thế. Tham số đầu tiên của hàm là chuỗi con tìm thấy. Nếu đối số đầu tiên cần thay thế là một đối tượng RegExp thì n tham số tiếp theo chứa các dấu ngoặc đơn lồng nhau khớp với nhau. Hai tham số cuối cùng là vị trí trong dòng nơi trận đấu xảy ra và chính dòng đó.

Lớp RegExp trong JavaScript là một biểu thức chính quy - một đối tượng mô tả một mẫu ký tự. Các đối tượng RegExp thường được tạo bằng cú pháp chữ đặc biệt được trình bày bên dưới, nhưng cũng có thể được tạo bằng cách sử dụng hàm tạo RegExp().

Cú pháp

// sử dụng cú pháp chữ đặc biệt var regrec = /pattern /flags ; // sử dụng hàm tạo var regrec = new RegExp("pattern", "flags"); var regrec = new RegExp(/pattern /, "flags ");

Giá trị tham số:

Cờ biểu thức chính quy

Lá cờSự miêu tả
gCho phép bạn tìm tất cả các trận đấu thay vì dừng lại sau trận đấu đầu tiên ( cờ trận đấu toàn cầu).
TôiCho phép khớp không phân biệt chữ hoa chữ thường ( cờ bỏ qua trường hợp).
tôiViệc khớp được thực hiện trên nhiều hàng. Các ký tự đầu và cuối (^ và $) được xử lý trên nhiều dòng, nghĩa là sự trùng khớp xảy ra ở đầu hoặc cuối mỗi dòng (dấu phân cách \n hoặc \r) chứ không chỉ ở đầu hoặc cuối của toàn bộ dòng ( cờ nhiều dòng).
bạnMẫu sẽ được hiểu là một chuỗi các điểm mã Unicode ( cờ unicode).
yViệc so khớp xảy ra tại chỉ mục được trỏ đến bởi thuộc tính LastIndex của biểu thức chính quy này, trong khi việc so khớp không được thực hiện ở chỉ mục muộn hơn hoặc sớm hơn ( cờ dính).

Bộ nhân vật

Siêu ký tự

Biểu tượngSự miêu tả
. Cho phép bạn tìm một ký tự không phải là ký tự dòng mới hoặc ký tự cuối dòng (\n, \r, \u2028 hoặc \u2029).
\dCho phép bạn tìm một ký hiệu số trong bảng chữ cái Latinh cơ bản. Tương đương với việc sử dụng bộ ký tự.
\DCho phép bạn tìm bất kỳ ký tự nào không phải là số trong bảng chữ cái Latinh cơ bản. Tương đương với bộ ký tự [^0-9].
\SCho phép bạn tìm một ký tự khoảng trắng duy nhất. Khoảng trắng đề cập đến khoảng trắng, tab, nguồn cấp dữ liệu trang, nguồn cấp dữ liệu dòng và các ký tự khoảng trắng Unicode khác. Tương đương với bộ ký tự [\f\n\r\t\v​\u00a0\u1680​\u180e\u2000​\u2001\u2002​\u2003\u2004​\u2005\u2006​\u2007\u2008​\u2009\ u200a​ \u2028\u2029​\u202f\u205f​\u3000].
\SCho phép bạn tìm một ký tự không phải là khoảng trắng. Khoảng trắng đề cập đến khoảng trắng, tab, nguồn cấp dữ liệu trang, nguồn cấp dữ liệu dòng và các ký tự khoảng trắng Unicode khác. Tương đương với bộ ký tự [^ \f\n\r\t\v​\u00a0\u1680​\u180e\u2000​\u2001\u2002​\u2003\u2004​\u2005\u2006​\u2007\u2008​\u2009 \u200a ​\u2028\u2029​\u202f\u205f​\u3000].
[\b]Cho phép bạn tìm ký tự xóa lùi (ký tự đặc biệt \b, U+0008).
\0 Cho phép bạn tìm ký hiệu 0 (không).
\NCho phép bạn tìm ký tự dòng mới.
\fCho phép bạn tìm ký tự nguồn cấp dữ liệu trang.
\rCho phép bạn tìm ký tự trả về đầu dòng.
\tCho phép bạn tìm ký tự tab ngang.
\vCho phép bạn tìm ký tự tab dọc.
\wCho phép bạn tìm bất kỳ ký tự chữ và số nào trong bảng chữ cái Latinh cơ bản, bao gồm cả dấu gạch dưới. Tương đương với bộ ký tự.
\WCho phép bạn tìm bất kỳ ký tự nào không phải là ký tự trong bảng chữ cái Latinh cơ bản. Tương đương với bộ ký tự [^a-Za-z0-9_].
\cXCho phép bạn tìm ký tự điều khiển trong một chuỗi. Trong đó X là chữ cái từ A đến Z. Ví dụ: /\cM/ đại diện cho ký tự Ctrl-M.
\xhhCho phép bạn tìm ký tự sử dụng giá trị thập lục phân (hh là giá trị thập lục phân gồm hai chữ số).
\uhhhCho phép bạn tìm ký tự bằng cách sử dụng mã hóa UTF-16 (hhhh là giá trị thập lục phân gồm bốn chữ số).
\u(hhhh) hoặc
\u(hhhh)
Cho phép bạn tìm ký tự có giá trị Unicode là U+hhhh hoặc U+hhhh (giá trị thập lục phân). Chỉ khi cờ u được đưa ra.
\ Cho biết ký tự sau đây là đặc biệt và không nên hiểu theo nghĩa đen. Đối với các ký tự thường được hiểu theo cách đặc biệt, hãy chỉ định rằng ký tự sau không đặc biệt và phải được hiểu theo nghĩa đen.

Những hạn chế

định lượng

Biểu tượngSự miêu tả
N*Sự trùng khớp xảy ra trên bất kỳ chuỗi nào chứa 0 hoặc nhiều lần xuất hiện của ký tự N.
n+Việc so khớp xảy ra với bất kỳ chuỗi nào chứa ít nhất một ký tự N.
N?Trận đấu xảy ra trên bất kỳ chuỗi nào có phần tử trước N không hoặc một lần.
n(x)Khớp với bất kỳ chuỗi nào chứa một chuỗi ký tự N một số lần nhất định x. X
n(x,) x sự xuất hiện của phần tử trước N. X phải là số nguyên dương.
n(x, y)Khớp với bất kỳ chuỗi nào chứa ít nhất x, nhưng không nhiều hơn với y sự xuất hiện của phần tử trước N. Xy phải là số nguyên dương.
N*?
n+?
N??
n(x)?
n(x,)?
n(x,y)?
Việc so sánh xảy ra bằng cách tương tự với các bộ định lượng *, +, ? và (...) nhưng đồng thời tìm kiếm đang được tiến hành so sánh tối thiểu có thể. Mặc định là chế độ "tham lam", ? ở cuối bộ định lượng cho phép bạn đặt chế độ “không tham lam” trong đó việc so sánh được lặp lại với số lần tối thiểu có thể.
x(?=y)Cho phép bạn so sánh x, chỉ nếu đối với x nên y.
x(?!y)Cho phép bạn so sánh x, chỉ nếu đối với x đừng làm việc đó y.
x|yViệc so sánh xảy ra với bất kỳ lựa chọn thay thế nào được chỉ định.

Nhóm và liên kết ngược

Biểu tượngSự miêu tả
(x)Cho phép bạn tìm thấy một biểu tượng x và ghi nhớ kết quả so sánh ("lấy dấu ngoặc đơn"). Chuỗi con phù hợp có thể được gọi từ các phần tử mảng kết quả ..., [n] hoặc từ các thuộc tính của đối tượng RegExp được xác định trước $1 ..., $9.
(?:x)Cho phép bạn tìm thấy một biểu tượng x, nhưng không nhớ kết quả trận đấu ("dấu ngoặc đơn không bắt được"). Chuỗi con phù hợp không thể được gọi từ các phần tử mảng kết quả ..., [n] hoặc từ các thuộc tính của đối tượng RegExp được xác định trước $1 ..., $9.
\NTham chiếu trả về chuỗi con cuối cùng khớp với chuỗi thứ n trong dấu ngoặc đơn trong một biểu thức chính quy (đánh số dấu ngoặc đơn đi từ trái sang phải). N phải là số nguyên dương.

Một số người khi gặp vấn đề sẽ nghĩ: “Ồ, tôi sẽ sử dụng các biểu thức thông thường”. Bây giờ họ có hai vấn đề.
Jamie Zawinski

Yuan-Ma nói, “Phải dùng rất nhiều lực để cắt gỗ ngang thớ gỗ. Phải mất rất nhiều mã để lập trình theo cấu trúc vấn đề.
Master Yuan-Ma, “Sách lập trình”

Các công cụ và kỹ thuật lập trình tồn tại và lan rộng một cách tiến hóa hỗn loạn. Đôi khi không phải những thứ đẹp đẽ và rực rỡ mới tồn tại được mà chỉ đơn giản là những thứ hoạt động đủ tốt trong lĩnh vực của họ - ví dụ: nếu chúng được tích hợp vào một công nghệ thành công khác.

Trong chương này, chúng ta sẽ thảo luận về một công cụ như vậy - biểu thức chính quy. Đây là một cách để mô tả các mẫu trong dữ liệu chuỗi. Họ tạo ra một ngôn ngữ nhỏ, độc lập được bao gồm trong JavaScript cũng như nhiều ngôn ngữ và công cụ khác.

Những lịch trình đều đặn vừa rất lạ vừa cực kỳ hữu ích. Cú pháp của họ rất khó hiểu và giao diện phần mềm JavaScript thật rắc rối đối với họ. Nhưng nó là một công cụ mạnh mẽ để khám phá và thao tác với các chuỗi. Một khi bạn hiểu chúng, bạn sẽ trở thành một lập trình viên hiệu quả hơn.

Tạo biểu thức chính quy

Thông thường - loại đối tượng. Nó có thể được tạo bằng cách gọi hàm tạo RegExp hoặc bằng cách viết mẫu bắt buộc, được bao quanh bởi các dấu gạch chéo.

Var re1 = new RegExp("abc"); var re2 = /abc/;

Cả hai biểu thức chính quy này đều biểu thị cùng một mẫu: ký tự “a” theo sau là ký tự “b” theo sau là ký tự “c”.

Nếu bạn sử dụng hàm tạo RegExp thì mẫu sẽ được viết dưới dạng chuỗi thông thường, do đó tất cả các quy tắc liên quan đến dấu gạch chéo ngược đều được áp dụng.

Mục nhập thứ hai, trong đó mẫu nằm giữa các dấu gạch chéo, xử lý các dấu gạch chéo ngược theo cách khác. Đầu tiên, vì mẫu kết thúc bằng dấu gạch chéo lên, chúng ta cần đặt dấu gạch chéo ngược trước dấu gạch chéo lên mà chúng ta muốn đưa vào mẫu của mình. Ngoài ra, các dấu gạch chéo ngược không phải là một phần của các ký tự đặc biệt như \n sẽ được giữ nguyên (thay vì bị bỏ qua như trong chuỗi) và sẽ thay đổi ý nghĩa của mẫu. Một số ký tự, chẳng hạn như dấu chấm hỏi hoặc dấu cộng, có ý nghĩa đặc biệt trong biểu thức chính quy và nếu bạn cần tìm ký tự như vậy thì trước nó cũng phải có dấu gạch chéo ngược.

Var 18Plus = /eightteen\+/;

Để biết những ký tự nào cần phải có dấu gạch chéo trước, bạn cần tìm hiểu danh sách tất cả các ký tự đặc biệt trong biểu thức chính quy. Điều này vẫn chưa thực hiện được, vì vậy khi nghi ngờ, chỉ cần đặt dấu gạch chéo ngược trước bất kỳ ký tự nào không phải là chữ cái, số hoặc dấu cách.

Kiểm tra các trận đấu

Chính quy có một số phương pháp. Đơn giản nhất là kiểm tra. Nếu bạn truyền cho nó một chuỗi, nó sẽ trả về giá trị Boolean cho biết liệu chuỗi đó có chứa sự xuất hiện của mẫu đã cho hay không.

Console.log(/abc/.test("abcde")); // → true console.log(/abc/.test("abxde")); // → sai

Một chuỗi thông thường chỉ bao gồm các ký tự không đặc biệt chỉ đơn giản là một chuỗi các ký tự này. Nếu abc ở bất kỳ vị trí nào trong dòng chúng ta đang kiểm tra (không chỉ ở đầu), kiểm tra sẽ trả về true.

Tìm kiếm một bộ ký tự

Bạn cũng có thể tìm hiểu xem một chuỗi có chứa abc hay không bằng cách sử dụng indexOf. Các mẫu thông thường cho phép bạn tiến xa hơn và tạo ra các mẫu phức tạp hơn.

Giả sử chúng ta cần tìm bất kỳ số nào. Khi chúng ta đặt một tập hợp các ký tự trong dấu ngoặc vuông trong biểu thức chính quy, điều đó có nghĩa là phần đó của biểu thức khớp với bất kỳ ký tự nào trong dấu ngoặc.

Cả hai biểu thức đều nằm trong dòng chứa một số.

Console.log(//.test("năm 1992")); // → true console.log(//.test("năm 1992")); // → đúng

Trong dấu ngoặc vuông, dấu gạch ngang giữa hai ký tự được sử dụng để chỉ định phạm vi ký tự trong đó chuỗi được chỉ định mã hóa Unicode. Các ký tự từ 0 đến 9 chỉ nằm trong một hàng (mã từ 48 đến 57), vì vậy nó nắm bắt tất cả và khớp với bất kỳ số nào.

Một số nhóm ký tự có chữ viết tắt tích hợp riêng.

\d Bất kỳ số nào
\w Ký tự chữ và số
\s Ký tự khoảng trắng (dấu cách, tab, dòng mới, v.v.)
\D không phải là số
\W không phải là ký tự chữ và số
\S không phải là ký tự khoảng trắng
. bất kỳ ký tự nào ngoại trừ nguồn cấp dữ liệu dòng

Do đó, bạn có thể đặt định dạng ngày và giờ như 30/01/2003 15:20 với biểu thức sau:

Thay đổi ngàyThời gian = /\d\d-\d\d-\d\d\d\d \d\d:\d\d/; console.log(dateTime.test("30-01-2003 15:20")); // → true console.log(dateTime.test("30-Jan-2003 15:20")); // → sai

Trông thật khủng khiếp phải không? Có quá nhiều dấu gạch chéo ngược khiến cho mẫu khó hiểu. Chúng tôi sẽ cải thiện nó một chút sau.

Dấu gạch chéo ngược cũng có thể được sử dụng trong dấu ngoặc vuông. Ví dụ: [\d.] có nghĩa là bất kỳ số hoặc dấu chấm nào. Lưu ý rằng dấu chấm bên trong dấu ngoặc vuông mất đi ý nghĩa đặc biệt của nó và trở thành một dấu chấm đơn giản. Điều tương tự cũng áp dụng cho các ký tự đặc biệt khác, chẳng hạn như +.

Bạn có thể đảo ngược một tập hợp các ký tự - nghĩa là bạn cần tìm bất kỳ ký tự nào ngoại trừ những ký tự có trong tập hợp đó - bằng cách đặt dấu ^ ngay sau dấu ngoặc vuông mở đầu.

Var notBinary = /[^01]/; console.log(notBinary.test("1100100010100110")); // → sai console.log(notBinary.test("1100100010200110")); // → đúng

Lặp lại các phần của mẫu

Chúng ta biết cách tìm một số. Điều gì sẽ xảy ra nếu chúng ta cần tìm số nguyên - một dãy gồm một hoặc nhiều chữ số?

Nếu bạn đặt dấu + sau thứ gì đó theo trình tự thông thường, điều này có nghĩa là phần tử này có thể được lặp lại nhiều lần. /\d+/ có nghĩa là một hoặc nhiều chữ số.

Console.log(/"\d+"/.test(""123"")); // → true console.log(/"\d+"/.test("""")); // → false console.log(/"\d*"/.test(""123"")); // → true console.log(/"\d*"/.test("""")); // → đúng

Dấu hoa thị * có ý nghĩa gần như giống nhau nhưng nó cho phép mẫu này không xuất hiện lần nào. Nếu thứ gì đó được theo sau bởi dấu hoa thị thì nó không bao giờ ngăn mẫu nằm trong dòng - nó chỉ xuất hiện ở đó 0 lần.

Dấu chấm hỏi làm cho một phần của mẫu trở thành tùy chọn, nghĩa là nó có thể xuất hiện 0 hoặc một lần. Trong ví dụ sau, ký tự u có thể xuất hiện nhưng mẫu này khớp ngay cả khi nó không xuất hiện.

Var hàng xóm = /neighbou?r/; console.log(neighbor.test("hàng xóm")); // → true console.log(neighbor.test("neighbor")); // → đúng

Dấu ngoặc nhọn được sử dụng để xác định chính xác số lần một mẫu phải xuất hiện. (4) sau một phần tử có nghĩa là nó phải xuất hiện 4 lần trong dòng. Bạn cũng có thể chỉ định một khoảng trống: (2,4) nghĩa là phần tử đó phải xuất hiện ít nhất 2 và không quá 4 lần.

Một phiên bản khác của định dạng ngày và giờ, trong đó cho phép ngày, tháng và giờ có một hoặc hai chữ số. Và nó cũng dễ đọc hơn một chút.

Var dateTime = /\d(1,2)-\d(1,2)-\d(4) \d(1,2):\d(2)/; console.log(dateTime.test("30-1-2003 8:45")); // → đúng

Bạn có thể sử dụng không gian mở bằng cách bỏ qua một trong các số. (,5,) có nghĩa là mẫu có thể xuất hiện từ 0 đến năm lần và (5,) có nghĩa là từ năm lần trở lên.

Nhóm các biểu thức con

Để sử dụng toán tử * hoặc + trên nhiều phần tử cùng một lúc, bạn có thể sử dụng dấu ngoặc tròn. Phần của biểu thức chính quy được đặt trong ngoặc được coi là một phần tử theo quan điểm của các toán tử.

Var phim hoạt hìnhCrying = /boo+(hoo+)+/i; console.log(cartoonCrying.test("Boohoooohoohoooo")); // → đúng

Điểm cộng thứ nhất và thứ hai chỉ áp dụng cho chữ o thứ hai trong boo và hoo. + thứ ba đề cập đến cả nhóm (hoo+), tìm một hoặc nhiều chuỗi như vậy.

Chữ cái i ở cuối biểu thức làm cho biểu thức chính quy không phân biệt chữ hoa chữ thường - sao cho B khớp với b.

Trận đấu và Nhóm

Phương pháp kiểm tra là phương pháp đơn giản nhất để kiểm tra các biểu thức chính quy. Nó chỉ cho bạn biết liệu kết quả có được tìm thấy hay không. Chính quy cũng có một phương thức exec, phương thức này sẽ trả về null nếu không tìm thấy gì và nếu không thì trả về một đối tượng có thông tin về trận đấu.

Var match = /\d+/.exec("một hai 100"); console.log(match); // → ["100"] console.log(match.index); // → 8

Đối tượng được trả về bởi exec có thuộc tính chỉ mục, chứa số lượng ký tự mà từ đó sự trùng khớp xảy ra. Nói chung, đối tượng trông giống như một mảng các chuỗi, trong đó phần tử đầu tiên là chuỗi đã được kiểm tra xem có khớp không. Trong ví dụ của chúng tôi, đây sẽ là dãy số chúng tôi đang tìm kiếm.

Chuỗi có một phương thức khớp hoạt động theo cách tương tự.

Console.log("một hai 100".match(/\d+/)); // → ["100"]

Khi một biểu thức chính quy chứa các biểu thức con được nhóm theo dấu ngoặc đơn, văn bản khớp với các nhóm này cũng sẽ xuất hiện trong mảng. Phần tử đầu tiên luôn khớp hoàn toàn. Phần thứ hai là phần khớp với nhóm đầu tiên (phần có dấu ngoặc đơn xuất hiện đầu tiên), sau đó là nhóm thứ hai, v.v.

Var quoteText = /"([^"]*)"/; console.log(quotedText.exec("she say "hello"")); // → [""hello"", "hello"]

Khi hoàn toàn không tìm thấy một nhóm nào (ví dụ: nếu theo sau nó là dấu chấm hỏi), vị trí của nhóm đó trong mảng là không xác định. Nếu một nhóm khớp nhiều lần thì chỉ có kết quả khớp cuối cùng nằm trong mảng.

Console.log(/bad(ly)?/.exec("bad")); // → ["xấu", không xác định] console.log(/(\d)+/.exec("123")); // → ["123", "3"]

Các nhóm rất hữu ích cho việc truy xuất các phần của chuỗi. Nếu chúng ta không chỉ muốn kiểm tra xem một chuỗi có ngày hay không mà còn trích xuất nó và tạo một đối tượng biểu thị ngày, thì chúng ta có thể đặt các chuỗi số trong dấu ngoặc đơn và chọn ngày từ kết quả của lệnh thực thi.

Nhưng trước tiên, hãy tìm hiểu sâu hơn một chút trong đó chúng ta sẽ tìm hiểu cách lưu trữ ngày và giờ ưa thích trong JavaScript.

Loại ngày

JavaScript có một loại đối tượng tiêu chuẩn cho ngày tháng—cụ thể hơn là các khoảnh khắc đúng lúc. Nó được gọi là Ngày. Nếu bạn chỉ cần tạo một đối tượng ngày qua mới, bạn sẽ nhận được ngay hiện tại và thời gian.

Console.log(Ngày mới()); // → Chủ nhật ngày 09 tháng 11 năm 2014 00:07:57 GMT+0300 (CET)

Bạn cũng có thể tạo một đối tượng chứa thời gian nhất định

Console.log(Ngày mới(2015, 9, 21)); // → Thứ Tư ngày 21 tháng 10 năm 2015 00:00:00 GMT+0300 (CET) console.log(Ngày mới (2009, 11, 9, 12, 59, 59, 999)); // → Thứ Tư ngày 09 tháng 12 năm 2009 12:59:59 GMT+0300 (CET)

JavaScript sử dụng quy ước trong đó số tháng bắt đầu bằng số 0 và số ngày bắt đầu bằng số 1. Điều này thật ngu ngốc và lố bịch. Hãy cẩn thận.

Bốn đối số cuối cùng (giờ, phút, giây và mili giây) là tùy chọn và được đặt thành 0 nếu thiếu.

Dấu thời gian được lưu trữ dưới dạng số mili giây đã trôi qua kể từ đầu năm 1970. Đối với thời gian trước năm 1970, hãy sử dụng số âm(điều này là do quy ước thời gian Unix được tạo ra vào khoảng thời gian đó). Phương thức getTime của đối tượng date trả về số này. Tự nhiên nó lớn.
console.log(new Date(2013, 11, 19).getTime()); // → 1387407600000 console.log(Ngày mới(1387407600000)); // → Thứ năm ngày 19 tháng 12 năm 2013 00:00:00 GMT+0100 (CET)

Nếu bạn cung cấp cho hàm tạo Ngày một đối số, nó sẽ được coi là số mili giây này. Bạn có thể nhận giá trị mili giây hiện tại bằng cách tạo đối tượng Date và gọi phương thức getTime hoặc bằng cách gọi hàm Date.now.

Đối tượng Date có các phương thức getFullYear, getMonth, getDate, getHours, getMinutes và getSeconds để truy xuất các thành phần của nó. Ngoài ra còn có một phương thức getYear trả về mã có hai chữ số khá vô dụng như 93 hoặc 14.

Bằng cách đặt các phần có liên quan của mẫu trong dấu ngoặc đơn, chúng ta có thể tạo đối tượng ngày tháng trực tiếp từ chuỗi.

Hàm findDate(string) ( var dateTime = /(\d(1,2))-(\d(1,2))-(\d(4))/; var match = dateTime.exec(string); return new Date(Number(match), Number(match) - 1, Number(match)); ) console.log(findDate("30-1-2003")); // → Thứ năm ngày 30 tháng 1 năm 2003 00:00:00 GMT+0100 (CET)

Ranh giới từ và dòng

Thật không may, findDate sẽ vui vẻ trích xuất ngày vô nghĩa 00-1-3000 từ chuỗi "100-1-30000". Sự so khớp có thể xảy ra ở bất cứ đâu trong chuỗi, vì vậy trong trường hợp này nó sẽ chỉ bắt đầu từ ký tự thứ hai và kết thúc ở ký tự thứ hai đến ký tự cuối cùng.

Nếu chúng ta cần buộc trận đấu lấy toàn bộ chuỗi, chúng ta sử dụng thẻ ^ và $. ^ khớp với đầu dòng và $ khớp với cuối dòng. Do đó /^\d+$/ khớp với một chuỗi chỉ bao gồm một hoặc nhiều chữ số, /^!/ khớp với một chuỗi bắt đầu bằng dấu chấm than, và /x^/ không khớp với bất kỳ dòng nào (không được có dấu x trước đầu dòng).

Mặt khác, nếu chúng ta chỉ muốn đảm bảo rằng ngày bắt đầu và kết thúc trên một ranh giới từ, chúng ta sử dụng dấu \b. Ranh giới từ có thể là điểm bắt đầu hoặc kết thúc của một dòng hoặc bất kỳ vị trí nào trong một dòng có ký tự chữ và số \w ở một bên và ký tự không phải chữ và số ở bên kia.

Console.log(/cat/.test("concatenate")); // → true console.log(/\bcat\b/.test("concatenate")); // → sai

Lưu ý rằng nhãn ranh giới không phải là một biểu tượng. Nó chỉ đơn giản là một ràng buộc, có nghĩa là sự trùng khớp chỉ xảy ra nếu đáp ứng một điều kiện nhất định.

Mẫu có sự lựa chọn

Giả sử bạn cần tìm hiểu xem văn bản không chỉ chứa một số mà còn chứa một số theo sau là lợn, bò hoặc gà ở số ít hay số nhiều.

Có thể viết ba biểu thức chính quy và kiểm tra từng biểu thức một, nhưng có một cách tốt hơn. Biểu tượng | biểu thị sự lựa chọn giữa các mẫu ở bên trái và bên phải của nó. Và chúng ta có thể nói như sau:

Var AnimalCount = /\b\d+ (lợn|bò|gà)s?\b/; console.log(animalCount.test("15 con lợn")); // → true console.log(animalCount.test("15 con gà con")); // → sai

Dấu ngoặc đơn phân định phần của mẫu mà | được áp dụng và nhiều toán tử như vậy có thể được đặt lần lượt để biểu thị lựa chọn từ nhiều hơn hai tùy chọn.

Máy tìm kiếm

Biểu thức chính quy có thể được coi như các sơ đồ. Sơ đồ sau đây mô tả một ví dụ về chăn nuôi gần đây.

Một biểu thức khớp với một chuỗi nếu có thể tìm thấy đường dẫn từ bên trái của sơ đồ sang bên phải. Chúng ta ghi nhớ vị trí hiện tại trong dòng và mỗi lần duyệt qua hình chữ nhật, chúng ta kiểm tra xem phần của dòng ngay sau vị trí của chúng ta trong đó có khớp với nội dung của hình chữ nhật hay không.

Điều này có nghĩa là việc kiểm tra sự trùng khớp của ký tự thông thường của chúng ta trong chuỗi “3 con lợn” khi xem qua sơ đồ sẽ như sau:

Ở vị trí 4 có ranh giới từ và chúng ta chuyển hình chữ nhật đầu tiên
- bắt đầu từ vị trí thứ 4 chúng ta tìm số và đi qua hình chữ nhật thứ hai
- ở vị trí 5, một đường dẫn đóng lại phía trước hình chữ nhật thứ hai và đường dẫn thứ hai đi xa hơn đến hình chữ nhật có khoảng trắng. Chúng tôi có một khoảng trắng, không phải một con số và chúng tôi chọn con đường thứ hai.
- bây giờ chúng ta đang ở vị trí 6, điểm bắt đầu của “lợn” và ở ngã ba của các con đường. Trong hàng không có “bò” hay “gà” mà có “lợn” nên chúng tôi chọn con đường này.
- tại vị trí 9 sau ngã ba, một đường đi qua “s” và đi đến hình chữ nhật ranh giới từ cuối cùng, và đường thứ hai đi qua “s”. Chúng ta có chữ “s” nên chúng ta tới đó.
- ở vị trí 10 ta ở cuối dòng, chỉ có ranh giới từ mới khớp được. Điểm cuối của dòng được coi là ranh giới và chúng ta đi qua hình chữ nhật cuối cùng. Và bây giờ chúng ta đã tìm thấy thành công mẫu của mình.

Về cơ bản, cách hoạt động của biểu thức chính quy là thuật toán bắt đầu ở đầu chuỗi và cố gắng tìm kết quả khớp ở đó. Trong trường hợp của chúng ta, có một ranh giới từ nên nó vượt qua hình chữ nhật đầu tiên - nhưng không có số ở đó nên nó vấp phải hình chữ nhật thứ hai. Sau đó, nó di chuyển đến ký tự thứ hai trong chuỗi và cố gắng tìm kết quả khớp ở đó... Và cứ tiếp tục như vậy cho đến khi nó tìm thấy kết quả khớp hoặc đến cuối chuỗi, trong trường hợp đó không tìm thấy kết quả khớp nào.

Hối lộ

Biểu thức chính quy /\b(+b|\d+|[\da-f]h)\b/ khớp với số nhị phân theo sau là b, số thập phân không có hậu tố hoặc số thập lục phân (các số từ 0 đến 9 hoặc các ký hiệu từ a đến h), theo sau là h. Sơ đồ liên quan:

Khi tìm kiếm kết quả trùng khớp, có thể xảy ra trường hợp thuật toán lấy đường dẫn trên cùng (số nhị phân), ngay cả khi không có số đó trong chuỗi. Ví dụ: nếu có dòng “103”, thì rõ ràng là chỉ sau khi đạt đến số 3, thuật toán mới hiểu rằng nó đang đi sai đường. Nói chung, dòng này khớp với trình tự thông thường, chỉ là không có trong chuỗi này.

Sau đó, thuật toán quay trở lại. Tại một ngã ba, nó ghi nhớ vị trí hiện tại (trong trường hợp của chúng tôi, đây là điểm bắt đầu của dòng, ngay sau ranh giới từ) để bạn có thể quay lại và thử một đường dẫn khác nếu đường đã chọn không hoạt động. Đối với dòng “103”, sau khi gặp troika, anh ta sẽ quay lại và cố gắng đi qua con đường dành cho số thập phân. Điều này sẽ hoạt động để một trận đấu sẽ được tìm thấy.

Thuật toán dừng ngay khi tìm thấy kết quả khớp hoàn chỉnh. Điều này có nghĩa là ngay cả khi một số tùy chọn có thể phù hợp thì chỉ một trong số chúng được sử dụng (theo thứ tự chúng xuất hiện theo trình tự thông thường).

Quay lui xảy ra khi sử dụng các toán tử lặp lại như + và *. Nếu bạn tìm kiếm /^.*x/ trong chuỗi "abcxe", phần biểu thức chính quy.* sẽ cố gắng tiêu thụ toàn bộ chuỗi. Thuật toán sau đó sẽ nhận ra rằng nó cũng cần “x”. Vì không có chữ “x” ở cuối chuỗi nên thuật toán sẽ cố gắng tìm kiếm sự trùng khớp bằng cách di chuyển lùi lại một ký tự. Sau abcx cũng không có x, sau đó nó lại quay trở lại, lần này về chuỗi con abc. Và sau dòng, nó tìm x và báo kết quả khớp thành công, ở vị trí từ 0 đến 4.

Bạn có thể viết một quy trình thông thường sẽ dẫn đến nhiều lần khôi phục. Sự cố này xảy ra khi mẫu có thể khớp với dữ liệu đầu vào nhiều lần. những cách khác. Ví dụ: nếu chúng ta mắc lỗi khi viết biểu thức chính quy cho số nhị phân, chúng ta có thể vô tình viết một cái gì đó như /(+)+b/.

Nếu thuật toán tìm kiếm một mẫu như vậy trong hàng dài của các số 0 và các số không chứa chữ “b” ở cuối, trước tiên nó sẽ đi qua vòng lặp bên trong cho đến khi hết chữ số. Sau đó, anh ta sẽ nhận thấy rằng không có chữ “b” ở cuối, anh ta sẽ lùi lại một vị trí, đi qua vòng ngoài, bỏ cuộc lần nữa, cố gắng quay trở lại vị trí khác dọc theo vòng trong… Và anh ta sẽ tiếp tục. để tìm kiếm theo cách này, sử dụng cả hai vòng lặp. Tức là khối lượng công việc với mỗi ký tự của dòng sẽ tăng gấp đôi. Ngay cả đối với vài chục nhân vật, việc tìm kiếm sự trùng khớp sẽ mất rất nhiều thời gian.

phương pháp thay thế

Chuỗi có một phương thức thay thế có thể thay thế một phần của chuỗi bằng một chuỗi khác.

Console.log("dad".replace("p", "m")); // → bản đồ

Đối số đầu tiên cũng có thể là một biểu thức chính quy, trong trường hợp đó, lần xuất hiện đầu tiên của biểu thức chính quy trong dòng sẽ được thay thế. Khi tùy chọn “g” (toàn cục) được thêm vào biểu thức chính quy, tất cả các lần xuất hiện đều được thay thế, không chỉ lần đầu tiên

Console.log("Borobudur".replace(//, "a")); // → Barobudur console.log("Borobudur".replace(//g, "a")); // → Barabadar

Sẽ hợp lý hơn nếu chuyển tùy chọn "thay thế tất cả" thông qua một đối số riêng hoặc thông qua một phương thức riêng như thay thế. Nhưng thật không may, tùy chọn này được truyền qua chính hệ thống thông thường.

Toàn bộ sức mạnh của biểu thức chính quy được bộc lộ khi chúng ta sử dụng liên kết đến các nhóm được tìm thấy trong một chuỗi, được chỉ định trong biểu thức chính quy. Ví dụ: chúng tôi có một dòng chứa tên người, mỗi tên một dòng, ở định dạng "Họ, Tên". Nếu chúng ta cần hoán đổi chúng và xóa dấu phẩy để lấy “Tên họ”, chúng ta viết như sau:

Console.log("Hopper, Grace\nMcCarthy, John\nRitchie, Dennis" .replace(/([\w ]+), ([\w ]+)/g, "$2 $1")); // → Grace Hopper // John McCarthy // Dennis Ritchie

$1 và $2 trong dòng thay thế đề cập đến các nhóm ký tự được đặt trong dấu ngoặc đơn. $1 được thay thế bằng văn bản khớp với nhóm đầu tiên, $2 với nhóm thứ hai, v.v., tối đa $9. Toàn bộ kết quả khớp được chứa trong biến $&.

Bạn cũng có thể truyền một hàm làm đối số thứ hai. Đối với mỗi lần thay thế, một hàm sẽ được gọi mà đối số của nó sẽ là các nhóm được tìm thấy (và toàn bộ phần khớp của dòng) và kết quả của nó sẽ được chèn vào một dòng mới.

Ví dụ đơn giản:

Var s = "cia và fbi"; console.log(s.replace(/\b(fbi|cia)\b/g, function(str) ( return str.toUpperCase(); ))); // → CIA và FBI

Đây là một điều thú vị hơn:

Var stock = "1 quả chanh, 2 củ cải và 101 quả trứng"; hàm trừOne(match, money, unit) ( money = Number(amount) - 1; if (amount == 1) // chỉ còn lại một, xóa "s" ở cuối unit = unit.slice(0, unit. length - 1); else if (amount == 0) money = "no"; trả về số tiền + " " + unit; ) console.log(stock.replace(/(\d+) (\w+)/g,trừOne) ); // → không chanh, 1 bắp cải và 100 quả trứng

Mã này lấy một chuỗi, tìm tất cả các lần xuất hiện của các số theo sau là một từ và trả về một chuỗi với mỗi số giảm đi một.

Nhóm (\d+) đi vào đối số số lượng và (\w+) đi vào đối số đơn vị. Hàm chuyển đổi số tiền thành số - và điều này luôn hoạt động vì mẫu của chúng tôi là \d+. Và sau đó thực hiện thay đổi từ, trong trường hợp chỉ còn 1 mục.

Tham lam

Thật dễ dàng để sử dụng thay thế để viết một hàm loại bỏ tất cả các nhận xét khỏi Mã JavaScript. Đây là lần thử đầu tiên:

Hàm StripComments(code) ( return code.replace(/\/\/.*|\/\*[^]*\*\//g, ""); ) console.log(stripComments("1 + /* 2 */3")); // → 1 + 3 console.log(stripComments("x = 10;// ten!")); // → x = 10; console.log(stripComments("1 /* a */+/* b */ 1")); // → 1 1

Phần trước toán tử "hoặc" khớp với hai dấu gạch chéo theo sau là bất kỳ số lượng ký tự nào ngoại trừ dòng mới. Phần loại bỏ các bình luận nhiều dòng phức tạp hơn. Chúng tôi sử dụng [^], tức là ký tự nào không trống là cách tìm ký tự bất kỳ. Chúng tôi không thể sử dụng dấu chấm vì khối nhận xét vẫn tiếp tục dòng mới và ký tự dòng mới không khớp với dấu chấm.

Nhưng kết quả của ví dụ trước không chính xác. Tại sao?

Phần [^]* trước tiên sẽ cố gắng chụp càng nhiều ký tự càng tốt. Nếu vì điều này mà phần tiếp theo của chuỗi thông thường không tìm thấy kết quả khớp, nó sẽ quay lại một ký tự và thử lại. Trong ví dụ này, thuật toán cố gắng lấy toàn bộ dòng rồi quay lại. Sau khi quay lại 4 ký tự, anh ta sẽ tìm thấy */ trong dòng - và đây không phải là điều chúng tôi mong muốn. Chúng tôi chỉ muốn lấy một bình luận chứ không phải đi đến cuối dòng và tìm bình luận cuối cùng.

Do đó, chúng tôi nói rằng các toán tử lặp lại (+, *, ?, và ()) rất tham lam, nghĩa là trước tiên chúng lấy càng nhiều càng tốt rồi quay lại. Nếu bạn đặt câu hỏi sau một toán tử như thế này (+?, *?, ??, ()?), họ sẽ trở nên không tham lam và bắt đầu tìm những lần xuất hiện nhỏ nhất có thể.

Và đó là những gì chúng ta cần. Bằng cách buộc dấu hoa thị tìm các kết quả trùng khớp với số lượng ký tự tối thiểu có thể có trong một dòng, chúng tôi chỉ sử dụng một khối nhận xét và không còn nữa.

Hàm StripComments(code) ( return code.replace(/\/\/.*|\/\*[^]*?\*\//g, ""); ) console.log(stripComments("1 /* a */+/* b */ 1")); // → 1 + 1

Nhiều lỗi xảy ra khi sử dụng toán tử tham lam thay vì toán tử không tham lam. Khi sử dụng toán tử lặp lại, trước tiên hãy luôn xem xét toán tử không tham lam.

Tự động tạo các đối tượng RegExp

Trong một số trường hợp, mẫu chính xác không được xác định tại thời điểm viết mã. Ví dụ: bạn sẽ cần tìm tên người dùng trong văn bản và đặt nó trong dấu gạch dưới. Vì bạn sẽ chỉ biết tên sau khi chạy chương trình nên bạn không thể sử dụng ký hiệu gạch chéo.

Nhưng bạn có thể xây dựng chuỗi và sử dụng hàm tạo RegExp. Đây là một ví dụ:

Tên Var = "Harry"; var text = "Và Harry có một vết sẹo trên trán."; var regrec = new RegExp("\\b(" + name + ")\\b", "gi"); console.log(text.replace(regexp, "_$1_")); // → Và _Harry_ có một vết sẹo trên trán.

Khi tạo ranh giới từ, chúng ta phải sử dụng dấu gạch chéo kép vì chúng ta viết chúng theo dòng thông thường chứ không phải theo trình tự đều đặn với dấu gạch chéo lên. Đối số thứ hai của RegExp chứa các tùy chọn cho biểu thức chính quy - trong trường hợp của chúng tôi là “gi”, tức là. toàn cầu và không phân biệt chữ hoa chữ thường.

Nhưng nếu tên đó là “dea+hlrd” (nếu người dùng của chúng tôi là kulhatzker) thì sao? Kết quả là chúng ta sẽ nhận được một biểu thức chính quy vô nghĩa và không tìm thấy kết quả khớp trong chuỗi.

Chúng ta có thể thêm dấu gạch chéo ngược trước bất kỳ ký tự nào mà chúng ta không thích. Chúng tôi không thể thêm dấu gạch chéo ngược trước các chữ cái vì \b hoặc \n là các ký tự đặc biệt. Nhưng bạn có thể thêm dấu gạch chéo trước bất kỳ ký tự không phải chữ và số nào mà không gặp vấn đề gì.

Tên Var = "dea+hlrd"; var text = "Cái dea+hlrd này làm mọi người khó chịu."; var escape = name.replace(/[^\w\s]/g, "\\$&"); var regrec = new RegExp("\\b(" + escape + ")\\b", "gi"); console.log(text.replace(regexp, "_$1_")); // → Điều này _dea+hlrd_ làm mọi người khó chịu.

phương pháp tìm kiếm

Phương thức indexOf không thể được sử dụng với các biểu thức thông thường. Nhưng có một phương pháp tìm kiếm chỉ mong đợi biểu thức chính quy. Giống như indexOf, nó trả về chỉ mục của lần xuất hiện đầu tiên hoặc -1 nếu không xảy ra.

Console.log("word".search(/\S/)); // → 2 console.log(" ".search(/\S/)); // → -1

Thật không may, không có cách nào để yêu cầu phương thức tìm kiếm kết quả khớp bắt đầu từ một giá trị chênh lệch cụ thể (như bạn có thể làm với indexOf). Điều đó sẽ hữu ích.

thuộc tính Last Index

Phương thức exec cũng không hoạt động Một cách thuận tiện bắt đầu tìm kiếm từ một vị trí nhất định trong chuỗi. Nhưng nó đưa ra một cách bất tiện.

Một đối tượng biểu thức chính quy có các thuộc tính. Một trong số đó là nguồn, chứa một chuỗi. Một cái khác là LastIndex, nó kiểm soát, trong một số điều kiện, nơi việc tìm kiếm lần xuất hiện tiếp theo sẽ bắt đầu.

Các điều kiện này bao gồm tùy chọn chung g phải có mặt và việc tìm kiếm phải được thực hiện bằng phương thức exec. Một giải pháp hợp lý hơn là chỉ cần cho phép chuyển một đối số bổ sung cho người thực thi, nhưng tính hợp lý không phải là tính năng cơ bản của giao diện biểu thức chính quy JavaScript.

Mẫu Var = /y/g; mẫu.lastIndex = 3; var match = model.exec("xyzzy"); console.log(match.index); // → 4 console.log(pattern.lastIndex); // → 5

Nếu tìm kiếm thành công, lệnh gọi exec sẽ cập nhật thuộc tính LastIndex để trỏ đến vị trí sau lần xuất hiện được tìm thấy. Nếu không thành công, LastIndex được đặt thành 0 - giống như LastIndex của đối tượng mới được tạo.

Khi sử dụng một biến thông thường toàn cục và nhiều lệnh gọi exec, những lệnh này cập nhật tự động LastIndex có thể gây ra vấn đề. Máy chủ thông thường của bạn có thể bắt đầu tìm kiếm từ vị trí còn lại từ cuộc gọi trước đó.

Chữ số Var = /\d/g; console.log(digit.exec("đây là: 1")); // → ["1"] console.log(digit.exec("and now: 1")); // → vô giá trị

Khác hiệu ứng thú vị Tùy chọn g là nó thay đổi cách hoạt động của phương thức khớp. Khi được gọi bằng tùy chọn này, thay vì trả về một mảng tương tự như kết quả của lệnh exec, nó sẽ tìm tất cả các lần xuất hiện của mẫu trong chuỗi và trả về một mảng gồm các chuỗi con được tìm thấy.

Console.log("Banana".match(/an/g)); // → ["an", "an"]

Vì vậy, hãy cẩn thận với các biến thông thường toàn cầu. Các trường hợp cần thiết - thay thế các cuộc gọi hoặc địa điểm mà bạn sử dụng cụ thể LastIndex - có lẽ là tất cả các trường hợp chúng nên được sử dụng.

Chu kỳ xảy ra

Một nhiệm vụ điển hình là lặp qua tất cả các lần xuất hiện của một mẫu trong chuỗi để nó có thể truy cập đối tượng khớp trong phần thân vòng lặp bằng cách sử dụng LastIndex và exec.

Var input = "Một dòng có 3 số trong đó... 42 và 88."; số var = /\b(\d+)\b/g; trận đấu var; while (match = number.exec(input)) console.log("Đã tìm thấy ", match," on ", match.index); // → Tìm thấy 3 x 14 // Tìm thấy 42 x 33 // Tìm thấy 88 x 40

Nó lợi dụng thực tế là giá trị của phép gán là giá trị được gán. Sử dụng match = re.exec(input) làm điều kiện trong trong khi lặp lại, chúng tôi tìm kiếm ở đầu mỗi lần lặp, lưu kết quả vào một biến và kết thúc vòng lặp khi tìm thấy tất cả các kết quả khớp.

Phân tích tệp INI

Để kết thúc chương này, chúng ta hãy xem xét một vấn đề sử dụng biểu thức chính quy. Hãy tưởng tượng rằng chúng ta đang viết một chương trình tự động thu thập thông tin về kẻ thù của chúng ta qua Internet. (Chúng tôi sẽ không viết toàn bộ chương trình, chỉ phần đọc tệp cài đặt. Xin lỗi.) Tệp trông như thế này:

Searchengine=http://www.google.com/search?q=$1 cay đắng=9.7 ; dấu chấm phẩy được đặt trước các bình luận; mỗi phần đề cập đến một kẻ thù khác nhau fullname=Larry Doe type=kindergarten bull website=http://www.geocities.com/CapeCanaveral/11451 fullname=Gargamel type=ác sĩ đầu radir=/home/marijn/enemies/gargamel

Định dạng tệp chính xác (được sử dụng khá rộng rãi và thường được gọi là INI) như sau:

Các dòng trống và các dòng bắt đầu bằng dấu chấm phẩy sẽ bị bỏ qua
- các dòng trong ngoặc vuông bắt đầu một phần mới
- các dòng chứa mã định danh chữ và số theo sau là = thêm cài đặt trong phần này

Mọi thứ khác đều là dữ liệu không chính xác.

Nhiệm vụ của chúng ta là chuyển đổi một chuỗi như vậy thành một mảng các đối tượng, mỗi đối tượng có một thuộc tính tên và một mảng cài đặt. Một đối tượng là cần thiết cho mỗi phần và một đối tượng khác là cần thiết cho cài đặt chung ở đầu tệp.

Vì tệp cần được phân tích cú pháp từng dòng nên bạn nên bắt đầu bằng cách chia tệp thành các dòng. Để làm điều này, chúng ta đã sử dụng string.split("\n") trong Chương 6. Một số hệ điều hành không sử dụng một ký tự \n để ngắt dòng mà sử dụng hai - \r\n. Bởi vì phương pháp chia lấy các biểu thức chính quy làm đối số, chúng ta có thể phân chia các dòng bằng cách sử dụng biểu thức /\r?\n/, cho phép cả \n và \r\n đơn lẻ giữa các dòng.

Hàm parsINI(string) ( // Hãy bắt đầu với một đối tượng chứa các cài đặt cấp cao nhất var currentSection = (tên: null, trường: ); var loại = ; string.split(/\r?\n/).forEach(function(line) ( var match; if (/^\s*(;.*)?$/.test(line)) ( return; ) else if (match = line.match(/^\[(.*)\]$/)) ( currentSection = (name: match, Fields: ); Category.push(currentSection); ) else if (match = line.match( /^(\w+)=(.*)$/)) ( currentSection.fields.push((name: match, value: match)); ) else ( Throw new Error("Line "" + line + "" contains dữ liệu sai."); ) )); trả lại danh mục; )

Mã đi qua tất cả các dòng, cập nhật đối tượng phần hiện tại “phần hiện tại”. Đầu tiên, nó kiểm tra xem dòng này có thể bị bỏ qua hay không bằng cách sử dụng biểu thức chính quy /^\s*(;.*)?$/. Bạn có thể tưởng tượng nó hoạt động như thế nào không? Phần trong ngoặc khớp với chú thích phải không? làm cho ký tự thông thường cũng sẽ khớp với các dòng chỉ bao gồm khoảng trắng.

Nếu dòng này không phải là nhận xét, mã sẽ kiểm tra xem liệu nó có bắt đầu một phần mới hay không. Nếu có, nó sẽ tạo một đối tượng mới cho phần hiện tại và các cài đặt tiếp theo sẽ được thêm vào.

Khả năng có ý nghĩa cuối cùng là chuỗi đó cài đặt bình thường, trong trường hợp đó nó được thêm vào đối tượng hiện tại.

Nếu không có tùy chọn nào hoạt động, hàm sẽ báo lỗi.

Nhận thấy như thế nào sử dụng thường xuyên^ và $ hãy đảm bảo rằng biểu thức khớp với toàn bộ chuỗi chứ không chỉ một phần của chuỗi đó. Nếu bạn không sử dụng chúng, mã thường sẽ hoạt động, nhưng đôi khi nó sẽ không hoạt động. kết quả lạ, và lỗi như vậy sẽ khó tìm ra.

Cấu trúc if (match = string.match(...)) tương tự như thủ thuật sử dụng phép gán làm điều kiện trong vòng lặp while. Thường thì bạn không biết rằng lệnh gọi khớp sẽ thành công, vì vậy bạn chỉ có thể truy cập đối tượng kết quả bên trong khối if để kiểm tra nó. Để không phá vỡ chuỗi kiểm tra if đẹp mắt, chúng tôi gán kết quả tìm kiếm cho một biến và ngay lập tức sử dụng phép gán này làm kiểm tra.

Ký hiệu quốc tế

Do việc triển khai ngôn ngữ ban đầu đơn giản và việc sửa chữa sau đó của việc triển khai như vậy “bằng đá granite”, các biểu thức chính quy của JavaScript rất ngu ngốc với các ký tự không có trong ngôn ngữ tiếng Anh. Ví dụ: ký tự “chữ cái”, theo quan điểm của biểu thức chính quy JavaScript, có thể là một trong 26 chữ cái của bảng chữ cái tiếng Anh và vì lý do nào đó cũng là dấu gạch dưới. Các chữ cái như é hoặc β, rõ ràng là các chữ cái, không khớp với \w (và sẽ khớp với \W, là một chữ cái không phải chữ cái).

Trong một sự trùng hợp kỳ lạ, về mặt lịch sử \s (dấu cách) khớp với tất cả các ký tự được coi là khoảng trắng trong Unicode, bao gồm những thứ như không gian không phá vỡ hoặc dấu phân cách nguyên âm tiếng Mông Cổ.

Một số cách triển khai biểu thức chính quy trong các ngôn ngữ khác có cú pháp đặc biệt để tìm kiếm các danh mục ký tự Unicode đặc biệt, chẳng hạn như "tất cả chữ in hoa", "tất cả dấu câu" hoặc "ký tự điều khiển". Có kế hoạch thêm các danh mục như vậy vào JavaScript, nhưng chúng có thể sẽ không được triển khai sớm.

Điểm mấu chốt

Chính quy là các đối tượng đại diện cho các mẫu tìm kiếm trong chuỗi. Họ sử dụng cú pháp riêng của họ để thể hiện các mẫu này.

/abc/ Chuỗi ký tự
// Bất kỳ ký tự nào trong danh sách
/[^abc]/ Bất kỳ ký tự nào ngoại trừ các ký tự trong danh sách
// Bất kỳ ký tự nào trong khoảng
/x+/ Một hoặc nhiều lần xuất hiện của mẫu x
/x+?/ Một hoặc nhiều lần xuất hiện, không tham lam
/x*/ Không hoặc nhiều lần xuất hiện
/x?/ Không hoặc một lần xuất hiện
/x(2,4)/ Từ hai đến bốn lần xuất hiện
/(abc)/ Nhóm
/a|b|c/ Bất kỳ mẫu nào trong số nhiều mẫu
/\d/ Bất kỳ số nào
/\w/ Bất kỳ ký tự chữ và số (“chữ cái”)
/\s/ Bất kỳ ký tự khoảng trắng nào
/./ Bất kỳ ký tự nào ngoại trừ dòng mới
/\b/ Ranh giới từ
/^/ Bắt đầu dòng
/$/ Cuối dòng

Regex có một phương thức kiểm tra để kiểm tra xem mẫu có trong chuỗi hay không. Có một phương thức exec trả về một mảng chứa tất cả các nhóm được tìm thấy. Mảng có thuộc tính chỉ mục, chứa số ký tự mà từ đó xảy ra kết quả khớp.

Các chuỗi có một phương thức khớp để khớp các mẫu và một phương thức tìm kiếm chỉ trả về vị trí bắt đầu của lần xuất hiện. Phương thức thay thế có thể thay thế các lần xuất hiện của một mẫu bằng một chuỗi khác. Ngoài ra, bạn có thể gửi tới chức năng thay thế, sẽ xây dựng một dòng thay thế dựa trên mẫu và các nhóm được tìm thấy.

Các ký tự thông thường có cài đặt được viết sau dấu gạch chéo đóng. Tùy chọn i làm cho biểu thức chính quy không phân biệt chữ hoa chữ thường và tùy chọn g làm cho nó trở nên toàn cục, trong số những thứ khác, khiến phương thức thay thế thay thế tất cả các lần xuất hiện được tìm thấy, không chỉ lần xuất hiện đầu tiên.

Hàm tạo RegExp có thể được sử dụng để tạo biểu thức chính quy từ chuỗi.

Bộ điều chỉnh là một dụng cụ sắc bén với tay cầm không thoải mái. Chúng đơn giản hóa rất nhiều một số nhiệm vụ và có thể trở nên khó quản lý khi giải quyết các nhiệm vụ khác, nhiệm vụ phức tạp. Một phần của việc học cách sử dụng biểu thức chính quy là có thể chống lại sự cám dỗ nhồi nhét chúng vào một nhiệm vụ mà chúng không được dự định thực hiện.

Bài tập

Không thể tránh khỏi, khi giải quyết vấn đề, bạn sẽ gặp phải những trường hợp khó hiểu, đôi khi có thể tuyệt vọng khi nhìn thấy cách hành xử khó lường của một số biểu thức chính quy. Đôi khi, việc nghiên cứu hoạt động của một công cụ thông thường thông qua một dịch vụ trực tuyến như debuggex.com sẽ rất hữu ích, nơi bạn có thể xem hình ảnh trực quan của nó và so sánh nó với hiệu quả mong muốn.
chơi gôn thường xuyên
“Golf” trong mã là một trò chơi trong đó bạn cần thể hiện một chương trình nhất định với số lượng ký tự tối thiểu. Chơi gôn thông thường là một bài tập thực tế bằng cách viết các quy tắc nhỏ nhất có thể để tìm ra một mẫu nhất định và chỉ có vậy.

Đối với mỗi dòng con, hãy viết một biểu thức chính quy để kiểm tra vị trí của chúng trong dòng. Công cụ thông thường chỉ nên tìm những chuỗi con được chỉ định này. Đừng lo lắng về ranh giới từ trừ khi được đề cập cụ thể. Khi bạn có một mẫu đang hoạt động bình thường, hãy thử giảm bớt nó.

Xe và mèo
- bật và chống đỡ
- chồn, phà và ferrari
- Bất kỳ từ nào kết thúc bằng ious
- Dấu cách theo sau là dấu chấm, dấu phẩy, dấu hai chấm hoặc dấu chấm phẩy.
- Một từ dài hơn sáu chữ cái
- Từ không có chữ e

// Nhập biểu thức chính quy của bạn verify(/.../, ["my car", "bad cats"], ["camper", "high art"]); verify(/.../, ["văn hóa đại chúng", "đạo cụ điên rồ"], ["plop"]); verify(/.../, ["ferret", "ferry", "ferrari"], ["ferrum", "transfer A"]); verify(/.../, ["ngon quá", "phòng rộng rãi"], ["tàn tạ", "ý thức"]); verify(/.../, ["dấu câu sai ."], [" thoát khỏi dot"]); verify(/.../, ["hottentottententen"], ["no", "hotten totten tenten"]); verify(/.../, ["thú mỏ vịt đỏ", "tổ lung lay" ], ["earth bed", "learning ape"]); hàm verify(regexp, vâng, không) ( // Bỏ qua các bài tập chưa hoàn thành if (regexp.source == "...") return; Yes.forEach(function (s) ( if (!regexp.test(s)) console.log("Không tìm thấy "" + s + """); )); no.forEach(function(s) ( if (regexp.test(s )) console.log("Sự xuất hiện không mong đợi của "" + s + """); )); )

Trích dẫn trong văn bản
Giả sử bạn đã viết một câu chuyện và ở mọi nơi bạn sử dụng để chỉ ra các đoạn hội thoại dấu nháy đơn. Bây giờ bạn muốn thay thế dấu ngoặc kép bằng dấu ngoặc kép và để lại dấu ngoặc đơn dưới dạng viết tắt cho những từ như không.

Hãy nghĩ ra một mẫu phân biệt giữa hai cách sử dụng dấu ngoặc kép này và viết lệnh gọi đến phương thức thay thế thực hiện việc thay thế.

Số lần nữa
Dãy số có thể được tìm thấy bằng một biểu thức chính quy đơn giản /\d+/.

Viết biểu thức chỉ tìm các số được viết bằng Kiểu JavaScript. Nó phải hỗ trợ một dấu trừ hoặc dấu cộng có thể có trước số, dấu thập phân và ký hiệu khoa học 5e-3 hoặc 1E10 - một lần nữa có thể có dấu cộng hoặc dấu trừ. Cũng lưu ý rằng có thể không nhất thiết phải có số trước hoặc sau dấu chấm, nhưng số không thể chỉ có một dấu chấm. Tức là 0,5 hoặc 5. – số hợp lệ, nhưng bản thân một điểm thì không.

// Nhập chuỗi thông thường vào đây. số var = /^...$/; // Kiểm tra: ["1", "-1", "+15", "1.55", ".5", "5.", "1.3e2", "1E-4", "1e+12"] .forEach(function(s) ( if (!number.test(s)) console.log("Không tìm thấy "" + s + """); )); ["1a", "+-1", "1.2.3", "1+1", "1e4.5", ".5.", "1f5", "."].forEach(các) hàm ( if (number.test(s)) console.log("Được chấp nhận không chính xác "" + s + """); ));

Một số người khi gặp vấn đề sẽ nghĩ: “Ồ, tôi sẽ sử dụng các biểu thức thông thường”. Bây giờ họ có hai vấn đề.
Jamie Zawinski

Yuan-Ma nói, “Phải dùng rất nhiều lực để cắt gỗ ngang thớ gỗ. Phải mất rất nhiều mã để lập trình theo cấu trúc vấn đề.
Master Yuan-Ma, “Sách lập trình”

Các công cụ và kỹ thuật lập trình tồn tại và lan rộng một cách tiến hóa hỗn loạn. Đôi khi không phải những thứ đẹp đẽ và rực rỡ mới tồn tại được mà chỉ đơn giản là những thứ hoạt động đủ tốt trong lĩnh vực của họ - ví dụ: nếu chúng được tích hợp vào một công nghệ thành công khác.

Trong chương này, chúng ta sẽ thảo luận về một công cụ như vậy - biểu thức chính quy. Đây là một cách để mô tả các mẫu trong dữ liệu chuỗi. Họ tạo ra một ngôn ngữ nhỏ, độc lập được bao gồm trong JavaScript cũng như nhiều ngôn ngữ và công cụ khác.

Những lịch trình đều đặn vừa rất lạ vừa cực kỳ hữu ích. Cú pháp của họ rất khó hiểu và giao diện lập trình JavaScript của họ rất rắc rối. Nhưng nó là một công cụ mạnh mẽ để khám phá và thao tác với các chuỗi. Một khi bạn hiểu chúng, bạn sẽ trở thành một lập trình viên hiệu quả hơn.

Tạo biểu thức chính quy

Thông thường - loại đối tượng. Nó có thể được tạo bằng cách gọi hàm tạo RegExp hoặc bằng cách viết mẫu mong muốn, được bao quanh bởi dấu gạch chéo.

Var re1 = new RegExp("abc"); var re2 = /abc/;

Cả hai biểu thức chính quy này đều biểu thị cùng một mẫu: ký tự “a” theo sau là ký tự “b” theo sau là ký tự “c”.

Nếu bạn sử dụng hàm tạo RegExp thì mẫu sẽ được viết dưới dạng một chuỗi thông thường, do đó tất cả các quy tắc liên quan đến dấu gạch chéo ngược sẽ được áp dụng.

Mục nhập thứ hai, trong đó mẫu nằm giữa các dấu gạch chéo, xử lý các dấu gạch chéo ngược theo cách khác. Đầu tiên, vì mẫu kết thúc bằng dấu gạch chéo lên, chúng ta cần đặt dấu gạch chéo ngược trước dấu gạch chéo lên mà chúng ta muốn đưa vào mẫu của mình. Ngoài ra, các dấu gạch chéo ngược không phải là một phần của các ký tự đặc biệt như \n sẽ được giữ nguyên (thay vì bị bỏ qua như trong chuỗi) và sẽ thay đổi ý nghĩa của mẫu. Một số ký tự, chẳng hạn như dấu chấm hỏi hoặc dấu cộng, có ý nghĩa đặc biệt trong biểu thức chính quy và nếu bạn cần tìm ký tự như vậy thì trước nó cũng phải có dấu gạch chéo ngược.

Var 18Plus = /eightteen\+/;

Để biết những ký tự nào cần phải có dấu gạch chéo trước, bạn cần tìm hiểu danh sách tất cả các ký tự đặc biệt trong biểu thức chính quy. Điều này vẫn chưa thực hiện được, vì vậy khi nghi ngờ, chỉ cần đặt dấu gạch chéo ngược trước bất kỳ ký tự nào không phải là chữ cái, số hoặc dấu cách.

Kiểm tra các trận đấu

Chính quy có một số phương pháp. Đơn giản nhất là kiểm tra. Nếu bạn truyền cho nó một chuỗi, nó sẽ trả về giá trị Boolean cho biết liệu chuỗi đó có chứa sự xuất hiện của mẫu đã cho hay không.

Console.log(/abc/.test("abcde")); // → true console.log(/abc/.test("abxde")); // → sai

Một chuỗi thông thường chỉ bao gồm các ký tự không đặc biệt chỉ đơn giản là một chuỗi các ký tự này. Nếu abc ở bất kỳ vị trí nào trong dòng chúng ta đang kiểm tra (không chỉ ở đầu), kiểm tra sẽ trả về true.

Tìm kiếm một bộ ký tự

Bạn cũng có thể tìm hiểu xem một chuỗi có chứa abc hay không bằng cách sử dụng indexOf. Các mẫu thông thường cho phép bạn tiến xa hơn và tạo ra các mẫu phức tạp hơn.

Giả sử chúng ta cần tìm bất kỳ số nào. Khi chúng ta đặt một tập hợp các ký tự trong dấu ngoặc vuông trong biểu thức chính quy, điều đó có nghĩa là phần đó của biểu thức khớp với bất kỳ ký tự nào trong dấu ngoặc.

Cả hai biểu thức đều nằm trong dòng chứa một số.

Console.log(//.test("năm 1992")); // → true console.log(//.test("năm 1992")); // → đúng

Trong dấu ngoặc vuông, dấu gạch ngang giữa hai ký tự được sử dụng để chỉ định một phạm vi ký tự, trong đó trình tự được chỉ định bằng mã hóa Unicode. Các ký tự từ 0 đến 9 chỉ nằm trong một hàng (mã từ 48 đến 57), vì vậy nó nắm bắt tất cả và khớp với bất kỳ số nào.

Một số nhóm ký tự có chữ viết tắt tích hợp riêng.

\d Bất kỳ số nào
\w Ký tự chữ và số
\s Ký tự khoảng trắng (dấu cách, tab, dòng mới, v.v.)
\D không phải là số
\W không phải là ký tự chữ và số
\S không phải là ký tự khoảng trắng
. bất kỳ ký tự nào ngoại trừ nguồn cấp dữ liệu dòng

Do đó, bạn có thể đặt định dạng ngày và giờ như 30/01/2003 15:20 với biểu thức sau:

Thay đổi ngàyThời gian = /\d\d-\d\d-\d\d\d\d \d\d:\d\d/; console.log(dateTime.test("30-01-2003 15:20")); // → true console.log(dateTime.test("30-Jan-2003 15:20")); // → sai

Trông thật khủng khiếp phải không? Có quá nhiều dấu gạch chéo ngược khiến cho mẫu khó hiểu. Chúng tôi sẽ cải thiện nó một chút sau.

Dấu gạch chéo ngược cũng có thể được sử dụng trong dấu ngoặc vuông. Ví dụ: [\d.] có nghĩa là bất kỳ số hoặc dấu chấm nào. Lưu ý rằng dấu chấm bên trong dấu ngoặc vuông mất đi ý nghĩa đặc biệt của nó và trở thành một dấu chấm đơn giản. Điều tương tự cũng áp dụng cho các ký tự đặc biệt khác, chẳng hạn như +.

Bạn có thể đảo ngược một tập hợp các ký tự - nghĩa là bạn cần tìm bất kỳ ký tự nào ngoại trừ những ký tự có trong tập hợp đó - bằng cách đặt dấu ^ ngay sau dấu ngoặc vuông mở đầu.

Var notBinary = /[^01]/; console.log(notBinary.test("1100100010100110")); // → sai console.log(notBinary.test("1100100010200110")); // → đúng

Lặp lại các phần của mẫu

Chúng ta biết cách tìm một số. Điều gì sẽ xảy ra nếu chúng ta cần tìm số nguyên - một dãy gồm một hoặc nhiều chữ số?

Nếu bạn đặt dấu + sau thứ gì đó theo trình tự thông thường, điều này có nghĩa là phần tử này có thể được lặp lại nhiều lần. /\d+/ có nghĩa là một hoặc nhiều chữ số.

Console.log(/"\d+"/.test(""123"")); // → true console.log(/"\d+"/.test("""")); // → false console.log(/"\d*"/.test(""123"")); // → true console.log(/"\d*"/.test("""")); // → đúng

Dấu hoa thị * có ý nghĩa gần như giống nhau nhưng nó cho phép mẫu này không xuất hiện lần nào. Nếu thứ gì đó được theo sau bởi dấu hoa thị thì nó không bao giờ ngăn mẫu nằm trong dòng - nó chỉ xuất hiện ở đó 0 lần.

Dấu chấm hỏi làm cho một phần của mẫu trở thành tùy chọn, nghĩa là nó có thể xuất hiện 0 hoặc một lần. Trong ví dụ sau, ký tự u có thể xuất hiện nhưng mẫu này khớp ngay cả khi nó không xuất hiện.

Var hàng xóm = /neighbou?r/; console.log(neighbor.test("hàng xóm")); // → true console.log(neighbor.test("neighbor")); // → đúng

Dấu ngoặc nhọn được sử dụng để xác định chính xác số lần một mẫu phải xuất hiện. (4) sau một phần tử có nghĩa là nó phải xuất hiện 4 lần trong dòng. Bạn cũng có thể chỉ định một khoảng trống: (2,4) nghĩa là phần tử đó phải xuất hiện ít nhất 2 và không quá 4 lần.

Một phiên bản khác của định dạng ngày và giờ, trong đó cho phép ngày, tháng và giờ có một hoặc hai chữ số. Và nó cũng dễ đọc hơn một chút.

Var dateTime = /\d(1,2)-\d(1,2)-\d(4) \d(1,2):\d(2)/; console.log(dateTime.test("30-1-2003 8:45")); // → đúng

Bạn có thể sử dụng không gian mở bằng cách bỏ qua một trong các số. (,5,) có nghĩa là mẫu có thể xuất hiện từ 0 đến năm lần và (5,) có nghĩa là từ năm lần trở lên.

Nhóm các biểu thức con

Để sử dụng toán tử * hoặc + trên nhiều phần tử cùng một lúc, bạn có thể sử dụng dấu ngoặc đơn. Phần của biểu thức chính quy được đặt trong ngoặc được coi là một phần tử theo quan điểm của các toán tử.

Var phim hoạt hìnhCrying = /boo+(hoo+)+/i; console.log(cartoonCrying.test("Boohoooohoohoooo")); // → đúng

Điểm cộng thứ nhất và thứ hai chỉ áp dụng cho chữ o thứ hai trong boo và hoo. + thứ ba đề cập đến cả nhóm (hoo+), tìm một hoặc nhiều chuỗi như vậy.

Chữ cái i ở cuối biểu thức làm cho biểu thức chính quy không phân biệt chữ hoa chữ thường - sao cho B khớp với b.

Trận đấu và Nhóm

Phương pháp kiểm tra là phương pháp đơn giản nhất để kiểm tra các biểu thức chính quy. Nó chỉ cho bạn biết liệu kết quả có được tìm thấy hay không. Chính quy cũng có một phương thức exec, phương thức này sẽ trả về null nếu không tìm thấy gì và nếu không thì trả về một đối tượng có thông tin về trận đấu.

Var match = /\d+/.exec("một hai 100"); console.log(match); // → ["100"] console.log(match.index); // → 8

Đối tượng được trả về bởi exec có thuộc tính chỉ mục, chứa số ký tự mà từ đó xảy ra kết quả khớp. Nói chung, đối tượng trông giống như một mảng các chuỗi, trong đó phần tử đầu tiên là chuỗi đã được kiểm tra xem có khớp không. Trong ví dụ của chúng tôi, đây sẽ là dãy số chúng tôi đang tìm kiếm.

Chuỗi có một phương thức khớp hoạt động theo cách tương tự.

Console.log("một hai 100".match(/\d+/)); // → ["100"]

Khi một biểu thức chính quy chứa các biểu thức con được nhóm theo dấu ngoặc đơn, văn bản khớp với các nhóm này cũng sẽ xuất hiện trong mảng. Phần tử đầu tiên luôn khớp hoàn toàn. Phần thứ hai là phần khớp với nhóm đầu tiên (phần có dấu ngoặc đơn xuất hiện đầu tiên), sau đó là nhóm thứ hai, v.v.

Var quoteText = /"([^"]*)"/; console.log(quotedText.exec("she say "hello"")); // → [""hello"", "hello"]

Khi hoàn toàn không tìm thấy một nhóm nào (ví dụ: nếu theo sau nó là dấu chấm hỏi), vị trí của nhóm đó trong mảng là không xác định. Nếu một nhóm khớp nhiều lần thì chỉ có kết quả khớp cuối cùng nằm trong mảng.

Console.log(/bad(ly)?/.exec("bad")); // → ["xấu", không xác định] console.log(/(\d)+/.exec("123")); // → ["123", "3"]

Các nhóm rất hữu ích cho việc truy xuất các phần của chuỗi. Nếu chúng ta không chỉ muốn kiểm tra xem một chuỗi có ngày hay không mà còn trích xuất nó và tạo một đối tượng biểu thị ngày, thì chúng ta có thể đặt các chuỗi số trong dấu ngoặc đơn và chọn ngày từ kết quả của lệnh thực thi.

Nhưng trước tiên, hãy tìm hiểu sâu hơn một chút trong đó chúng ta sẽ tìm hiểu cách lưu trữ ngày và giờ ưa thích trong JavaScript.

Loại ngày

JavaScript có một loại đối tượng tiêu chuẩn cho ngày tháng—cụ thể hơn là các khoảnh khắc đúng lúc. Nó được gọi là Ngày. Nếu bạn chỉ đơn giản tạo một đối tượng ngày bằng cách sử dụng new, bạn sẽ nhận được ngày và giờ hiện tại.

Console.log(Ngày mới()); // → Chủ nhật ngày 09 tháng 11 năm 2014 00:07:57 GMT+0300 (CET)

Bạn cũng có thể tạo một đối tượng chứa thời gian nhất định

Console.log(Ngày mới(2015, 9, 21)); // → Thứ Tư ngày 21 tháng 10 năm 2015 00:00:00 GMT+0300 (CET) console.log(Ngày mới (2009, 11, 9, 12, 59, 59, 999)); // → Thứ Tư ngày 09 tháng 12 năm 2009 12:59:59 GMT+0300 (CET)

JavaScript sử dụng quy ước trong đó số tháng bắt đầu bằng số 0 và số ngày bắt đầu bằng số 1. Điều này thật ngu ngốc và lố bịch. Hãy cẩn thận.

Bốn đối số cuối cùng (giờ, phút, giây và mili giây) là tùy chọn và được đặt thành 0 nếu thiếu.

Dấu thời gian được lưu trữ dưới dạng số mili giây đã trôi qua kể từ đầu năm 1970. Đối với thời gian trước năm 1970, số âm được sử dụng (điều này là do quy ước thời gian Unix được tạo ra vào khoảng thời gian đó). Phương thức getTime của đối tượng date trả về số này. Tự nhiên nó lớn.
console.log(new Date(2013, 11, 19).getTime()); // → 1387407600000 console.log(Ngày mới(1387407600000)); // → Thứ năm ngày 19 tháng 12 năm 2013 00:00:00 GMT+0100 (CET)

Nếu bạn cung cấp cho hàm tạo Ngày một đối số, nó sẽ được coi là số mili giây này. Bạn có thể nhận giá trị mili giây hiện tại bằng cách tạo đối tượng Date và gọi phương thức getTime hoặc bằng cách gọi hàm Date.now.

Đối tượng Date có các phương thức getFullYear, getMonth, getDate, getHours, getMinutes và getSeconds để truy xuất các thành phần của nó. Ngoài ra còn có một phương thức getYear trả về mã có hai chữ số khá vô dụng như 93 hoặc 14.

Bằng cách đặt các phần có liên quan của mẫu trong dấu ngoặc đơn, chúng ta có thể tạo đối tượng ngày tháng trực tiếp từ chuỗi.

Hàm findDate(string) ( var dateTime = /(\d(1,2))-(\d(1,2))-(\d(4))/; var match = dateTime.exec(string); return new Date(Number(match), Number(match) - 1, Number(match)); ) console.log(findDate("30-1-2003")); // → Thứ năm ngày 30 tháng 1 năm 2003 00:00:00 GMT+0100 (CET)

Ranh giới từ và dòng

Thật không may, findDate sẽ vui vẻ trích xuất ngày vô nghĩa 00-1-3000 từ chuỗi "100-1-30000". Sự so khớp có thể xảy ra ở bất cứ đâu trong chuỗi, vì vậy trong trường hợp này nó sẽ chỉ bắt đầu ở ký tự thứ hai và kết thúc ở ký tự thứ hai đến ký tự cuối cùng.

Nếu chúng ta cần buộc trận đấu lấy toàn bộ chuỗi, chúng ta sử dụng thẻ ^ và $. ^ khớp với đầu dòng và $ khớp với cuối dòng. Do đó, /^\d+$/ khớp với một chuỗi chỉ chứa một hoặc nhiều chữ số, /^!/ khớp với một chuỗi bắt đầu bằng dấu chấm than và /x^/ không khớp với bất kỳ chuỗi nào (không thể có x).

Mặt khác, nếu chúng ta chỉ muốn đảm bảo rằng ngày bắt đầu và kết thúc trên một ranh giới từ, chúng ta sử dụng dấu \b. Ranh giới từ có thể là điểm bắt đầu hoặc kết thúc của một dòng hoặc bất kỳ vị trí nào trong một dòng có ký tự chữ và số \w ở một bên và ký tự không phải chữ và số ở bên kia.

Console.log(/cat/.test("concatenate")); // → true console.log(/\bcat\b/.test("concatenate")); // → sai

Lưu ý rằng nhãn ranh giới không phải là một biểu tượng. Nó chỉ đơn giản là một ràng buộc, có nghĩa là sự trùng khớp chỉ xảy ra nếu đáp ứng một điều kiện nhất định.

Mẫu có sự lựa chọn

Giả sử bạn cần tìm hiểu xem văn bản không chỉ chứa một số mà còn chứa một số theo sau là lợn, bò hoặc gà ở số ít hay số nhiều.

Có thể viết ba biểu thức chính quy và kiểm tra từng biểu thức một, nhưng có một cách tốt hơn. Biểu tượng | biểu thị sự lựa chọn giữa các mẫu ở bên trái và bên phải của nó. Và chúng ta có thể nói như sau:

Var AnimalCount = /\b\d+ (lợn|bò|gà)s?\b/; console.log(animalCount.test("15 con lợn")); // → true console.log(animalCount.test("15 con gà con")); // → sai

Dấu ngoặc đơn phân định phần của mẫu mà | được áp dụng và nhiều toán tử như vậy có thể được đặt lần lượt để biểu thị lựa chọn từ nhiều hơn hai tùy chọn.

Máy tìm kiếm

Biểu thức chính quy có thể được coi là sơ đồ. Sơ đồ sau đây mô tả một ví dụ về chăn nuôi gần đây.

Một biểu thức khớp với một chuỗi nếu có thể tìm thấy đường dẫn từ bên trái của sơ đồ sang bên phải. Chúng ta ghi nhớ vị trí hiện tại trong dòng và mỗi lần duyệt qua hình chữ nhật, chúng ta kiểm tra xem phần của dòng ngay sau vị trí của chúng ta trong đó có khớp với nội dung của hình chữ nhật hay không.

Điều này có nghĩa là việc kiểm tra sự trùng khớp của ký tự thông thường của chúng ta trong chuỗi “3 con lợn” khi xem qua sơ đồ sẽ như sau:

Ở vị trí 4 có ranh giới từ và chúng ta chuyển hình chữ nhật đầu tiên
- bắt đầu từ vị trí thứ 4 chúng ta tìm số và đi qua hình chữ nhật thứ hai
- ở vị trí 5, một đường dẫn đóng lại phía trước hình chữ nhật thứ hai và đường dẫn thứ hai đi xa hơn đến hình chữ nhật có khoảng trắng. Chúng tôi có một khoảng trắng, không phải một con số và chúng tôi chọn con đường thứ hai.
- bây giờ chúng ta đang ở vị trí 6, điểm bắt đầu của “lợn” và ở ngã ba của các con đường. Trong hàng không có “bò” hay “gà” mà có “lợn” nên chúng tôi chọn con đường này.
- tại vị trí 9 sau ngã ba, một đường đi qua “s” và đi đến hình chữ nhật ranh giới từ cuối cùng, và đường thứ hai đi qua “s”. Chúng ta có chữ “s” nên chúng ta tới đó.
- ở vị trí 10 ta ở cuối dòng, chỉ có ranh giới từ mới khớp được. Điểm cuối của dòng được coi là ranh giới và chúng ta đi qua hình chữ nhật cuối cùng. Và bây giờ chúng ta đã tìm thấy thành công mẫu của mình.

Về cơ bản, cách hoạt động của biểu thức chính quy là thuật toán bắt đầu ở đầu chuỗi và cố gắng tìm kết quả khớp ở đó. Trong trường hợp của chúng ta, có một ranh giới từ nên nó vượt qua hình chữ nhật đầu tiên - nhưng không có số ở đó nên nó vấp phải hình chữ nhật thứ hai. Sau đó, nó di chuyển đến ký tự thứ hai trong chuỗi và cố gắng tìm kết quả khớp ở đó... Và cứ tiếp tục như vậy cho đến khi nó tìm thấy kết quả khớp hoặc đến cuối chuỗi, trong trường hợp đó không tìm thấy kết quả khớp nào.

Hối lộ

Biểu thức chính quy /\b(+b|\d+|[\da-f]h)\b/ khớp với số nhị phân theo sau là b, số thập phân không có hậu tố hoặc số thập lục phân (các số từ 0 đến 9 hoặc các ký hiệu từ a đến h), theo sau là h. Sơ đồ liên quan:

Khi tìm kiếm kết quả trùng khớp, có thể xảy ra trường hợp thuật toán lấy đường dẫn trên cùng (số nhị phân), ngay cả khi không có số đó trong chuỗi. Ví dụ: nếu có dòng “103”, thì rõ ràng là chỉ sau khi đạt đến số 3, thuật toán mới hiểu rằng nó đang đi sai đường. Nói chung, dòng này khớp với trình tự thông thường, chỉ là không có trong chuỗi này.

Sau đó, thuật toán quay trở lại. Tại một ngã ba, nó ghi nhớ vị trí hiện tại (trong trường hợp của chúng tôi, đây là điểm bắt đầu của dòng, ngay sau ranh giới từ) để bạn có thể quay lại và thử một đường dẫn khác nếu đường đã chọn không hoạt động. Đối với chuỗi “103”, sau khi gặp số 3, nó sẽ quay trở lại và cố gắng đi theo đường thập phân. Điều này sẽ hoạt động để một trận đấu sẽ được tìm thấy.

Thuật toán dừng ngay khi tìm thấy kết quả khớp hoàn chỉnh. Điều này có nghĩa là ngay cả khi một số tùy chọn có thể phù hợp thì chỉ một trong số chúng được sử dụng (theo thứ tự chúng xuất hiện theo trình tự thông thường).

Quay lui xảy ra khi sử dụng các toán tử lặp lại như + và *. Nếu bạn tìm kiếm /^.*x/ trong chuỗi "abcxe", phần biểu thức chính quy.* sẽ cố gắng tiêu thụ toàn bộ chuỗi. Thuật toán sau đó sẽ nhận ra rằng nó cũng cần “x”. Vì không có chữ “x” ở cuối chuỗi nên thuật toán sẽ cố gắng tìm kiếm sự trùng khớp bằng cách di chuyển lùi lại một ký tự. Sau abcx cũng không có x, sau đó nó lại quay trở lại, lần này về chuỗi con abc. Và sau dòng, nó tìm x và báo kết quả khớp thành công, ở vị trí từ 0 đến 4.

Bạn có thể viết một quy trình thông thường sẽ dẫn đến nhiều lần khôi phục. Sự cố này xảy ra khi mẫu có thể khớp với đầu vào theo nhiều cách khác nhau. Ví dụ: nếu chúng ta mắc lỗi khi viết biểu thức chính quy cho số nhị phân, chúng ta có thể vô tình viết một cái gì đó như /(+)+b/.

Nếu thuật toán tìm kiếm một mẫu như vậy trong một chuỗi dài gồm các số 0 và 1 không có chữ "b" ở cuối, thì trước tiên nó sẽ đi qua vòng lặp bên trong cho đến khi hết chữ số. Sau đó, anh ta sẽ nhận thấy rằng không có chữ “b” ở cuối, anh ta sẽ lùi lại một vị trí, đi qua vòng ngoài, bỏ cuộc lần nữa, cố gắng quay trở lại vị trí khác dọc theo vòng trong… Và anh ta sẽ tiếp tục. để tìm kiếm theo cách này, sử dụng cả hai vòng lặp. Tức là khối lượng công việc với mỗi ký tự của dòng sẽ tăng gấp đôi. Ngay cả đối với vài chục nhân vật, việc tìm kiếm sự trùng khớp sẽ mất rất nhiều thời gian.

phương pháp thay thế

Chuỗi có một phương thức thay thế có thể thay thế một phần của chuỗi bằng một chuỗi khác.

Console.log("dad".replace("p", "m")); // → bản đồ

Đối số đầu tiên cũng có thể là một biểu thức chính quy, trong trường hợp đó, lần xuất hiện đầu tiên của biểu thức chính quy trong dòng sẽ được thay thế. Khi tùy chọn “g” (toàn cục) được thêm vào biểu thức chính quy, tất cả các lần xuất hiện đều được thay thế, không chỉ lần đầu tiên

Console.log("Borobudur".replace(//, "a")); // → Barobudur console.log("Borobudur".replace(//g, "a")); // → Barabadar

Sẽ hợp lý hơn nếu chuyển tùy chọn "thay thế tất cả" thông qua một đối số riêng hoặc thông qua một phương thức riêng như thay thế. Nhưng thật không may, tùy chọn này được truyền qua chính hệ thống thông thường.

Toàn bộ sức mạnh của biểu thức chính quy được bộc lộ khi chúng ta sử dụng liên kết đến các nhóm được tìm thấy trong một chuỗi, được chỉ định trong biểu thức chính quy. Ví dụ: chúng tôi có một dòng chứa tên người, mỗi tên một dòng, ở định dạng "Họ, Tên". Nếu chúng ta cần hoán đổi chúng và xóa dấu phẩy để lấy “Tên họ”, chúng ta viết như sau:

Console.log("Hopper, Grace\nMcCarthy, John\nRitchie, Dennis" .replace(/([\w ]+), ([\w ]+)/g, "$2 $1")); // → Grace Hopper // John McCarthy // Dennis Ritchie

$1 và $2 trong dòng thay thế đề cập đến các nhóm ký tự được đặt trong dấu ngoặc đơn. $1 được thay thế bằng văn bản khớp với nhóm đầu tiên, $2 với nhóm thứ hai, v.v., tối đa $9. Toàn bộ kết quả khớp được chứa trong biến $&.

Bạn cũng có thể truyền một hàm làm đối số thứ hai. Đối với mỗi lần thay thế, một hàm sẽ được gọi mà đối số của nó sẽ là các nhóm được tìm thấy (và toàn bộ phần khớp của dòng) và kết quả của nó sẽ được chèn vào một dòng mới.

Ví dụ đơn giản:

Var s = "cia và fbi"; console.log(s.replace(/\b(fbi|cia)\b/g, function(str) ( return str.toUpperCase(); ))); // → CIA và FBI

Đây là một điều thú vị hơn:

Var stock = "1 quả chanh, 2 củ cải và 101 quả trứng"; hàm trừOne(match, money, unit) ( money = Number(amount) - 1; if (amount == 1) // chỉ còn lại một, xóa "s" ở cuối unit = unit.slice(0, unit. length - 1); else if (amount == 0) money = "no"; trả về số tiền + " " + unit; ) console.log(stock.replace(/(\d+) (\w+)/g,trừOne) ); // → không chanh, 1 bắp cải và 100 quả trứng

Mã này lấy một chuỗi, tìm tất cả các lần xuất hiện của các số theo sau là một từ và trả về một chuỗi với mỗi số giảm đi một.

Nhóm (\d+) đi vào đối số số lượng và (\w+) đi vào đối số đơn vị. Hàm chuyển đổi số tiền thành số - và điều này luôn hoạt động vì mẫu của chúng tôi là \d+. Và sau đó thực hiện thay đổi từ, trong trường hợp chỉ còn 1 mục.

Tham lam

Thật dễ dàng để sử dụng thay thế để viết một hàm loại bỏ tất cả các nhận xét khỏi mã JavaScript. Đây là lần thử đầu tiên:

Hàm StripComments(code) ( return code.replace(/\/\/.*|\/\*[^]*\*\//g, ""); ) console.log(stripComments("1 + /* 2 */3")); // → 1 + 3 console.log(stripComments("x = 10;// ten!")); // → x = 10; console.log(stripComments("1 /* a */+/* b */ 1")); // → 1 1

Phần trước toán tử "hoặc" khớp với hai dấu gạch chéo theo sau là bất kỳ số lượng ký tự nào ngoại trừ dòng mới. Phần loại bỏ các bình luận nhiều dòng phức tạp hơn. Chúng tôi sử dụng [^], tức là ký tự nào không trống là cách tìm ký tự bất kỳ. Chúng tôi không thể sử dụng dấu chấm vì khối nhận xét tiếp tục trên một dòng mới và ký tự dòng mới không khớp với dấu chấm.

Nhưng kết quả của ví dụ trước không chính xác. Tại sao?

Phần [^]* trước tiên sẽ cố gắng chụp càng nhiều ký tự càng tốt. Nếu vì điều này mà phần tiếp theo của chuỗi thông thường không tìm thấy kết quả khớp, nó sẽ quay lại một ký tự và thử lại. Trong ví dụ này, thuật toán cố gắng lấy toàn bộ dòng rồi quay lại. Sau khi quay lại 4 ký tự, anh ta sẽ tìm thấy */ trong dòng - và đây không phải là điều chúng tôi mong muốn. Chúng tôi chỉ muốn lấy một bình luận chứ không phải đi đến cuối dòng và tìm bình luận cuối cùng.

Do đó, chúng tôi nói rằng các toán tử lặp lại (+, *, ?, và ()) rất tham lam, nghĩa là trước tiên chúng lấy càng nhiều càng tốt rồi quay lại. Nếu bạn đặt câu hỏi sau một toán tử như thế này (+?, *?, ??, ()?), họ sẽ trở nên không tham lam và bắt đầu tìm những lần xuất hiện nhỏ nhất có thể.

Và đó là những gì chúng ta cần. Bằng cách buộc dấu hoa thị tìm các kết quả trùng khớp với số lượng ký tự tối thiểu có thể có trong một dòng, chúng tôi chỉ sử dụng một khối nhận xét và không còn nữa.

Hàm StripComments(code) ( return code.replace(/\/\/.*|\/\*[^]*?\*\//g, ""); ) console.log(stripComments("1 /* a */+/* b */ 1")); // → 1 + 1

Nhiều lỗi xảy ra khi sử dụng toán tử tham lam thay vì toán tử không tham lam. Khi sử dụng toán tử lặp lại, trước tiên hãy luôn xem xét toán tử không tham lam.

Tự động tạo các đối tượng RegExp

Trong một số trường hợp, mẫu chính xác không được xác định tại thời điểm viết mã. Ví dụ: bạn sẽ cần tìm tên người dùng trong văn bản và đặt nó trong dấu gạch dưới. Vì bạn sẽ chỉ biết tên sau khi chạy chương trình nên bạn không thể sử dụng ký hiệu gạch chéo.

Nhưng bạn có thể xây dựng chuỗi và sử dụng hàm tạo RegExp. Đây là một ví dụ:

Tên Var = "Harry"; var text = "Và Harry có một vết sẹo trên trán."; var regrec = new RegExp("\\b(" + name + ")\\b", "gi"); console.log(text.replace(regexp, "_$1_")); // → Và _Harry_ có một vết sẹo trên trán.

Khi tạo ranh giới từ, chúng ta phải sử dụng dấu gạch chéo kép vì chúng ta viết chúng theo dòng thông thường chứ không phải theo trình tự đều đặn với dấu gạch chéo lên. Đối số thứ hai của RegExp chứa các tùy chọn cho biểu thức chính quy - trong trường hợp của chúng tôi là “gi”, tức là. toàn cầu và không phân biệt chữ hoa chữ thường.

Nhưng nếu tên đó là “dea+hlrd” (nếu người dùng của chúng tôi là kulhatzker) thì sao? Kết quả là chúng ta sẽ nhận được một biểu thức chính quy vô nghĩa và không tìm thấy kết quả khớp trong chuỗi.

Chúng ta có thể thêm dấu gạch chéo ngược trước bất kỳ ký tự nào mà chúng ta không thích. Chúng tôi không thể thêm dấu gạch chéo ngược trước các chữ cái vì \b hoặc \n là các ký tự đặc biệt. Nhưng bạn có thể thêm dấu gạch chéo trước bất kỳ ký tự không phải chữ và số nào mà không gặp vấn đề gì.

Tên Var = "dea+hlrd"; var text = "Cái dea+hlrd này làm mọi người khó chịu."; var escape = name.replace(/[^\w\s]/g, "\\$&"); var regrec = new RegExp("\\b(" + escape + ")\\b", "gi"); console.log(text.replace(regexp, "_$1_")); // → Điều này _dea+hlrd_ làm mọi người khó chịu.

phương pháp tìm kiếm

Phương thức indexOf không thể được sử dụng với các biểu thức thông thường. Nhưng có một phương pháp tìm kiếm chỉ mong đợi biểu thức chính quy. Giống như indexOf, nó trả về chỉ mục của lần xuất hiện đầu tiên hoặc -1 nếu không xảy ra.

Console.log("word".search(/\S/)); // → 2 console.log(" ".search(/\S/)); // → -1

Thật không may, không có cách nào để yêu cầu phương thức tìm kiếm kết quả khớp bắt đầu từ một giá trị chênh lệch cụ thể (như bạn có thể làm với indexOf). Điều đó sẽ hữu ích.

thuộc tính Last Index

Phương thức exec cũng không cung cấp một cách thuận tiện để bắt đầu tìm kiếm từ một vị trí nhất định trong chuỗi. Nhưng nó đưa ra một cách bất tiện.

Một đối tượng biểu thức chính quy có các thuộc tính. Một trong số đó là nguồn, chứa một chuỗi. Một cái khác là LastIndex, nó kiểm soát, trong một số điều kiện, nơi việc tìm kiếm lần xuất hiện tiếp theo sẽ bắt đầu.

Các điều kiện này bao gồm tùy chọn chung g phải có mặt và việc tìm kiếm phải được thực hiện bằng phương thức exec. Một giải pháp hợp lý hơn là chỉ cần cho phép chuyển một đối số bổ sung cho người thực thi, nhưng tính hợp lý không phải là tính năng cơ bản của giao diện biểu thức chính quy JavaScript.

Mẫu Var = /y/g; mẫu.lastIndex = 3; var match = model.exec("xyzzy"); console.log(match.index); // → 4 console.log(pattern.lastIndex); // → 5

Nếu tìm kiếm thành công, lệnh gọi exec sẽ cập nhật thuộc tính LastIndex để trỏ đến vị trí sau lần xuất hiện được tìm thấy. Nếu không thành công, LastIndex được đặt thành 0 - giống như LastIndex của đối tượng mới được tạo.

Khi sử dụng một biến thông thường toàn cục và nhiều lệnh gọi exec, những cập nhật LastIndex tự động này có thể gây ra sự cố. Máy chủ thông thường của bạn có thể bắt đầu tìm kiếm từ vị trí còn lại từ cuộc gọi trước đó.

Chữ số Var = /\d/g; console.log(digit.exec("đây là: 1")); // → ["1"] console.log(digit.exec("and now: 1")); // → vô giá trị

Một hiệu ứng thú vị khác của tùy chọn g là nó thay đổi cách hoạt động của phương thức so khớp. Khi được gọi bằng tùy chọn này, thay vì trả về một mảng tương tự như kết quả của lệnh exec, nó sẽ tìm tất cả các lần xuất hiện của mẫu trong chuỗi và trả về một mảng gồm các chuỗi con được tìm thấy.

Console.log("Banana".match(/an/g)); // → ["an", "an"]

Vì vậy, hãy cẩn thận với các biến thông thường toàn cầu. Các trường hợp cần thiết - thay thế các cuộc gọi hoặc địa điểm mà bạn sử dụng cụ thể LastIndex - có lẽ là tất cả các trường hợp chúng nên được sử dụng.

Chu kỳ xảy ra

Một nhiệm vụ điển hình là lặp qua tất cả các lần xuất hiện của một mẫu trong chuỗi để nó có thể truy cập đối tượng khớp trong phần thân vòng lặp bằng cách sử dụng LastIndex và exec.

Var input = "Một dòng có 3 số trong đó... 42 và 88."; số var = /\b(\d+)\b/g; trận đấu var; while (match = number.exec(input)) console.log("Đã tìm thấy ", match," on ", match.index); // → Tìm thấy 3 x 14 // Tìm thấy 42 x 33 // Tìm thấy 88 x 40

Nó lợi dụng thực tế là giá trị của phép gán là giá trị được gán. Bằng cách sử dụng match = re.exec(input) làm điều kiện trong vòng lặp while, chúng ta tìm kiếm ở đầu mỗi lần lặp, lưu kết quả vào một biến và kết thúc vòng lặp khi tìm thấy tất cả kết quả khớp.

Phân tích tệp INI

Để kết thúc chương này, chúng ta hãy xem xét một vấn đề sử dụng biểu thức chính quy. Hãy tưởng tượng rằng chúng ta đang viết một chương trình tự động thu thập thông tin về kẻ thù của chúng ta qua Internet. (Chúng tôi sẽ không viết toàn bộ chương trình, chỉ phần đọc tệp cài đặt. Xin lỗi.) Tệp trông như thế này:

Searchengine=http://www.google.com/search?q=$1 cay đắng=9.7 ; dấu chấm phẩy được đặt trước các bình luận; mỗi phần đề cập đến một kẻ thù khác nhau fullname=Larry Doe type=kindergarten bull website=http://www.geocities.com/CapeCanaveral/11451 fullname=Gargamel type=ác sĩ đầu radir=/home/marijn/enemies/gargamel

Định dạng tệp chính xác (được sử dụng khá rộng rãi và thường được gọi là INI) như sau:

Các dòng trống và các dòng bắt đầu bằng dấu chấm phẩy sẽ bị bỏ qua
- các dòng trong ngoặc vuông bắt đầu một phần mới
- các dòng chứa mã định danh chữ và số theo sau là = thêm cài đặt trong phần này

Mọi thứ khác đều là dữ liệu không chính xác.

Nhiệm vụ của chúng ta là chuyển đổi một chuỗi như vậy thành một mảng các đối tượng, mỗi đối tượng có một thuộc tính tên và một mảng cài đặt. Một đối tượng là cần thiết cho mỗi phần và một đối tượng khác là cần thiết cho cài đặt chung ở đầu tệp.

Vì tệp cần được phân tích cú pháp từng dòng nên bạn nên bắt đầu bằng cách chia tệp thành các dòng. Để làm điều này, chúng ta đã sử dụng string.split("\n") trong Chương 6. Một số hệ điều hành không sử dụng một ký tự \n để ngắt dòng mà sử dụng hai - \r\n. Vì phương thức phân tách lấy các biểu thức chính quy làm đối số nên chúng ta có thể phân chia các dòng bằng cách sử dụng biểu thức /\r?\n/, cho phép cả \n và \r\n đơn lẻ giữa các dòng.

Hàm parsINI(string) ( // Hãy bắt đầu với một đối tượng chứa các cài đặt cấp cao nhất var currentSection = (name: null, Fields: ); var Category = ; string.split(/\r?\n/).forEach(function (line ) ( var match; if (/^\s*(;.*)?$/.test(line)) ( return; ) else if (match = line.match(/^\[(.*)\ ]$ /)) ( currentSection = (name: match, field: ); Category.push(currentSection); ) else if (match = line.match(/^(\w+)=(.*)$/)) ( currentSection.fields.push((name: match, value: match)); ) else ( ném new Error("Dòng "" + line + "" chứa dữ liệu không hợp lệ."); ) )); trả về danh mục; )

Mã đi qua tất cả các dòng, cập nhật đối tượng phần hiện tại “phần hiện tại”. Đầu tiên, nó kiểm tra xem dòng này có thể bị bỏ qua hay không bằng cách sử dụng biểu thức chính quy /^\s*(;.*)?$/. Bạn có thể tưởng tượng nó hoạt động như thế nào không? Phần trong ngoặc khớp với chú thích phải không? làm cho ký tự thông thường cũng sẽ khớp với các dòng chỉ bao gồm khoảng trắng.

Nếu dòng này không phải là nhận xét, mã sẽ kiểm tra xem liệu nó có bắt đầu một phần mới hay không. Nếu có, nó sẽ tạo một đối tượng mới cho phần hiện tại và các cài đặt tiếp theo sẽ được thêm vào.

Khả năng có ý nghĩa cuối cùng là chuỗi đó là một cài đặt bình thường, trong trường hợp đó nó được thêm vào đối tượng hiện tại.

Nếu không có tùy chọn nào hoạt động, hàm sẽ báo lỗi.

Lưu ý cách sử dụng thường xuyên ^ và $ đảm bảo rằng biểu thức khớp với toàn bộ chuỗi thay vì chỉ một phần của chuỗi đó. Nếu bạn không sử dụng chúng, mã nhìn chung sẽ hoạt động nhưng đôi khi sẽ tạo ra các kết quả lạ và sẽ khó tìm ra lỗi.

Cấu trúc if (match = string.match(...)) tương tự như thủ thuật sử dụng phép gán làm điều kiện trong vòng lặp while. Thường thì bạn không biết rằng lệnh gọi khớp sẽ thành công, vì vậy bạn chỉ có thể truy cập đối tượng kết quả bên trong khối if để kiểm tra nó. Để không phá vỡ chuỗi kiểm tra if đẹp mắt, chúng tôi gán kết quả tìm kiếm cho một biến và ngay lập tức sử dụng phép gán này làm kiểm tra.

Ký hiệu quốc tế

Do việc triển khai ngôn ngữ ban đầu đơn giản và việc sửa chữa sau đó của việc triển khai như vậy “bằng đá granite”, các biểu thức chính quy của JavaScript rất ngu ngốc với các ký tự không có trong ngôn ngữ tiếng Anh. Ví dụ: ký tự “chữ cái”, theo quan điểm của biểu thức chính quy JavaScript, có thể là một trong 26 chữ cái của bảng chữ cái tiếng Anh và vì lý do nào đó cũng là dấu gạch dưới. Các chữ cái như é hoặc β, rõ ràng là các chữ cái, không khớp với \w (và sẽ khớp với \W, là một chữ cái không phải chữ cái).

Trong một sự thay đổi kỳ lạ, về mặt lịch sử \s (dấu cách) khớp với tất cả các ký tự được coi là khoảng trắng trong Unicode, bao gồm những thứ như dấu cách không ngắt hoặc dấu phân tách nguyên âm tiếng Mông Cổ.

Một số cách triển khai biểu thức chính quy bằng các ngôn ngữ khác có cú pháp đặc biệt để tìm kiếm các danh mục ký tự Unicode đặc biệt, chẳng hạn như "tất cả chữ hoa", "tất cả dấu câu" hoặc "ký tự điều khiển". Có kế hoạch thêm các danh mục như vậy vào JavaScript, nhưng chúng có thể sẽ không được triển khai sớm.

Điểm mấu chốt

Chính quy là các đối tượng đại diện cho các mẫu tìm kiếm trong chuỗi. Họ sử dụng cú pháp riêng của họ để thể hiện các mẫu này.

/abc/ Chuỗi ký tự
// Bất kỳ ký tự nào trong danh sách
/[^abc]/ Bất kỳ ký tự nào ngoại trừ các ký tự trong danh sách
// Bất kỳ ký tự nào trong khoảng
/x+/ Một hoặc nhiều lần xuất hiện của mẫu x
/x+?/ Một hoặc nhiều lần xuất hiện, không tham lam
/x*/ Không hoặc nhiều lần xuất hiện
/x?/ Không hoặc một lần xuất hiện
/x(2,4)/ Từ hai đến bốn lần xuất hiện
/(abc)/ Nhóm
/a|b|c/ Bất kỳ mẫu nào trong số nhiều mẫu
/\d/ Bất kỳ số nào
/\w/ Bất kỳ ký tự chữ và số (“chữ cái”)
/\s/ Bất kỳ ký tự khoảng trắng nào
/./ Bất kỳ ký tự nào ngoại trừ dòng mới
/\b/ Ranh giới từ
/^/ Bắt đầu dòng
/$/ Cuối dòng

Regex có một phương thức kiểm tra để kiểm tra xem mẫu có trong chuỗi hay không. Có một phương thức exec trả về một mảng chứa tất cả các nhóm được tìm thấy. Mảng có thuộc tính chỉ mục, chứa số ký tự mà từ đó xảy ra kết quả khớp.

Các chuỗi có một phương thức khớp để khớp các mẫu và một phương thức tìm kiếm chỉ trả về vị trí bắt đầu của lần xuất hiện. Phương thức thay thế có thể thay thế các lần xuất hiện của một mẫu bằng một chuỗi khác. Ngoài ra, bạn có thể chuyển một hàm tới thay thế để xây dựng một dòng thay thế dựa trên mẫu và các nhóm được tìm thấy.

Các ký tự thông thường có cài đặt được viết sau dấu gạch chéo đóng. Tùy chọn i làm cho biểu thức chính quy không phân biệt chữ hoa chữ thường và tùy chọn g làm cho nó trở nên toàn cục, trong số những thứ khác, khiến phương thức thay thế thay thế tất cả các lần xuất hiện được tìm thấy, không chỉ lần xuất hiện đầu tiên.

Hàm tạo RegExp có thể được sử dụng để tạo biểu thức chính quy từ chuỗi.

Bộ điều chỉnh là một dụng cụ sắc bén với tay cầm không thoải mái. Chúng đơn giản hóa đáng kể một số nhiệm vụ và có thể trở nên khó quản lý khi giải quyết các vấn đề phức tạp khác. Một phần của việc học cách sử dụng biểu thức chính quy là có thể chống lại sự cám dỗ nhồi nhét chúng vào một nhiệm vụ mà chúng không được dự định thực hiện.

Bài tập

Không thể tránh khỏi, khi giải quyết vấn đề, bạn sẽ gặp phải những trường hợp khó hiểu, đôi khi có thể tuyệt vọng khi nhìn thấy cách hành xử khó lường của một số biểu thức chính quy. Đôi khi, việc nghiên cứu hoạt động của một công cụ thông thường thông qua một dịch vụ trực tuyến như debuggex.com sẽ rất hữu ích, nơi bạn có thể xem hình ảnh trực quan của nó và so sánh nó với hiệu quả mong muốn.
chơi gôn thường xuyên
“Golf” trong mã là một trò chơi trong đó bạn cần thể hiện một chương trình nhất định với số lượng ký tự tối thiểu. Chơi gôn thông thường là một bài tập thực tế bằng cách viết các quy tắc nhỏ nhất có thể để tìm ra một mẫu nhất định và chỉ có vậy.

Đối với mỗi dòng con, hãy viết một biểu thức chính quy để kiểm tra vị trí của chúng trong dòng. Công cụ thông thường chỉ nên tìm những chuỗi con được chỉ định này. Đừng lo lắng về ranh giới từ trừ khi được đề cập cụ thể. Khi bạn có một mẫu đang hoạt động bình thường, hãy thử giảm bớt nó.

Xe và mèo
- bật và chống đỡ
- chồn, phà và ferrari
- Bất kỳ từ nào kết thúc bằng ious
- Dấu cách theo sau là dấu chấm, dấu phẩy, dấu hai chấm hoặc dấu chấm phẩy.
- Một từ dài hơn sáu chữ cái
- Từ không có chữ e

// Nhập biểu thức chính quy của bạn verify(/.../, ["my car", "bad cats"], ["camper", "high art"]); verify(/.../, ["văn hóa đại chúng", "đạo cụ điên rồ"], ["plop"]); verify(/.../, ["ferret", "ferry", "ferrari"], ["ferrum", "transfer A"]); verify(/.../, ["ngon quá", "phòng rộng rãi"], ["tàn tạ", "ý thức"]); verify(/.../, ["dấu câu sai ."], ["thoát dấu chấm"]); verify(/.../, ["hottenottententen"], ["no", "hotten totten tenten"]); verify(/.../, ["thú mỏ vịt đỏ", "tổ lung lay"], ["giường đất", "vượn học"]); function verify(regexp,yes, no) ( // Bỏ qua các bài tập chưa hoàn thành if (regexp.source == "...") return; Yes.forEach(function(s) ( if (!regexp.test(s)) console .log("Không tìm thấy "" + s + """); )); no.forEach(function(s) ( if (regexp.test(s)) console.log("Sự xuất hiện không mong đợi của "" + s + """); )); )

Trích dẫn trong văn bản
Giả sử bạn đã viết một câu chuyện và sử dụng các trích dẫn đơn xuyên suốt để biểu thị cuộc đối thoại. Bây giờ bạn muốn thay thế dấu ngoặc kép bằng dấu ngoặc kép và để lại dấu ngoặc đơn dưới dạng viết tắt cho những từ như không.

Hãy nghĩ ra một mẫu phân biệt giữa hai cách sử dụng dấu ngoặc kép này và viết lệnh gọi đến phương thức thay thế thực hiện việc thay thế.

Số lần nữa
Dãy số có thể được tìm thấy bằng một biểu thức chính quy đơn giản /\d+/.

Viết biểu thức chỉ tìm các số được viết theo kiểu JavaScript. Nó phải hỗ trợ một dấu trừ hoặc dấu cộng có thể có trước số, dấu thập phân và ký hiệu khoa học 5e-3 hoặc 1E10 - một lần nữa có thể có dấu cộng hoặc dấu trừ. Cũng lưu ý rằng có thể không nhất thiết phải có số trước hoặc sau dấu chấm, nhưng số không thể chỉ có một dấu chấm. Nghĩa là, 0,5 hoặc 5. là những số hợp lệ, nhưng bản thân một dấu chấm thì không.

// Nhập chuỗi thông thường vào đây. số var = /^...$/; // Kiểm tra: ["1", "-1", "+15", "1.55", ".5", "5.", "1.3e2", "1E-4", "1e+12"] .forEach(function(s) ( if (!number.test(s)) console.log("Không tìm thấy "" + s + """); )); ["1a", "+-1", "1.2.3", "1+1", "1e4.5", ".5.", "1f5", "."].forEach(các) hàm ( if (number.test(s)) console.log("Được chấp nhận không chính xác "" + s + """); ));