Các biểu thức chính quy trong lập trình Java

Thảo luận trong 'Java nâng cao' bắt đầu bởi thanhlongi90.it, 1/4/13.

  1. thanhlong90.it

    thanhlong90.it Admin Support

    Lượt xem: 24,941
    android
    Một biểu thức chính quy là gì?

    Một biểu thức chính quy (regular expression) về bản chất là một mẫu để mô tả một tập hợp các chuỗi ký tự chia sẻ chung mẫu này. Ví dụ, đây là một tập hợp các chuỗi ký tự có một số điều chung:
    * một chuỗi (a string).
    * một chuỗi dài hơn (a longer string).
    * một chuỗi rất dài (a much longer string).
    Mỗi chuỗi ký tự này đều bắt đầu bằng “a” và kết thúc bằng “string.” API của các biểu thức chính quy của Java (Java Regular Expression) giúp bạn thể hiện điều đó và làm nhiều việc lý thú với các kết quả.
    API của biểu thức chính quy (Regular Expression – hoặc viết tắt là regex) của Java là khá giống với các công cụ biểu thức chính quy có sẵn trong ngôn ngữ Perl. Nếu bạn là một lập trình viên của Perl, bạn sẽ cảm thấy đúng như đang ở nhà, ít nhất là với cú pháp mẫu biểu thức chính quy của ngôn ngữ Java. Tuy nhiên, nếu bạn không thường sử dụng biểu thức chính quy , chắc là nó có vẻ trông hơi lạ một chút. Đừng lo lắng: không phải phức tạp như nó có vẻ thế đâu.

    API của biểu thức chính quy
    Năng lực về biểu thức chính quy của ngôn ngữ Java gồm có ba lớp cốt lõi mà bạn sẽ sử dụng hầu như mọi lúc:
    * Pattern, trong đó mô tả một mẫu chuỗi ký tự.
    * Matcher, để kiểm tra một chuỗi ký tự xem nó có khớp với mẫu không.
    * PatternSyntaxException, để báo cho bạn rằng một số thứ không thể chấp nhận được với mẫu mà bạn đã thử định nghĩa.
    Cách tốt nhất để tìm hiểu về biểu thức chính quy là qua các ví dụ, do đó trong phần này chúng ta sẽ tạo ra một ví dụ đơn giản trong CommunityApplication.main(). Tuy nhiên, trước khi chúng ta tiến hành, điều quan trọng là hiểu được một số cú pháp mẫu biểu thức chính quy . Chúng ta sẽ thảo luận điều đó chi tiết hơn trong phần kế tiếp.

    Cú pháp mẫu
    Một mẫu (pattern) biểu thức chính quy mô tả cấu trúc của chuỗi ký tự mà một biểu thức sẽ cố gắng tìm kiếm trong một chuỗi ký tự đầu vào. Đây chính là tại sao biểu thức chính quy nhìn có vẻ hơi lạ thường. Tuy nhiên, một khi bạn hiểu được cú pháp, để giải mã sẽ ít khó khăn hơn.
    Dưới đây là một số trong các cấu kiện mẫu phổ biến nhất mà bạn có thể sử dụng trong các chuỗi ký tự mẫu:
    Cấu kiện
    Cái được coi là ăn khớp
    .
    Bất kỳ ký tự nào.
    ?
    Không (0) hoặc một (1) của ký tự đứng trước.
    *
    Không (0) hoặc lớn hơn của ký tự đứng trước.
    +
    Một (1) hoặc lớn hơn của ký tự đứng trước.
    []
    Một dải các ký tự hay chữ số.
    ^
    Không phải cái tiếp sau (tức là, “không phải <cái gì đó>”).
    d
    Bất kỳ số nào (tùy chọn, [0-9]).
    D
    Bất kỳ cái gì không là số (tùy chọn, [^0-9]).
    s
    Bất kỳ khoảng trống nào (tùy chọn, [ ntfr]).
    S
    Không có bất kỳ khoảng trống nào (tùy chọn, [^ ntfr]).
    w
    Bất kỳ từ nào (tùy chọn, [a-zA-Z_0-9]).
    W
    Không có bất kỳ từ nào (tùy chọn, [^w]).
    Một số cấu kiện đầu tiên ở đây được gọi là các lượng tử ((quantifiers), bởi vì chúng xác định số lượng cái đứng trước chúng. Các cấu kiện như là d là các lớp ký tự được định nghĩa trước. Bất kỳ ký tự nào mà không có ý nghĩa đặc biệt trong một mẫu sẽ là một trực kiện và chỉ khớp với chính nó.

    So khớp
    Sau khi đã trang bị những hiểu biết mới của chúng ta về các mẫu, đây là một ví dụ đơn giản về mã sử dụng các lớp trong API của biểu thức chính quy Java:
    PHP:
    Pattern pattern Pattern.compile("a.*string");
    Matcher matcher pattern.matcher("a string");
     
    boolean didMatch matcher.matches();
    System.out.println(didMatch);
     
    int patternStartIndex matcher.start();
    System.out.println(patternStartIndex);
     
    int patternEndIndex matcher.end();
    System.out.println(patternEndIndex);
    Trước tiên, chúng ta tạo ra một Pattern. Chúng ta làm điều đó bằng cách gọi compile(), một phương thức tĩnh trên Pattern, với một chữ chuỗi ký tự biểu diễn mẫu mà chúng ta muốn so khớp. Chữ đó sử dụng cú pháp mẫu biểu thức chính quy mà bây giờ chúng ta đã có thể hiểu được. Trong ví dụ này, khi dịch thành ngôn ngữ thông thường, mẫu biểu thức chính quy có nghĩa là: “Tìm một chuỗi ký tự có dạng bắt đầu là ‘a’, theo sau là không hay nhiều ký khác, kết thúc bằng ‘string’”.
    Tiếp theo, chúng ta gọi matcher() trên Pattern của chúng ta. Lời gọi này tạo ra một cá thể Matcher. Khi điều đó xảy ra, Matcher tìm kiếm chuỗi ký tự mà chúng ta đã chuyển cho nó để so khớp với chuỗi mẫu mà chúng ta đã dùng để tạo ra Pattern Như bạn đã biết, mọi chuỗi ký tự trong ngôn ngữ Java là một sưu tập các ký tự có đánh chỉ số, bắt đầu từ 0 và kết thúc bằng độ dài chuỗi trừ một. Matcher phân tích cú pháp của chuỗi ký tự, bắt đầu từ 0 và tìm các kết quả khớp với mẫu.
    Sau khi hoàn tất quá trình đó, Matcher chứa rất nhiều thông tin về các kết quả khớp đã được tìm thấy (hoặc không tìm thấy) trong chuỗi đầu vào của chúng ta. Chúng ta có thể truy cập thông tin đó bằng cách gọi các phương thức khác nhau trên Matcher của chúng ta::
    * matches() đơn giản cho chúng ta biết rằng toàn bộ chuỗi đầu vào có khớp đúng với mẫu hay không.
    * start() cho chúng ta biết giá trị chỉ số trong chuỗi ở đó bắt đầu khớp đúng với mẫu.
    * end() cho chúng ta biết giá trị chỉ số ở đó kết thúc khớp đúng với mẫu, cộng với một.
    Trong ví dụ đơn giản của chúng ta, có một kết quả khớp bắt đầu từ 0 và kết thúc tại 7. Vì vậy, lời gọi matches() trả về kết quả đúng (true), lời gọi start() trả về 0 và lời gọi end() trả về 8. Nếu trong chuỗi ký tự của chúng ta có nhiều ký tự hơn trong mẫu mà chúng ta tìm kiếm, chúng ta có thể sử dụng lookingAt() thay cho matches(). lookingAt() tìm kiếm chuỗi con khớp với mẫu của chúng ta. Ví dụ, hãy xem xét chuỗi ký tự sau đây:
    Here is a string with more than just the pattern.
    Chúng ta có thể tìm kiếm mẫu a.*string và có được kết quả khớp nếu chúng ta sử dụng lookingAt(). Nếu chúng ta sử dụng matches() để thay thế, nó sẽ trả về kết quả là sai (false), bởi vì có nhiều thứ trong chuỗi đầu vào hơn là đúng những gì có trong mẫu.

    Các mẫu phức tạp
    Những việc tìm kiếm đơn giản là dễ dàng với các lớp biểu thức chính quy, nhưng cũng có thể tìm kiếm tinh vi hơn nhiều.
    Bạn có thể đã quen thuộc với wiki, một hệ thống dựa trên web cho phép người dùng chỉnh sửa các trang web để làm nó “lớn lên”. Các Wiki, cho dù được viết bằng ngôn ngữ Java hay không, hầu như hoàn toàn được dựa trên các biểu thức chính quy. Nội dung của chúng được dựa trên chuỗi ký tự mà người sử dụng nhập vào, được các biểu thức chính quy phân tích cú pháp và định dạng. Một trong những đặc tính nổi bật nhất của các wiki là ở chỗ bất kỳ người dùng nào cũng có thể tạo ra một liên kết đến một chủ đề khác trong wiki bằng cách nhập vào một từ wiki , mà thường là một loạt các từ được móc nối với nhau, mỗi một từ trong đó bắt đầu bằng một chữ cái viết hoa, như sau:
    MyWikiWord
    Giả sử có chuỗi ký tự sau:
    Here is a WikiWord followed by AnotherWikiWord, then YetAnotherWikiWord.
    Bạn có thể tìm kiếm các từ wiki trong chuỗi này với mẫu biểu thức chính quy như sau:
    PHP:
    [A-Z][a-z]*([A-Z][a-z]*)+

    Dưới đây là một số mã để tìm kiếm các từ wiki:
    String input = “Here is a WikiWord followed by AnotherWikiWord, then SomeWikiWord.”;
    Pattern pattern = Pattern.compile(“[A-Z][a-z]*([A-Z][a-z]*)+”);
    Matcher matcher = pattern.matcher(input);
    PHP:
    while (matcher.find()) {
    System.out.println("Found this wiki word: " matcher.group());
    }
    Bạn sẽ thấy ba từ wiki trong màn hình của bạn.

    Việc thay thế
    Tìm kiếm các kết quả khớp đúng là rất có ích, nhưng chúng ta cũng có thể thao tác chuỗi ký tự sau khi tìm thấy một kết quả khớp. Chúng ta có thể thực hiện điều đó bằng cách thay thế các kết quả khớp bằng một thứ gì khác, cũng giống như bạn có thể tìm kiếm một đoạn văn bản trong một chương trình xử lý van bản và thay thế nó bằng một cái gì khác. Có một số phương thức trên Matcher để giúp cho chúng ta:
    * replaceAll(), để thay thế tất cả các kết quả khớp bằng một chuỗi ký tự mà chúng ta chỉ định.
    * replaceFirst(), để chỉ thay thế kết quả khớp đầu tiên bằng một chuỗi ký tự mà chúng ta chỉ định.
    Sử dụng các phương thức này rất dễ hiểu:
    PHP:
    String input "Here is a WikiWord followed by AnotherWikiWord, then SomeWikiWord.";
    Pattern pattern Pattern.compile("[A-Z][a-z]*([A-Z][a-z]*)+");
    Matcher matcher pattern.matcher(input);
    System.out.println("Before: " input);
     
    String result matcher.replaceAll("replacement");
    System.out.println("After: " result);
    Mã này tìm các từ wiki, như trước đây. Khi Matcher tìm thấy một kết quả khớp, nó thay mỗi từ wiki bằng chuỗi replacement. Khi bạn chạy mã này, bạn sẽ thấy phần sau đây trên màn hình:
    Trước: Here is WikiWord followed by AnotherWikiWord, then SomeWikiWord.
    Sau: Here is replacement followed by replacement, then replacement.
    Nếu chúng ta đã sử dụng replaceFirst(), chúng ta sẽ thấy như sau:
    Trước: Here is a WikiWord followed by AnotherWikiWord, then SomeWikiWord.
    Sau: Here is a replacement followed by AnotherWikiWord, then SomeWikiWord.

    Các nhóm
    Chúng ta cũng có thể tưởng tượng hơn một chút. Khi bạn tìm kiếm các kết quả khớp với một mẫu biểu thức chính quy, bạn có thể nhận được thông tin về những gì bạn đã tìm thấy. Chúng ta đã thấy điều này với các phương thức start() và end() trên Matcher. Nhưng chúng ta cũng có thể tham khảo các kết quả khớp thông qua các nhóm bắt giữ (capturing groups). Trong mỗi mẫu, bạn thường tạo ra các nhóm bằng cách bao quanh một phần mẫu bằng cặp dấu ngoặc đơn. Các nhóm được đánh số từ trái sang phải, bắt đầu từ 1 (nhóm 0 đại diện cho kết quả khớp toàn bộ). Sau đây là một số mã để thay thế mỗi từ wiki bằng một chuỗi ký tự “bọc quanh” từ:
    PHP:
    String input "Here is a WikiWord followed by AnotherWikiWord, then SomeWikiWord.";
    Pattern pattern Pattern.compile("[A-Z][a-z]*([A-Z][a-z]*)+");
    Matcher matcher pattern.matcher(input);
    System.out.println("Before: " input);
     
    String result matcher.replaceAll("blah$0blah");
    System.out.println("After: " result);
    Việc chạy mã này sẽ tạo ra kết quả sau:
    Trước: Here is a WikiWord followed by AnotherWikiWord, then SomeWikiWord.
    Sau: Here is a blahWikiWordblah followed by blahAnotherWikiWordblah,
    then blahSomeWikiWordblah.
    Trong mã này, chúng ta đã tham chiếu kết quả khớp toàn bộ bằng cách đưa thêm $0 vào trong chuỗi thay thế. Bất kỳ phần nào của chuỗi ký tự thay thế có dạng $<một số nguyên> sẽ tham chiếu đến nhóm được xác định bởi các số nguyên (do đó $1 trỏ đến nhóm 1 và tiếp tục). Nói cách khác, $0 tương đương với điều sau đây:
    PHP:
    matcher.group(0);

    Chúng ta có thể hoàn thành mục tiêu thay thế tương tự bằng cách sử dụng một số các phương thức khác, hơn là gọi replaceAll():
    PHP:
    public void listen(String conversation) {
    Pattern pattern Pattern.compile(".*my name is (.*).");
    Matcher matcher pattern.matcher(conversation);
     
    if (
    matcher.lookingAt())
    System.out.println("Hello, " matcher.group(1) + "!");
    else
    System.out.println("I didn't understand.");
    }
    Phương thức này cho phép chúng ta tiến hành một số cuộc đối thoại với một Adult. Nếu chuỗi ký tự đó có dạng đặc biệt, Adult của chúng ta có thể trả lời bằng một lời chào tốt lành. Nếu không, nó có thể nói rằng nó không hiểu được.
    Phương thức listen() kiểm tra chuỗi ký tự đầu vào để xem nó có khớp với một mẫu nhất định không: một hay nhiều ký tự, theo sau là “tên tôi là” (my name is), tiếp theo là một hoặc nhiều ký tự, tiếp theo là một dấu chấm câu. Chúng ta sử dụng lookingAt() để tìm kiếm một chuỗi con của đầu vào khớp với mẫu. Nếu chúng ta tìm thấy một kết quả khớp, chúng ta xây dựng một chuỗi ký tự làm lời chào bằng cách nắm bắt lấy những gì đi sau ” my name is “, mà chúng ta cho rằng đó sẽ là tên (đó là những gì nhóm 1 sẽ chứa). Nếu chúng ta không tìm thấy một kết quả khớp nào, chúng ta trả lời rằng chúng ta không hiểu. Dĩ nhiên là Adult của chúng ta không có nhiều khả năng đối thoại lắm vào lúc này.
    Đây là một ví dụ tầm thường về các khả năng xử lý biểu thức chính quy của ngôn ngữ Java, nhưng nó minh họa cách làm thế nào để sử dụng chúng.

    Làm rõ các biểu thức
    Các biểu thức chính quy có vẻ bí hiểm. Rất dễ bị thất bại với mã trông rất giống như tiếng Phạn ấy. Đặt tên các thứ cho đúng và xây dựng các biểu thức cho tốt cũng có thể giúp đỡ nhiều.
    Ví dụ, đây là mẫu của chúng ta cho một từ wiki:
    PHP:
    [A-Z][a-z]*([A-Z][a-z]*)+

    Bây giờ bạn hiểu cú pháp của biểu thức chính quy, bạn sẽ có thể đọc mà không phải tốn công quá nhiều, nhưng mã của chúng ta sẽ dễ hiểu hơn nhiều nếu chúng ta khai báo một hằng số để lưu giữ chuỗi mẫu. Chúng ta có thể đặt tên nó kiểu như WIKI_WORD. Phương thức listen() của chúng ta sẽ bắt đầu như thế này:
    PHP:
    public void listen(String conversation) {
    Pattern pattern Pattern.compile(WIKI_WORD);
    Matcher matcher pattern.matcher(conversation);
    ...
    }
    Một thủ thuật khác có thể trợ giúp là định nghĩa các hằng số cho mỗi phần của các mẫu, sau đó xây dựng các mẫu phức tạp hơn như việc lắp ráp các phần có tên. Nói chung, mẫu càng phức tạp thì càng khó khăn để giải mã nó và càng dễ xảy ra lỗi hơn. Bạn sẽ thấy rằng không có cách thực sự nào để gỡ lỗi các biểu thức chính quy khác hơn cách thử nghiệm và sửa lỗi. Hãy làm cho cuộc sống đơn giản hơn bằng cách đặt tên các mẫu và các thành phần mẫu.
    java
  2. dodanhhieu

    dodanhhieu New Member

  3. R700

    R700 Member

    lấy nguồn IBM mà không ghi nhé, trừ 1 điểm rèn luyện :D
  4. Datbau

    Datbau New Member

    ?
    Không (0) hoặc một (1) của ký tự đứng trước.
    *
    Không (0) hoặc lớn hơn của ký tự đứng trước.
    +
    Một (1) hoặc lớn hơn của ký tự đứng trước.

    ----> Cái này hiểu ntn ạ :-ss Bác giải thích giúp em vs đc ko ạ :(
    Mà cái chỗ này thì mỗi giấu để check có khớp ko, có thể là nhiều ký tự chứ ko phải 1 ký tự phải ko ạ :(
    Em cảm ơn nhiều nhé!!!

Chia sẻ trang này