Sự khác biệt giữa các nguyên tử '\ zs' và '\ @ <=' trong Vim regex là gì?


11

Đây là những gì tôi nhận được từ tài liệu: \zs"bắt đầu phần được tô sáng" sau khi khớp với biểu thức chính trước đó và \@<="bắt đầu phần được tô sáng" sau khi khớp với nguyên tử trước . Nhưng tôi không hiểu chính xác sự tinh tế của điều này, vậy ai có thể giải thích làm thế nào chúng khác nhau sâu hơn một chút không?

Đây là điều khiến tôi tò mò: nếu tôi chạy

/\_s\zsnnoremap

tức là chọn nnoremaptrước một khoảng trắng hoặc một dòng bắt đầu (tức là dòng mới từ dòng trước, do đó \_trước đó s) và sau đó chạy gnđể vào Chế độ trực quanchọn trực quan trận đấu tiếp theo, vì một số lý do chỉ là cột đầu tiên (nghĩa là là người đầu tiên ntrong nnoremap) được chọn - bất chấp thực tế rằng toàn bộ nnoremaptừ được nhấn mạnh với :hlsearchbật.

Tuy nhiên, nếu tôi thay vì chạy tìm kiếm

/\_s\@<=nnoremap

và sau đó thử gn, toàn bộ nnoremapđược chọn đúng. Điều gì có thể xảy ra ở đây? Có phải tôi (dám nói) phát hiện ra một số lỗi tối nghĩa?


Tôi nghĩ rằng nó trong :h patternsnhưng bộ nhớ của tôi cho thấy rằng regex bao gồm các nguyên tử, nếu điều đó giúp giải thích sự khác biệt.
D. Ben Knoble

Câu trả lời:


15

Điều này có vẻ như bạn thực sự tìm thấy một lỗi tối nghĩa. Tôi đã triển khai gntextobject vào năm 2012 cho Vim 7.3. Về cơ bản nó hoạt động theo cách sau:

1) Nó tìm kiếm ngược cho trận đấu cuối cùng của biểu thức chính quy hiện tại.

2) Nó tìm kiếm chuyển tiếp cho trận đấu tiếp theo của biểu thức chính quy hiện tại.

Điều này sẽ làm rõ, rằng con trỏ sẽ bắt đầu trận đấu tiếp theo, ngay cả khi nó đã ở đó khi bắt đầu 1). Cuối cùng

3) nó tìm kiếm kết thúc của biểu thức chính quy hiện tại. và đặt con trỏ ở đó.

Bây giờ điều xảy ra ở đây là việc tìm kiếm kết thúc của trận đấu hiện tại kết thúc và quay trở lại kết thúc trận đấu trước đó (vì wrapscanđã được đặt, sau khi bị vô hiệu hóa cho 1)). Sau đó, nó đặt điểm đánh dấu Visual vào khu vực từ đầu (cuối điểm 2) và khu vực được chuyển đến bởi mục tìm kiếm tiếp theo 3).

Tôi sẽ xem xét kỹ hơn vấn đề và có thể sẽ gửi bản vá cho Vim sau.

[Cập nhật 22.05.2018] Tôi đã viết và gửi một bản vá để khắc phục hành vi này.

[Update2 22.05.2018] Và bản vá đã được hợp nhất dưới dạng bản vá 8.1.0018

[Cập nhật 22.10.2019] Kể từ bản vá Vim 8.1.629 , bước thứ ba không được thực hiện nữa. Thay vào đó, giờ đây Vim có thể xác định kết thúc trận đấu khi tìm điểm bắt đầu của trận đấu (Bước 2)


8

Christian đã hoàn toàn giải quyết câu hỏi về hành vi lỗi của gn, nhưng vẫn có những khác biệt cơ bản giữa \zs\@<=. Sinh vật lớn nhất \@<=sửa đổi một nguyên tử trước, trong khi đó \zslà một nguyên tử trong chính nó.

Xem xét:

Xnnoremap

\%1cX\zsnnoremap     (regex 1)
\%1cX\@<=nnoremap    (regex 2)
\%2cX\@<=nnoremap    (regex 3)

