Lời khuyên cho Regex Golf


43

Tương tự như chủ đề của chúng tôi về các mẹo chơi gôn dành riêng cho ngôn ngữ: các thủ thuật chung để rút ngắn các biểu thức chính quy là gì?

Tôi có thể thấy ba cách sử dụng regex khi chơi golf: golf regex cổ điển ("đây là danh sách nên khớp và đây là danh sách nên thất bại"), sử dụng regex để giải các bài toán tính toán và biểu thức chính quy được sử dụng như một phần của mã golf lớn hơn. Hãy gửi lời khuyên giải quyết bất kỳ hoặc tất cả những điều này. Nếu mẹo của bạn bị giới hạn ở một hoặc nhiều hương vị, vui lòng nêu rõ các hương vị này ở đầu.

Như thường lệ, vui lòng giữ một mẹo (hoặc gia đình các mẹo có liên quan rất chặt chẽ) cho mỗi câu trả lời, để các mẹo hữu ích nhất có thể tăng lên hàng đầu thông qua bỏ phiếu.


Tự quảng cáo trắng trợn: loại này sử dụng regex nào rơi vào? codegolf.stackexchange.com/a/37685/8048
Kyle Strand

@KyleStrand "các biểu thức chính quy được sử dụng như một phần của mã golf lớn hơn."
Martin Ender

Câu trả lời:


24

Khi nào không trốn thoát

Các quy tắc này áp dụng cho hầu hết các hương vị, nếu không phải tất cả:

  • ] không cần thoát khi chưa từng có.

  • {}không cần phải trốn thoát khi chúng không phải là một phần của sự lặp lại, ví dụ như {a}khớp theo {a}nghĩa đen. Ngay cả khi bạn muốn khớp một cái gì đó như {2}, bạn chỉ cần thoát khỏi một trong số họ, ví dụ {2\}.

