Làm thế nào hoặc tại sao sử dụng `. *?` Tốt hơn `. *`?


9

Tôi đã trả lời câu hỏi này trên SuperUser , một cái gì đó liên quan đến loại biểu thức chính quy được sử dụng trong khi lấy một đầu ra.

Câu trả lời tôi đưa ra là:

 tail -f log | grep "some_string.*some_string"

Và sau đó, trong ba bình luận cho câu trả lời của tôi @Bob đã viết điều này:

.*là tham lam và có thể nắm bắt nhiều hơn bạn muốn. .*?thường là tốt hơn.

Thì đây,

những ?là một modifier trên *, làm cho nó lười biếng thay vì mặc định tham lam. Giả sử PCRE.

Tôi googled cho PCRE, nhưng không thể hiểu ý nghĩa của điều này trong câu trả lời của tôi là gì?

và cuối cùng là

Tôi cũng nên chỉ ra rằng đây là regex (grep thực hiện regex POSIX theo mặc định), không phải là shell toàn cầu.

Tôi chỉ biết Regex là gì và sử dụng nó rất cơ bản trong lệnh grep. Vì vậy, tôi không thể nhận được bất kỳ ý kiến ​​nào trong số 3 ý kiến ​​đó và tôi có những câu hỏi sau:

  • Sự khác biệt trong việc sử dụng .*?so với là .*gì?
  • Cái nào tốt hơn và trong hoàn cảnh nào? Vui lòng cung cấp các ví dụ.

Ngoài ra sẽ rất hữu ích để hiểu các ý kiến, Nếu bất cứ ai có thể


CẬP NHẬT: Như một câu trả lời cho câu hỏi Regex khác với Shell Globs như thế nào? @Kusalananda cung cấp liên kết này trong bình luận của mình.

LƯU Ý: Nếu cần, Vui lòng đọc câu trả lời của tôi cho câu hỏi này trước khi trả lời để tham khảo ngữ cảnh.


Đây là hai câu hỏi rất khác nhau. Câu hỏi đầu tiên được trả lời bởi unix.stackexchange.com/questions/57957/, trong khi câu hỏi thứ hai phụ thuộc vào ứng dụng của mẫu (không thể nói là "tốt hơn" trong mọi trường hợp).
Kusalananda

Bạn có thể chỉnh sửa câu hỏi này để chỉ về vấn đề .*so với .*?. Câu hỏi "sự khác biệt giữa biểu thức chính quy và câu hỏi vỏ" đã được giải quyết trên trang web này.
Kusalananda

Câu trả lời:


7

Ashok đã chỉ ra sự khác biệt giữa .*.*?, vì vậy tôi sẽ chỉ cung cấp một số thông tin bổ sung.

grep (giả sử phiên bản GNU) hỗ trợ 4 cách để khớp chuỗi:

  • Chuỗi cố định
  • Biểu thức chính quy cơ bản (BRE)
  • Biểu thức chính quy mở rộng (ERE)
  • Biểu thức chính quy tương thích Perl (PCRE)

grep sử dụng BRE theo mặc định.

BRE và ERE được ghi lại trong chương Biểu thức chính quy của POSIX và PCRE được ghi lại trên trang web chính thức của nó . Xin lưu ý rằng các tính năng và cú pháp có thể khác nhau giữa các lần triển khai.

Điều đáng nói là cả BRE và ERE đều không ủng hộ sự lười biếng :

Hành vi của nhiều ký hiệu trùng lặp liền kề ('+', '*', '?' Và khoảng) tạo ra kết quả không xác định.

Vì vậy, nếu bạn muốn sử dụng tính năng đó, bạn sẽ cần sử dụng PCRE thay thế:

# BRE greedy
$ grep -o 'c.*s' <<< 'can cats eat plants?'
can cats eat plants

# BRE lazy
$ grep -o 'c.*\?s' <<< 'can cats eat plants?'
can cats eat plants

# ERE greedy
$ grep -E -o 'c.*s' <<< 'can cats eat plants?'
can cats eat plants

# ERE lazy
$ grep -E -o 'c.*?s' <<< 'can cats eat plants?'
can cats eat plants

# PCRE greedy
$ grep -P -o 'c.*s' <<< 'can cats eat plants?'
can cats eat plants

# PCRE lazy
$ grep -P -o 'c.*?s' <<< 'can cats eat plants?'
can cats

Chỉnh sửa 1

Bạn có thể vui lòng giải thích một chút về .*vs .*??

  • .*được sử dụng để phù hợp với "dài nhất" 1 mẫu có thể.

  • .*?được sử dụng để phù hợp với "ngắn" 1 mẫu có thể.

Theo kinh nghiệm của tôi, hành vi mong muốn nhất thường là hành vi thứ hai.

Ví dụ: giả sử chúng tôi có chuỗi sau đây và chúng tôi chỉ muốn khớp các thẻ html 2 , không phải nội dung giữa chúng:

<title>My webpage title</title>

Bây giờ so sánh .*với .*?:

# Greedy
$ grep -P -o '<.*>' <<< '<title>My webpage title</title>'
<title>My webpage title</title>

# Lazy
$ grep -P -o '<.*?>' <<< '<title>My webpage title</title>'
<title>
</title>

1. Ý nghĩa của "dài nhất" và "ngắn nhất" trong bối cảnh regex là một chút khó khăn, như Kusalananda chỉ ra . Tham khảo tài liệu chính thức để biết thêm thông tin.
2. Không nên phân tích cú pháp html bằng regex . Đây chỉ là một ví dụ cho mục đích giáo dục, không sử dụng nó trong sản xuất.