Regex 1 khớp, vì \%1ckhớp với cột 1 và có X ở đó. \zschỉ khiến trận đấu được khởi động lại tại một vị trí sau X.

Tuy nhiên, Regex 2 không khớp, vì mặc dù \%1ckhớp với cột đầu tiên, X\@<=có độ rộng bằng không (như được đề cập trong tài liệu) và nnoremapbắt đầu ở cột 2. Không có gì để tạo ra sự khác biệt vị trí giữa các cột 1 và 2.

Regex 3 khớp từ khi nnoremapbắt đầu ở cột 2.


1
Tôi không nghĩ regex 2 thất bại vì không có gì để tạo ra sự khác biệt về vị trí giữa các cột 1 và 2. Nếu đó là vấn đề, loại bỏ nnoremapkhỏi regex sẽ tạo ra một kết quả khớp; nhưng regex vẫn thất bại ngay cả khi không có. Tôi nghĩ rằng nó thất bại bởi vì \%1cX\@<=thể hiện một vị trí không thể tồn tại. \%1ckhớp với vị trí ở cột 1 và X\@<=yêu cầu một ký tự Xkhớp với trước đó. Nhưng không thể có bất kỳ ký tự nào trước cột đầu tiên. Đó là lý do tại sao, ngay cả khi bạn thay thế Xbằng dấu chấm (bất kỳ ký tự nào), regex \%1c.\@<=vẫn thất bại.
user938271

4

\zsáp dụng cho toàn bộ biểu thức chính quy và đặt ký tự tiếp theo là ký tự đầu tiên của toàn bộ kết quả khớp. Bất cứ điều gì trước \zssẽ không được bao gồm như là một phần của văn bản phù hợp.

\@<=mặt khác, chỉ ảnh hưởng đến các nguyên tử trực tiếp xung quanh nó, cho phép bạn xác định rằng nguyên tử tiếp theo sẽ chỉ khớp nếu nó tuân theo nguyên tử trước. Vì vậy, ví dụ, biểu thức chính quy:

\vbar.*(foo)@<=bar

Sẽ khớp tất cả các văn bản giữa hai trường hợp bar(bao gồm cả các phiên bản), nhưng chỉ khi lần thứ hai đi trước foo. tức là nó sẽ khớp

barbazfoobar

nhưng không:

barbazbazbar

\@<=được bản địa hóa theo cách này, bạn thậm chí có thể sử dụng \@<=nhiều lần trong một biểu thức:

\vbar.*(foo)@<=bar.*(foo)@<=bar

Các trường hợp sau đây sẽ khớp với ba trường hợp bar, nhưng chỉ khi hai trường hợp thứ hai được đặt trước foo.

tức là đưa ra văn bản:

barfoobarbazfoobar
barfoobarbazbazbar
barbazbarbazfoobar

Nó sẽ chỉ phù hợp với dòng đầu tiên.


Nhưng bạn có thể trao đổi giao diện đầu tiên với \zs, nghĩa là, điều này cũng sẽ hoạt động : \vfoo\zsbar.*(foo)@<=bar.
Karl Yngve Lervåg

@ KarlYngveLervåg Điểm tốt. Tôi đã chỉnh sửa để làm rõ hơn sự khác biệt và sử dụng các ví dụ \zskhông thể thay thế được.
Giàu

Vì vậy, theo sự hiểu biết của tôi, \zs\zecó thể được thay thế bằng cách nhìn xung quanh các mẫu regex, và chúng mạnh hơn, phải không? Nguyên nhân mạnh mẽ hơn chúng có thể được sử dụng nhiều lần và có thể được nhóm lại \(\). Và cũng bởi vì chúng hoạt động như cái nhìn của perl xung quanh regex. Có gì nhầm không?
klaus

1
@klaus Điều đó nghe có vẻ đúng với tôi (mặc dù tôi không phải là chuyên gia). Lưu ý rằng bạn nên sử dụng \zs/ \zekhi bạn có thể, bởi vì chúng nhanh hơn so với xung quanh.
Giàu

Hiểu. Và \zs\zerõ ràng trực quan hơn. Cảm ơn đã giải thích.
klaus
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.