Trong các lớp nhân vật:

  • ]không cần thoát khi đó là ký tự đầu tiên trong bộ ký tự, ví dụ []abc]khớp với một ]abchoặc khi đó là ký tự thứ hai sau một ^, ví dụ [^]]khớp với bất cứ thứ gì ngoại trừ ]. (Ngoại lệ đáng chú ý: hương vị ECMAScript!)

  • [không cần phải trốn thoát chút nào Cùng với mẹo trên, điều này có nghĩa là bạn có thể khớp cả hai dấu ngoặc với lớp nhân vật phản trực giác khủng khiếp [][].

  • ^không cần thoát khi đó không phải là ký tự đầu tiên trong bộ ký tự, vd [ab^c].

  • -không cần phải thoát ra khi nó là một trong hai người đầu tiên (thứ hai sau khi một ^nhân vật) hoặc cuối cùng trong một bộ ký tự, ví dụ [-abc], [^-abc]hoặc [abc-].

  • Không có nhân vật nào khác cần thoát ra bên trong một lớp nhân vật, ngay cả khi họ là các nhân vật meta bên ngoài các lớp nhân vật (ngoại trừ dấu gạch chéo ngược \).

Ngoài ra, trong một số hương vị ^$được kết hợp theo nghĩa đen khi chúng không ở đầu hoặc cuối của regex tương ứng.

(Cảm ơn @ MartinBüttner đã điền một vài chi tiết)


Một số người thích thoát dấu chấm thực tế bằng cách đặt nó trong một lớp ký tự mà ở đó nó không cần thoát (ví dụ. [.]). Thoát nó bình thường sẽ tiết kiệm 1 byte trong trường hợp này\.
CSᵠ

Lưu ý rằng [phải được thoát trong Java. Dù vậy, không chắc chắn về ICU (được sử dụng trong Android và iOS) hoặc .NET.
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

18

Một biểu thức chính quy đơn giản để khớp với tất cả các ký tự có thể in trong bảng ASCII .

[ -~]

1
tuyệt vời, tất cả các ký tự từ một bàn phím tiêu chuẩn Hoa Kỳ! lưu ý: bảng ascii tiêu chuẩn (không bao gồm phạm vi mở rộng 127-255
CSᵠ

Tôi sử dụng nó thường xuyên, nhưng nó thiếu một ký tự "thông thường": TAB. Và nó giả định rằng bạn đang sử dụng LC_ALL = "C" (hoặc tương tự) vì một số địa phương khác sẽ thất bại.
Olivier Dulac

Dấu gạch nối có thể được sử dụng như thế để chỉ định bất kỳ phạm vi ký tự nào trong bảng ASCII không? Điều đó làm việc cho tất cả các hương vị của regex?
Josh Withee

14

Biết hương vị regex của bạn

Có một số lượng đáng ngạc nhiên những người nghĩ rằng các biểu thức thông thường về cơ bản là bất khả tri ngôn ngữ. Tuy nhiên, thực sự có sự khác biệt khá lớn giữa các hương vị, và đặc biệt đối với môn đánh gôn, thật tốt khi biết một vài trong số chúng, và các tính năng thú vị của chúng, vì vậy bạn có thể chọn thứ tốt nhất cho mỗi nhiệm vụ. Dưới đây là một cái nhìn tổng quan về một số hương vị quan trọng và những gì làm cho chúng khác biệt với những hương vị khác. (Danh sách này không thể thực sự đầy đủ, nhưng hãy cho tôi biết nếu tôi bỏ lỡ điều gì đó thực sự rõ ràng.)

Perl và PCRE

Tôi đang ném những thứ này vào một nồi, vì tôi không quá quen thuộc với hương vị Perl và chúng gần như tương đương (PCRE là dành cho Biểu thức chính quy tương thích Perl). Ưu điểm chính của hương vị Perl là bạn thực sự có thể gọi mã Perl từ bên trong regex và thay thế.

  • Đệ quy / chương trình con . Có lẽ là tính năng quan trọng nhất để chơi golf (chỉ tồn tại trong một vài hương vị).
  • Mẫu có điều kiện (?(group)yes|no).
  • Hỗ trợ thay đổi của trường hợp trong chuỗi thay thế với \l, \u, \L\U.
  • PCRE cho phép luân phiên trong giao diện, trong đó mỗi phương án có thể có độ dài khác nhau (nhưng cố định). (Hầu hết các hương vị, bao gồm Perl yêu cầu lookbehind phải có chiều dài cố định tổng thể.)
  • \G để neo một trận đấu đến cuối trận đấu trước.
  • \K để thiết lập lại bắt đầu của trận đấu
  • PCRE hỗ trợ cả thuộc tính và tập lệnh Unicode .
  • \Q...\Eđể thoát khỏi các nhân vật chạy dài hơn. Hữu ích khi bạn đang cố gắng khớp chuỗi có chứa nhiều ký tự meta.

.MẠNG LƯỚI

Đây có lẽ là hương vị mạnh mẽ nhất, chỉ có rất ít thiếu sót.

Một thiếu sót quan trọng trong việc chơi golf là nó không hỗ trợ các bộ lượng hóa sở hữu như một số hương vị khác. Thay vì .?+bạn sẽ phải viết (?>.?).

Java

  • Do một lỗi (xem Phụ lục) Java hỗ trợ một loại hình nhìn có độ dài thay đổi hạn chế: bạn có thể tìm mọi cách từ đầu chuỗi đến .*từ nơi bạn có thể bắt đầu một giao diện, như thế nào (?<=(?=lookahead).*).
  • Hỗ trợ công đoàn và giao điểm của các lớp nhân vật.
  • Có sự hỗ trợ rộng rãi nhất cho Unicode, với các lớp ký tự cho "tập lệnh Unicode, khối, danh mục và thuộc tính nhị phân" .
  • \Q...\E như trong Perl / PCRE.

Hồng ngọc

Trong các phiên bản gần đây, hương vị này mạnh mẽ tương tự như PCRE, bao gồm hỗ trợ cho các cuộc gọi chương trình con. Giống như Java, nó cũng hỗ trợ liên kết và giao điểm của các lớp ký tự. Một tính năng đặc biệt là lớp ký tự tích hợp cho các chữ số hex: \h(và phủ định \H).

Tính năng hữu ích nhất để chơi gôn là cách Ruby xử lý các bộ định lượng. Đáng chú ý nhất, có thể lồng các bộ định lượng mà không cần dấu ngoặc đơn. .{5,7}+hoạt động và như vậy .{3}?. Ngoài ra, trái ngược với hầu hết các hương vị khác, nếu giới hạn dưới của bộ định lượng là 0nó có thể được bỏ qua, ví dụ như .{,5}tương đương với .{0,5}.

Đối với các chương trình con, sự khác biệt chính giữa các chương trình con của PCRE và các chương trình con của Ruby, là cú pháp của Ruby dài hơn một byte (?n)so với \g<n>, nhưng các chương trình con của Ruby có thể được sử dụng để chụp, trong khi PCRE đặt lại sau khi kết thúc chương trình con.

Cuối cùng, Ruby có ngữ nghĩa khác nhau cho các sửa đổi liên quan đến dòng hơn hầu hết các hương vị khác. Công cụ sửa đổi thường được gọi mtrong các hương vị khác luôn có trong Ruby. Vì vậy, ^$luôn luôn khớp với đầu và cuối của một dòng không chỉ là đầu và cuối của chuỗi. Điều này có thể giúp bạn tiết kiệm một byte nếu bạn cần hành vi này, nhưng nó sẽ khiến bạn tốn thêm byte nếu bạn không, bởi vì bạn sẽ phải thay thế ^$bằng \A\z, tương ứng. Ngoài ra, công cụ sửa đổi thường được gọi s(thay thế cho các nguồn cấp dữ liệu .khớp) được gọi mtrong Ruby thay thế. Điều này không ảnh hưởng đến số byte, nhưng nên ghi nhớ để tránh nhầm lẫn.

Con trăn

Python có hương vị đặc, nhưng tôi không biết bất kỳ tính năng đặc biệt hữu ích nào mà bạn sẽ không tìm thấy ở bất kỳ nơi nào khác.

Tuy nhiên , có một hương vị thay thế được dự định để thay thế remô-đun tại một số điểm, và có chứa nhiều tính năng thú vị. Ngoài việc thêm hỗ trợ cho đệ quy, giao diện có độ dài thay đổi và toán tử kết hợp lớp ký tự, nó còn có tính năng duy nhất của kết hợp mờ . Về bản chất, bạn có thể chỉ định một số lỗi (chèn, xóa, thay thế) được phép và động cơ cũng sẽ cung cấp cho bạn các kết quả gần đúng.

Bản thảo

Hương vị ECMAScript rất hạn chế, và do đó hiếm khi rất hữu ích cho việc chơi golf. Điều duy nhất nó có được là lớp nhân vật trống bị phủ [^] định để phù hợp với bất kỳ nhân vật nào cũng như lớp nhân vật trống thất bại vô điều kiện [](trái ngược với thông thường (?!)). Thật không may, hương vị không có bất kỳ tính năng nào làm cho cái sau hữu ích cho các vấn đề bình thường.

Lua

Lua có hương vị khá độc đáo của riêng nó, khá hạn chế (ví dụ: bạn thậm chí không thể định lượng được các nhóm) nhưng đi kèm với một số tính năng hữu ích và thú vị.

  • Nó có một số lượng lớn các tốc ký cho các lớp ký tự tích hợp , bao gồm dấu câu, ký tự chữ hoa / thường và chữ số hex.
  • Với %bnó hỗ trợ một cú pháp rất nhỏ gọn để phù hợp với các chuỗi cân bằng. Ví dụ: %b()khớp một (và sau đó mọi thứ cho đến khớp )(bỏ qua các cặp khớp bên trong một cách chính xác). ()có thể là bất kỳ hai nhân vật ở đây.

Tăng cường

Hương vị regex của Boost về cơ bản là của Perl. Tuy nhiên, nó có một số tính năng mới tuyệt vời để thay thế regex, bao gồm thay đổi trường hợp và điều kiện . Cái sau là duy nhất để Boost theo như tôi biết.


Lưu ý rằng nhìn về phía trước trong nhìn phía sau sẽ vượt qua giới hạn bị ràng buộc trong nhìn phía sau. Đã thử nghiệm trong Java và PCRE.
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

Không .?+tương đương với .*?
Máy

@CalculatorFeline Cái trước là bộ định lượng 0 hoặc 1 sở hữu (trong các hương vị hỗ trợ bộ lượng hóa sở hữu), cái sau là bộ định lượng 0 hoặc hơn.
Martin Ender

@CalculatorFeline ah Tôi hiểu sự nhầm lẫn. Có một lỗi đánh máy.
Martin Ender

13

Biết các lớp nhân vật của bạn

Hầu hết các hương vị regex có các lớp nhân vật được xác định trước. Ví dụ, \dkhớp với một chữ số thập phân, ngắn hơn ba byte [0-9]. Đúng, chúng có thể hơi khác nhau vì \dcũng có thể phù hợp với các chữ số Unicode cũng như trong một số hương vị, nhưng đối với hầu hết các thách thức, điều này sẽ không tạo ra sự khác biệt.

Dưới đây là một số lớp nhân vật được tìm thấy trong hầu hết các hương vị regex:

\d      Match a decimal digit character
\s      Match a whitespace character
\w      Match a word character (typically [a-zA-Z0-9_])

Ngoài ra, chúng tôi cũng có:

\D \S \W

đó là những phiên bản phủ định ở trên.

Hãy chắc chắn kiểm tra hương vị của bạn cho bất kỳ lớp nhân vật bổ sung nào nó có thể có. Ví dụ, PCRE có \Rcác dòng mới và Lua thậm chí có các lớp như ký tự chữ thường và chữ hoa.

(Cảm ơn @ HamZa và @ MartinBüttner đã chỉ ra những điều này)


3
\Rcho các dòng mới trong PCRE.
HamZa

12

Đừng bận tâm với các nhóm không bắt giữ (trừ khi ...)

Mẹo này áp dụng cho (ít nhất) tất cả các hương vị phổ biến lấy cảm hứng từ Perl.

Điều này có thể rõ ràng, nhưng (khi không chơi gôn) nên sử dụng các nhóm không bắt giữ (?:...)bất cứ khi nào có thể. ?:Tuy nhiên, hai nhân vật phụ này rất lãng phí khi chơi golf, vì vậy chỉ cần sử dụng các nhóm bắt giữ, ngay cả khi bạn sẽ không phản ứng lại họ.

Mặc dù có một ngoại lệ (hiếm): nếu bạn tình cờ tham gia nhóm phản 10hồi ít nhất 3 lần, bạn thực sự có thể lưu byte bằng cách biến một nhóm trước đó thành một nhóm không bắt giữ, sao cho tất cả những người đó \10trở thành \9s. (Áp dụng các thủ thuật tương tự, nếu bạn sử dụng nhóm 11ít nhất 5 lần, v.v.)


Tại sao 11 cần 5 lần để có giá trị khi 10 yêu cầu 3?
Nic Hartley

1
@QPaysTaxes có thể sử dụng $9thay vì $10hoặc $11một lần lưu một byte. Việc chuyển $10thành $9yêu cầu một ?:, là hai byte, vì vậy bạn sẽ cần ba $10giây để lưu thứ gì đó. Việc chuyển $11thành $9yêu cầu hai ?:s là bốn byte, vì vậy bạn sẽ cần năm $11giây để lưu thứ gì đó (hoặc năm $10$11kết hợp).
Martin Ender

10

Đệ quy cho tái sử dụng mẫu

Một số ít các hương vị hỗ trợ đệ quy ( theo hiểu biết của tôi , Perl, PCRE và Ruby). Ngay cả khi bạn không cố gắng giải quyết các vấn đề đệ quy, tính năng này có thể tiết kiệm rất nhiều byte theo các mẫu phức tạp hơn. Không cần thực hiện cuộc gọi đến nhóm khác (được đặt tên hoặc đánh số) trong chính nhóm đó. Nếu bạn có một mẫu nhất định xuất hiện nhiều lần trong regex của bạn, chỉ cần nhóm nó và tham khảo nó bên ngoài nhóm đó. Điều này không khác với một cuộc gọi chương trình con trong các ngôn ngữ lập trình bình thường. Vì vậy, thay vì

...someComplexPatternHere...someComplexPatternHere...someComplexPatternHere... 

trong Perl / PCRE bạn có thể làm:

...(someComplexPatternHere)...(?1)...(?1)...

hoặc trong Ruby:

...(someComplexPatternHere)...\g<1>...\g<1>...

với điều kiện đó là nhóm đầu tiên (tất nhiên, bạn có thể sử dụng bất kỳ số nào trong cuộc gọi đệ quy).

Lưu ý rằng điều này không giống như phản hồi ( \1). Backreferences khớp chính xác cùng chuỗi mà nhóm đã khớp lần trước. Các cuộc gọi chương trình con này thực sự đánh giá mô hình một lần nữa. Để làm ví dụ cho someComplexPatternHeremột lớp nhân vật dài:

a[0_B!$]b[0_B!$]c[0_B!$]d

Điều này sẽ phù hợp với một cái gì đó như

aBb0c!d

Lưu ý rằng bạn không thể sử dụng phản hồi ở đây trong khi duy trì hành vi. Một backreference sẽ thất bại trên chuỗi trên, vì B0!không giống nhau. Tuy nhiên, với các cuộc gọi chương trình con, mô hình thực sự được đánh giá lại. Mẫu trên hoàn toàn tương đương với

a([0_B!$])b(?1)c(?1)d

Nắm bắt trong các cuộc gọi chương trình con

Một lưu ý cho Perl và PCRE: nếu nhóm 1trong các ví dụ trên có chứa các nhóm khác, thì các lệnh gọi chương trình con sẽ không nhớ các lần bắt của chúng. Xem xét ví dụ này:

(\w(\d):)\2 (?1)\2 (?1)\2

Điều này sẽ không phù hợp

x1:1 y2:2 z3:3

bởi vì sau khi gọi lại chương trình con, việc bắt nhóm mới 2sẽ bị loại bỏ. Thay vào đó, mẫu này sẽ khớp với chuỗi này:

x1:1 y2:1 z3:1

Điều này khác với Ruby, nơi các cuộc gọi chương trình con làm giữ lại ảnh chụp của họ, vì vậy Ruby regex tương đương (\w(\d):)\2 \g<1>\2 \g<1>\2sẽ phù hợp với người đầu tiên của các ví dụ trên.


Bạn có thể sử dụng \1cho Javascript. Và PHP cũng vậy (tôi đoán vậy).
Ismael Miguel

5
@IsmaelMiguel Đây không phải là phản hồi. Điều này thực sự đánh giá mô hình một lần nữa. Ví dụ (..)\1sẽ khớp ababnhưng thất bại abbatrong khi (..)(?1)sẽ khớp với cái sau. Đây thực sự là một cuộc gọi chương trình con theo nghĩa là biểu thức được áp dụng lại, thay vì khớp theo nghĩa đen của nó so với lần trước.
Martin Ender

Wow, tôi không có ý tưởng! Học một cái gì đó mới mỗi ngày
Ismael Miguel

Trong .NET (hoặc các hương vị khác không có tính năng này):(?=a.b.c)(.[0_B!$]){3}d
jimmy23013 13/03/2015

@ user23013 có vẻ rất cụ thể với ví dụ cụ thể này. Tôi không chắc điều đó có thể áp dụng được không nếu tôi sử dụng lại một mẫu con nhất định trong nhiều trường hợp khác nhau.
Martin Ender

9

Làm cho trận đấu thất bại

Khi sử dụng regex để giải quyết các vấn đề tính toán hoặc khớp các ngôn ngữ không thường xuyên, đôi khi cần phải làm cho một nhánh của mẫu bị lỗi bất kể bạn đang ở đâu trong chuỗi. Cách tiếp cận ngây thơ là sử dụng một cái nhìn tiêu cực trống rỗng:

(?!)

Các nội dung (mẫu trống) luôn khớp, do đó, cái nhìn tiêu cực luôn luôn thất bại. Nhưng thường xuyên hơn không, có một tùy chọn đơn giản hơn nhiều: chỉ cần sử dụng một ký tự mà bạn biết sẽ không bao giờ xuất hiện trong đầu vào. Ví dụ: nếu bạn biết đầu vào của bạn sẽ luôn chỉ bao gồm các chữ số, bạn chỉ cần sử dụng

!

hoặc bất kỳ ký tự không chữ số, không meta nào khác để gây ra lỗi.

Ngay cả khi đầu vào của bạn có khả năng chứa bất kỳ chuỗi con nào, vẫn có những cách ngắn hơn (?!). Bất kỳ hương vị nào cho phép neo xuất hiện trong một mẫu trái ngược với kết thúc, có thể sử dụng một trong hai giải pháp 2 ký tự sau:

a^
$a

Tuy nhiên, lưu ý rằng một số hương vị sẽ coi ^$là các ký tự theo nghĩa đen ở các vị trí này, vì rõ ràng chúng không thực sự có ý nghĩa như các mỏ neo.

Trong hương vị ECMAScript cũng có giải pháp 2 ký tự khá thanh lịch

[]

Đây là một lớp nhân vật trống, cố gắng đảm bảo rằng các nhân vật tiếp theo là một trong những nhân vật trong lớp - nhưng không có nhân vật nào trong lớp, vì vậy điều này luôn thất bại. Lưu ý rằng điều này sẽ không hoạt động trong bất kỳ hương vị nào khác, bởi vì các lớp nhân vật thường không thể trống.


8

Tối ưu hóa bạn HOẶC

Bất cứ khi nào bạn có 3 hoặc nhiều lựa chọn thay thế trong RegEx:

/aliceblue|antiquewhite|aquamarine|azure/

Kiểm tra xem nếu có một khởi đầu chung:

/a(liceblue|ntiquewhite|quamarine|zure)/

Và thậm chí có thể là một kết thúc chung?

/a(liceblu|ntiquewhit|quamarin|zur)e/

Lưu ý: 3 chỉ là bắt đầu và sẽ chiếm cùng một độ dài, 4+ sẽ tạo ra sự khác biệt


Nhưng nếu không phải tất cả chúng đều có tiền tố chung thì sao? (khoảng trắng chỉ được thêm vào cho rõ ràng)

/aliceblue|antiquewhite|aqua|aquamarine|azure
|beige|bisque|black|blanchedalmond|blue|blueviolet|brown|burlywood
|cadetblue|chartreuse|chocolate|coral|cornflowerblue|cornsilk|crimson|cyan/

Nhóm chúng, miễn là quy tắc 3+ có ý nghĩa:

/a(liceblue|ntiquewhite|qua|quamarine|zure)
|b(eige|isque|lack|lanchedalmond|lue|lueviolet|rown|urlywood)
|c(adetblue|hartreuse|hocolate|oral|ornflowerblue|ornsilk|rimson|yan)/

Hoặc thậm chí khái quát hóa nếu entropy thỏa mãn usecase của bạn:

/\w(liceblue|ntiquewhite|qua|quamarine|zure
|eige|isque|lack|lanchedalmond|lue|lueviolet|rown|urlywood
|adetblue|hartreuse|hocolate|oral|ornflowerblue|ornsilk|rimson|yan)/

^ trong trường hợp này, chúng tôi chắc chắn rằng chúng tôi không nhận được bất kỳ cluehoặccrown slack Ryan

Điều này "theo một số thử nghiệm" cũng cải thiện hiệu suất, vì nó cung cấp một neo để bắt đầu.


1
Nếu bắt đầu hoặc kết thúc chung dài hơn một ký tự, thậm chí nhóm hai có thể tạo ra sự khác biệt. Thích aqua|aquamarineaqua(|marine)hoặc aqua(marine)?.
Paŭlo Ebermann

6

Điều này là khá đơn giản, nhưng đáng nói:

Nếu bạn thấy mình lặp lại những lớp nhân vật [a-zA-Z]có lẽ bạn có thể chỉ cần sử dụng [a-z]và append sự i(đựng pin- i nsensitive sửa đổi) để regex của bạn.

Ví dụ: trong Ruby, hai biểu thức chính sau là tương đương:

/[a-zA-Z]+\d{3}[a-zA-Z]+/
/[a-z]+\d{3}[a-z]/i - 7 byte ngắn hơn

Đối với vấn đề đó, các sửa đổi khác cũng có thể rút ngắn tổng chiều dài của bạn. Thay vì làm điều này:

/(.|\n)/

đó phù hợp với bất kỳ ký tự (vì dấu chấm không phù hợp xuống dòng), sử dụng s Ingle-line sửa đổi s, mà làm cho dòng mới phù hợp với dấu chấm.

/./s - 3 byte ngắn hơn


Trong Ruby, có rất nhiều Lớp nhân vật tích hợp cho regex. Xem trang này và tìm kiếm "Thuộc tính nhân vật".
Một ví dụ tuyệt vời là "Biểu tượng tiền tệ". Theo Wikipedia, có rất nhiều ký hiệu tiền tệ có thể và để đặt chúng vào một lớp ký tự sẽ rất tốn kém ( [$฿¢₡Ð₫€.....]) trong khi bạn có thể ghép bất kỳ ký hiệu nào trong 6 byte:\p{Sc}


1
Ngoại trừ JavaScript, nơi ssửa đổi không được hỗ trợ. :( Nhưng ở đó bạn có thể sử dụng /[^]/thủ thuật độc quyền của JavaScript .
manatwork

Lưu ý rằng (.|\n)thậm chí không hoạt động trong một số hương vị, vì .thường cũng không phù hợp với các loại dấu tách dòng khác. Tuy nhiên, cách thông thường để làm điều này (không có s) [\s\S]là các byte giống như (.|\n).
Martin Ender

@ MartinBüttner, ý tưởng của tôi là giữ nó cùng với các mẹo liên quan đến kết thúc dòng khác. Nhưng nếu bạn cảm thấy câu trả lời này là nhiều hơn về sửa đổi, tôi không phản đối nếu bạn đăng lại nó.
manatwork 6/03/2015

@manatwork đã hoàn thành (và cũng đã thêm một thủ thuật không liên quan đến ES cụ thể)
Martin Ender

6

Trình phân tích cú pháp ngôn ngữ đơn giản

Bạn có thể xây dựng một trình phân tích cú pháp rất đơn giản với RE like \d+|\w+|".*?"|\n|\S. Các mã thông báo bạn cần khớp được phân tách bằng ký tự RE 'hoặc'.

Mỗi lần công cụ RE cố gắng khớp ở vị trí hiện tại trong văn bản, nó sẽ thử mẫu đầu tiên, sau đó là mẫu thứ hai, v.v. Nếu thất bại (ví dụ về ký tự không gian ở đây), nó sẽ di chuyển và thử lại các lần khớp . Trật tự là quan trọng. Nếu chúng tôi đặt \Sthuật ngữ trước \d+thuật ngữ, \Sthì từ đầu tiên sẽ khớp với bất kỳ ký tự không phải không gian nào sẽ phá vỡ trình phân tích cú pháp của chúng tôi.

Bộ so ".*?"khớp chuỗi sử dụng một công cụ sửa đổi không tham lam để chúng tôi chỉ khớp một chuỗi tại một thời điểm. Nếu RE của bạn không có chức năng không tham lam, bạn có thể sử dụng chức năng "[^"]*"tương đương.

Ví dụ về Python:

text = 'd="dogfinder"\nx=sum(ord(c)*872 for c in "fish"+d[3:])'
pat = r'\d+|\w+|".*?"|\n|\S'
print re.findall(pat, text)

['d', '=', '"dogfinder"', '\n', 'x', '=', 'sum', '(', 'ord', '(', 'c', ')',
    '*', '872', 'for', 'c', 'in', '"fish"', '+', 'd', '[', '3', ':', ']', ')']

Ví dụ về Golfed Python:

# assume we have language text in A, and a token processing function P
map(P,findall(r'\d+|\w+|".*?"|\n|\S',A))

Bạn có thể điều chỉnh các mẫu và thứ tự của chúng cho ngôn ngữ bạn cần khớp. Kỹ thuật này hoạt động tốt cho JSON, HTML cơ bản và các biểu thức số. Nó đã được sử dụng thành công nhiều lần với Python 2, nhưng phải đủ chung để hoạt động trong các môi trường khác.


6

\K thay vì cái nhìn tích cực

PCRE và Perl hỗ trợ chuỗi thoát \K, thiết lập lại phần đầu của trận đấu. Điều đó ab\Kcdsẽ yêu cầu chuỗi đầu vào của bạn chứa abcdnhưng kết quả khớp được báo cáo sẽ chỉ có cd.

Nếu bạn đang sử dụng giao diện tích cực khi bắt đầu mẫu của bạn (có lẽ là nơi có khả năng nhất), thì trong hầu hết các trường hợp, bạn có thể sử dụng \Kthay thế và lưu 3 byte:

(?<=abc)def
abc\Kdef

Điều này là tương đương cho hầu hết các mục đích, nhưng không hoàn toàn. Sự khác biệt mang lại cả ưu điểm và nhược điểm với chúng:

  • Ưu điểm: PCRE và Perl không hỗ trợ các giao diện có độ dài tùy ý (chỉ .NET mới có). Đó là, bạn không thể làm một cái gì đó như (?<=ab*). Nhưng với \Kbạn có thể đặt bất kỳ loại mô hình trước mặt nó! Vì vậy, ab*\Klàm việc. Điều này thực sự làm cho kỹ thuật này mạnh hơn rất nhiều trong các trường hợp áp dụng.
  • Ưu điểm: Trông không quay lại. Điều này có liên quan nếu bạn muốn chụp một cái gì đó trong giao diện để phản hồi lại sau này, nhưng có một số cách chụp có thể dẫn đến kết quả khớp hợp lệ. Trong trường hợp này, công cụ regex sẽ chỉ thử một trong những khả năng đó. Khi sử dụng \Kphần đó của regex sẽ bị quay lại như mọi thứ khác.
  • Nhược điểm: Như bạn có thể biết, một số trận đấu của regex không thể trùng nhau. Thông thường, các lần nhìn được sử dụng để khắc phục một phần xung quanh giới hạn này, vì giao diện có thể xác nhận một phần của chuỗi đã được sử dụng bởi một trận đấu trước đó. Vì vậy, nếu bạn muốn khớp tất cả các ký tự theo sau ab bạn có thể sử dụng (?<=ab).. Đưa ra đầu vào

    ababc
    

    cái này sẽ khớp với cái thứ hai avà cái c. Điều này không thể được sao chép với \K. Nếu bạn đã sử dụng ab\K., bạn sẽ chỉ nhận được trận đấu đầu tiên, bởi vì bây giờ abkhông phải là trong một cái nhìn.


Nếu một mẫu sử dụng \Kchuỗi thoát trong một xác nhận tích cực, thì bắt đầu báo cáo của một trận đấu thành công có thể lớn hơn kết thúc của trận đấu.
HWND

@hwnd Quan điểm của tôi là đã cho ababc, không có cách nào khớp cả thứ hai acvới \K. Bạn sẽ chỉ nhận được một trận đấu.
Martin Ender

Bạn đã đúng, không phải với tính năng này. Bạn sẽ phải neo với\G
hwnd

@hwnd Ah tôi thấy quan điểm của bạn bây giờ. Nhưng tôi đoán tại thời điểm đó (từ góc độ chơi golf) bạn sẽ tốt hơn với một cái nhìn tiêu cực, bởi vì bạn thực sự thậm chí có thể cần nó vì bạn không thể chắc chắn rằng .trận đấu cuối cùng thực sự là một a.
Martin Ender

1
Cách sử dụng thú vị của \ K =)
hwnd

5

Phù hợp với bất kỳ nhân vật

Hương vị ECMAScript thiếu các công cụ ssửa đổi .phù hợp với bất kỳ ký tự nào (bao gồm cả dòng mới). Điều này có nghĩa là không có giải pháp một ký tự để khớp các ký tự hoàn toàn tùy ý. Giải pháp tiêu chuẩn trong các hương vị khác (khi một người không muốn sử dụng svì một số lý do) là [\s\S]. Tuy nhiên, ECMAScript là hương vị duy nhất (theo hiểu biết của tôi) hỗ trợ các lớp nhân vật trống rỗng, và do đó có một sự thay thế ngắn hơn nhiều : [^]. Đây là một lớp nhân vật trống bị phủ định - nghĩa là, nó phù hợp với bất kỳ nhân vật nào.

Ngay cả đối với các hương vị khác, chúng ta có thể học hỏi từ kỹ thuật này: nếu chúng ta không muốn sử dụng s(ví dụ: vì chúng ta vẫn cần có nghĩa thông thường .ở những nơi khác), vẫn có thể có một cách ngắn hơn để khớp cả hai ký tự dòng mới và có thể in được, miễn là có một số ký tự mà chúng ta biết không xuất hiện trong đầu vào. Giả sử, chúng tôi đang xử lý các số được phân định bởi các dòng mới. Sau đó, chúng ta có thể khớp bất kỳ nhân vật nào với [^!], vì chúng ta biết rằng !sẽ không bao giờ là một phần của chuỗi. Điều này tiết kiệm hai byte trên ngây thơ [\s\S]hoặc [\d\n].


4
Trong Perl, \Ncó nghĩa là chính xác những gì .có nghĩa là bên ngoài /schế độ, ngoại trừ nó không bị ảnh hưởng bởi một chế độ.
Konrad Borowski

4

Sử dụng các nhóm nguyên tử và định lượng sở hữu

Tôi thấy nhóm nguyên tử ( (?>...)) và quantifiers sở hữu ( ?+, *+, ++, {m,n}+) đôi khi rất hữu ích cho chơi golf. Nó phù hợp với một chuỗi và không cho phép quay lại sau. Vì vậy, nó sẽ chỉ khớp với chuỗi phù hợp đầu tiên được tìm thấy bởi công cụ regex.

Ví dụ: Để khớp một chuỗi có số lẻ lẻ aở đầu, không được theo sau bởi nhiều hơn a, bạn có thể sử dụng:

^(aa)*+a
^(?>(aa)*)a

Điều này cho phép bạn sử dụng những thứ như một .*cách tự do và nếu có sự trùng khớp rõ ràng, sẽ không có khả năng nào khác phù hợp với quá nhiều hoặc quá ít ký tự, có thể phá vỡ mô hình của bạn.

Trong .NET regex (không có bộ định lượng sở hữu), bạn có thể sử dụng nhóm này để bật nhóm 1 bội số lớn nhất của 3 (với tối đa 30) lần (không được chơi golf tốt):

(?>((?<-1>){3}|){10})

1
ECMAscript cũng thiếu các bộ lượng hóa sở hữu hoặc các nhóm nguyên tử :(
CSᵠ 6/03/2015

4

Quên một nhóm bị bắt sau khi thể hiện phụ (PCRE)

Đối với regex này:

^((a)(?=\2))(?!\2)

Nếu bạn muốn xóa \ 2 sau nhóm 1, bạn có thể sử dụng đệ quy:

^((a)(?=\2)){0}(?1)(?!\2)

Nó sẽ phù hợp aatrong khi cái trước sẽ không. Đôi khi bạn cũng có thể sử dụng ??hoặc thậm chí ?thay thế {0}.

Điều này có thể hữu ích nếu bạn sử dụng thu hồi rất nhiều và một số phản hồi hoặc nhóm điều kiện xuất hiện ở những nơi khác nhau trong biểu thức chính thức của bạn.

Cũng lưu ý rằng các nhóm nguyên tử được giả định cho việc thu hồi trong PCRE. Vì vậy, điều này sẽ không khớp với một chữ cái duy nhất a:

^(a?){0}(?1)a

Tôi đã không thử nó trong các hương vị khác chưa.

Đối với người tìm kiếm, bạn cũng có thể sử dụng tiêu cực kép cho mục đích này:

^(?!(?!(a)(?=\1))).(?!\1)

4

Biểu thức tùy chọn

Đôi khi thật hữu ích khi nhớ rằng

(abc)?

chủ yếu giống như

(abc|)

Mặc dù có một sự khác biệt nhỏ: trong trường hợp đầu tiên, nhóm hoặc bắt abchoặc không bắt được. Trường hợp thứ hai sẽ làm cho một phản ứng thất bại vô điều kiện. Trong biểu thức thứ hai, nhóm sẽ chụp abchoặc một chuỗi rỗng, trong đó trường hợp sau sẽ tạo ra một kết quả ngược lại vô điều kiện. Để mô phỏng hành vi sau với ?bạn cần phải bao quanh mọi thứ trong một nhóm khác có giá hai byte:

((abc)?)

Phiên bản sử dụng |cũng hữu ích khi bạn muốn bọc biểu thức trong một số dạng nhóm khác và không quan tâm đến việc chụp:

(?=(abc)?)
(?=abc|)

(?>(abc)?)
(?>abc|)

Cuối cùng, thủ thuật này cũng có thể được áp dụng cho sự không tham gia ?khi nó lưu một byte ngay cả ở dạng thô (và do đó là 3 byte khi kết hợp với các dạng nhóm khác):

(abc)??
(|abc)

1

Nhiều giao diện luôn khớp (.NET)

Nếu bạn có 3 cấu trúc lookahead trở lên luôn khớp (để chụp các biểu tượng con) hoặc có một bộ định lượng trên một cái nhìn theo sau bởi một thứ khác, vì vậy chúng phải nằm trong một nhóm không nhất thiết phải bị bắt:

(?=a)(?=b)(?=c)
((?=a)b){...}

Đây là ngắn hơn:

(?(?(?(a)b)c))
(?(a)b){...}

nơi akhông nên là tên của một nhóm bị bắt. Bạn không thể sử dụng |để chỉ những điều thông thường trong bckhông cần thêm một cặp dấu ngoặc đơn khác.

Thật không may, các nhóm cân bằng trong các điều kiện dường như có lỗi, làm cho nó vô dụng trong nhiều trường hợp.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.