Bạn có thể vui lòng giải thích một chút về .*vs .*??
C0deDaedalus

@ C0deDaedalus Cập nhật.
nxnev

9

Giả sử tôi lấy một chuỗi như:

can cats eat plants?

Sử dụng tham lam c.*ssẽ khớp với toàn bộ chuỗi kể từ khi nó bắt đầu cvà kết thúc bằng s, là một toán tử tham lam, nó tiếp tục khớp cho đến khi xuất hiện cuối cùng của s.

Trong khi đó, việc sử dụng lười biếng c.*?ssẽ chỉ khớp với nhau cho đến khi xuất hiện lần đầu tiên s, tức là chuỗi can cats.

Từ ví dụ trên, bạn có thể thu thập được rằng:

"Tham lam" có nghĩa là khớp chuỗi dài nhất có thể. "Lười" có nghĩa là phù hợp với chuỗi ngắn nhất có thể. Thêm ?vào một lượng hóa như *, +, ?, hoặc {n,m}làm cho nó lười biếng.


1
"Ngắn nhất có thể" sẽ là cats, vì vậy nó không thực thi "ngắn nhất có thể" theo nghĩa đó.
Kusalananda

2
@Kusalananda đúng, không hoàn toàn theo nghĩa đó nhưng "ngắn nhất có thể" ở đây có nghĩa là giữa lần xuất hiện đầu tiên của cả c và s.
Ashok

1

Một chuỗi có thể được khớp theo nhiều cách (từ đơn giản đến phức tạp hơn):

  1. Là một chuỗi tĩnh (Giả sử var = 'Hello World!'):

    [ "$var" = "Hello World!" ] && echo yes
    echo "$var" | grep -F "Hello"
    grep -F "Hello" <<<"$var"

  2. Như một quả địa cầu:

    echo ./* # liệt kê tất cả các tập tin trong pwd.
    case $var in (*Worl*) echo yes;; (*) echo no;; esac
    [[ "$var" == *"Worl"* ]] && echo yes

    Có những khối cơ bản và mở rộng. Các caseví dụ sử dụng những đống cơ bản. [[Ví dụ bash sử dụng các khối lượng mở rộng. Khớp tập tin đầu tiên có thể là cơ bản hoặc mở rộng trên một số shell như cài đặt extglobtrong bash. Cả hai đều giống hệt nhau trong trường hợp này. Grep không thể sử dụng quả cầu.

    Các dấu hoa thị trong một glob nghĩa một cái gì đó khác với một dấu sao trong một regex :

    * matches any number (including none) ofbất kỳ nhân vật . yếu tố
    * matches any number (including none) of thetrước .

  3. Là một biểu thức chính quy cơ bản (BRE):

    echo "$var" | sed 's/W.*d//' # in: Xin chào!
    grep -o 'W.*d' <<<"$var" # in thế giới!

    Không có BRE trong vỏ (cơ bản) hoặc awk.

  4. Biểu thức chính quy mở rộng (ERE):

    [[ "$var" =~ (H.*l) ]] # trận đấu: Xin chào Worl
    echo "$var" | sed -E 's/(d|o)//g' # print: Hell Wrl!
    awk '/W.*d/{print $1}' <<<"$var" # print: Xin chào
    grep -oE 'H.*l' <<<"$var" # print: Xin chào Worl

  5. Biểu thức chính quy tương thích Perl:

    grep -oP 'H.*?l # in: Trợ giúp

Chỉ trong PCRE a *?có một số ý nghĩa cú pháp cụ thể.
Nó làm cho dấu hoa thị lười biếng (vô duyên): Lười thay vì tham lam .

$ grep -oP 'e.*l' <<<"$var"
ello Worl

$ grep -oP 'e.*?l' <<<"$var"
el

Đây chỉ là phần nổi của tảng băng trôi, có những kẻ tham lam, lười biếng , và ngoan ngoãn hoặc sở hữu . Ngoài ra còn có lookahead và lookbehind nhưng những cái đó không áp dụng cho dấu hoa thị *.

Có một cách khác để có được hiệu quả tương tự như một regex không tham lam:

$ grep -o 'e[^o]*o' <<<"$var"
ello

Ý tưởng rất đơn giản: không sử dụng dấu chấm ., phủ định ký tự tiếp theo để khớp [^o]. Với một thẻ web:

$ grep -o '<[^>]*>' <<<'<script type="text/javascript">document.write(5 + 6);</script>'
<script type="text/javascript">
</script>

Ở trên cần làm rõ hoàn toàn tất cả các bình luận @Bob 3. Diễn giải:

  • A. * Là một biểu thức chính, không phải toàn cầu.
  • Chỉ một regex có thể tương thích với PCRE.
  • Trong PCRE: a? sửa đổi định lượng *. .*tham lam .*?là không.

Câu hỏi

  • Sự khác biệt trong cách sử dụng. ? so với ?

    • A .*?chỉ hợp lệ trong cú pháp PCRE.
    • A .*là di động hơn.
    • Hiệu ứng tương tự như một trận đấu không tham lam có thể được thực hiện bằng cách thay thế dấu chấm bằng một phạm vi ký tự bị phủ định: [^a]*
  • Cái nào tốt hơn và trong hoàn cảnh nào? Vui lòng cung cấp các ví dụ.
    Tốt hơn? Nó phụ thuộc vào mục tiêu. Không có tốt hơn, mỗi là hữu ích cho các mục đích khác nhau. Tôi đã cung cấp một số ví dụ ở trên. Bạn cần nhiều hơn?

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.