2012-04-07 95 views
7

Có cách nào dễ dàng so sánh hai đối tượng Pattern không?java so sánh hai đối tượng Mẫu

Tôi có một số Pattern được biên dịch bằng cách sử dụng regex "//" để kiểm tra nhận xét trong mã.

Vì có nhiều regex để mô tả nhận xét, tôi muốn tìm cách để phân biệt chúng.

Làm thế nào nó có thể được thực hiện? lớp Pattern không thực hiện phương thức equals.

Trả lời

4

Có lẽ tôi không hiểu đầy đủ câu hỏi. Nhưng như bạn có thể thấy trong ví dụ sau, có một phương thức mặc định là java.lang.Object.equals(Object) cho mọi đối tượng Java. Phương pháp này so sánh các tham chiếu đến các đối tượng, tức là sử dụng toán tử ==.

 

package test; 

import java.util.regex.Pattern; 

public class Main { 

    private static final Pattern P1 = Pattern.compile("//.*"); 
    private static final Pattern P2 = Pattern.compile("//.*"); 

    public static void main(String[] args) { 
    System.out.println(P1.equals(P1)); 
    System.out.println(P1.equals(P2)); 
    System.out.println(P1.pattern().equals(P1.pattern())); 
    System.out.println(P1.pattern().equals(P2.pattern())); 
    } 
} 
 

Đầu ra:

 

true 
false 
true 
true 
 
2

Pattern không nhưng String. Tại sao không chỉ so sánh regex mà từ đó các số Pattern được biên soạn?

0

Bạn có thể so sánh cơ quan đại diện chuỗi từ đó mô hình đã được thực hiện:

Pattern p1 = getPattern1(); 
Pattern p2 = getPattern2(); 
if (p1.pattern().equals(p2.pattern())){ 
    // your code here 
} 
4

Bạn có thể so sánh Pattern đối tượng bằng cách so sánh kết quả của gọi pattern() hoặc toString nhưng điều này không làm những gì bạn muốn (nếu tôi hiểu câu hỏi của bạn một cách chính xác). Cụ thể, điều này so sánh các chuỗi được chuyển đến phương thức nhà máy Pattern.compile(...).

Không có cách nào đơn giản để kiểm tra nếu hai regex tương tự không giống nhau. Ví dụ: ".+""..*" đại diện cho các regex tương đương, nhưng không có cách tiến thẳng nào để xác định điều này bằng cách sử dụng API Pattern.

(tôi thậm chí không biết nếu vấn đề là về mặt lý thuyết có thể giải quyết ... trong trường hợp tổng quát.)


Tôi cũng muốn nhận xét về câu trả lời chấp nhận. Tác giả cung cấp một số mã mà anh ta tuyên bố hiển thị rằng phương thức của Pattern là equals được kế thừa từ Object. Trên thực tế, kết quả mà anh ta thấy là nhất quán với điều đó ... nhưng nó không hiển thị hiển thị.

Cách chính xác để biết nếu đây là trường hợp là xem javadoc ... trong đó phương pháp equals được liệt kê trong danh sách các phương pháp kế thừa. Đó là dứt khoát.

Vậy tại sao ví dụ này không hiển thị nội dung tác giả cho biết?

  1. Có thể có hai phương pháp để hành xử theo cùng một cách nhưng được triển khai khác nhau. Nếu chúng ta coi lớp Pattern là hộp đen, thì chúng ta không thể chứng minh rằng điều này không xảy ra. (Hoặc ít nhất ... không phải không sử dụng sự phản chiếu.)

  2. Tác giả chỉ chạy trên một nền tảng. Các nền tảng khác có thể hoạt động khác nhau.

Trên quan điểm thứ hai, hồi ức của tôi là trong việc thực hiện trước đó của Pattern (trong Java 1.4) các Pattern.compile(...) phương pháp lưu giữ một bộ nhớ cache của mô hình thời gian gần đây thu thập các đối tượng . Nếu bạn đã biên soạn một chuỗi mẫu cụ thể hai lần, lần thứ hai bạn có thể nhận được cùng một đối tượng như đã được trả lại lần đầu tiên. Điều đó sẽ khiến mã kiểm tra xuất ra:

true 
    true 
    true 
    true 

Nhưng điều đó hiển thị điều gì? Liệu nó có hiển thị rằng Pattern ghi đè Object.equals? Không!

Bài học ở đây là bạn nên tìm hiểu làm thế nào một thư viện phương thức Java xử chủ yếu bằng cách nhìn vào javadocs:

  • Nếu bạn viết một "hộp đen" kiểm tra, bạn phải chịu trách nhiệm để vẽ kết luận không chính xác ... hoặc ít nhất, kết luận có thể không đúng cho tất cả các nền tảng.

  • Nếu bạn đưa ra kết luận về "đọc mã", bạn có nguy cơ rút ra các kết luận không hợp lệ cho các nền tảng khác.


1 - Ngay cả khi hồi ức của tôi là không chính xác, một thực hiện như vậy sẽ phù hợp với javadocs cho Pattern.compile(...) phương pháp. Họ không nói rằng mỗi cuộc gọi compile trả về đối tượng Pattern mới.

+0

Nếu bạn so sánh 'pattern' bạn sẽ bỏ lỡ những lá cờ. –

+0

@MarkusMalkusch - Vâng, đó là một lý do khác để so sánh các đối tượng 'Pattern' bằng cách sử dụng' pattern() 'có thể có vấn đề. –

+0

Đối tượng mẫu chưa bao giờ được lưu trữ tự động. Như bằng chứng, các tài liệu API cảnh báo rằng 'Pattern.matches()' và 'String # matches()' không cho phép đối tượng Pattern được sử dụng lại, và do đó không nên được sử dụng cho các cuộc gọi lặp lại, như trong vòng lặp. (Lớp Scanner * làm * lưu trữ tất cả các mẫu mà nó sử dụng, nhưng đó là xử lý nội bộ.) –

3

Vì lý do bí ẩn, đối tượng Mẫu không triển khai bằng(). Ví dụ, unittest đơn giản này sẽ thất bại:

@Test 
    public void testPatternEquals() { 
     Pattern p1 = Pattern.compile("test"); 
     Pattern p2 = Pattern.compile("test"); 
     assertEquals(p1, p2); // fails! 
    } 

Các phương pháp phổ biến nhất cho điều này có vẻ là để so sánh các cơ quan đại diện chuỗi của các đối tượng mẫu (trong đó trả về chuỗi được sử dụng để tạo ra các mẫu):

@Test 
    public void testPatternEquals() { 
     Pattern p1 = Pattern.compile("test"); 
     Pattern p2 = Pattern.compile("test"); 
     assertEquals(p1.toString(), p2.toString()); // succeeds! 
    } 
0

Tôi nghĩ rằng tôi có ý tưởng về câu hỏi và vì tôi đã tìm kiếm các cách để so sánh Pattern tôi kết thúc ở đây (hai năm quá muộn có thể, tốt, xin lỗi ...).

Tôi đang viết các bài kiểm tra và tôi cần biết liệu phương pháp của tôi có trả về mẫu dự kiến ​​hay không. Trong khi văn bản qua toString() hoặc pattern() có thể giống nhau, các cờ có thể khác nhau và kết quả khi sử dụng mẫu sẽ không mong muốn.

Một thời gian trước, tôi đã viết triển khai chung của riêng mình là toString(). Nó thu thập tất cả các trường bao gồm các trường private và xây dựng một chuỗi có thể được sử dụng để ghi nhật ký và dường như để thử nghiệm. Nó cho thấy rằng các trường rootmatchRoot khác nhau khi biên dịch hai mẫu bằng nhau. Giả sử rằng hai điều đó không liên quan đến bình đẳng và vì có một trường flag, giải pháp của tôi khá tốt nếu không hoàn hảo.

/** 
* Don't call this method from a <code>toString()</code> method with 
* <code>useExistingToString</code> set to <code>true</code>!!! 
*/ 
public static String toString(Object object, boolean useExistingToString, String... ignoreFieldNames) { 
    if (object == null) { 
    return null; 
    } 

    Class<? extends Object> clazz = object.getClass(); 
    if (useExistingToString) { 
    try { 
     // avoid the default implementation Object.toString() 
     Method methodToString = clazz.getMethod("toString"); 
     if (!methodToString.getDeclaringClass().isAssignableFrom(Object.class)) { 
     return object.toString(); 
     } 
    } catch (Exception e) { 
    } 
    } 

    List<String> ignoreFieldNameList = Arrays.asList(ignoreFieldNames); 
    Map<String, Object> fields = new HashMap<String, Object>(); 
    while (clazz != null) { 
    for (Field field : clazz.getDeclaredFields()) { 
     String fieldName = field.getName(); 
     if (ignoreFieldNameList.contains(fieldName) || fields.containsKey(fieldName)) { 
     continue; 
     } 

     boolean accessible = field.isAccessible(); 
     if (!accessible) { 
     field.setAccessible(true); 
     } 
     try { 
     Object fieldValue = field.get(object); 
     if (fieldValue instanceof String) { 
      fieldValue = stringifyValue(fieldValue); 
     } 
     fields.put(fieldName, fieldValue); 
     } catch (Exception e) { 
     fields.put(fieldName, "-inaccessible- " + e.getMessage()); 
     } 
     if (!accessible) { 
     field.setAccessible(false); 
     } 
    } 
    // travel upwards in the class hierarchy 
    clazz = clazz.getSuperclass(); 
    } 

    return object.getClass().getName() + ": " + fields; 
} 

public static String stringifyValue(Object value) { 
    if (value == null) { 
    return "null"; 
    } 
    return "'" + value.toString() + "'"; 
} 

Và thử nghiệm là màu xanh lá cây:

String toString1 = Utility.toString(Pattern.compile("test", Pattern.CASE_INSENSITIVE), false, "root", "matchRoot"); 
String toString2 = Utility.toString(Pattern.compile("test", Pattern.CASE_INSENSITIVE), false, "root", "matchRoot"); 
assertEquals(toString1, toString2); 
0

Để xác định xem hai Pattern đối tượng là tương đương, điều đơn giản nhất để làm là để so sánh chuỗi mô hình thực tế cờ sử dụng để tạo mẫu:

boolean isPatternEqualToPattern(final Pattern p1, final Pattern p2) { 
    return p1.flags() == p2.flags() && 
     p1.pattern().equals(p2.pattern()); 
} 
0

Tôi biết automata có thể giải quyết được sự cố của bạn. Nhưng điều đó có thể phức tạp. Nói chung, bạn nên so sánh ít nhất pattern.pattern()pattern.flags(), mặc dù không đủ để quyết định liệu hai regex có tương đương hay không.

0

Mặc dù các câu trả lời khác có thể giải quyết được vấn đề, tôi không nghĩ rằng đó là câu trả lời thực sự cho vấn đề.

Nếu bạn thực sự muốn so sánh hai mẫu về cơ bản bạn muốn so sánh hai ngôn ngữ thông thường.

Để làm điều này, cs stackexchange đã đăng một giải pháp: https://cs.stackexchange.com/questions/12876/equivalence-of-regular-expressions

Một phương pháp nhanh chóng để kiểm tra sự tương đương của các ngôn ngữ thông thường là các thuật toán Hopcroft và Karp (HK).

Đây là một thực hiện java của thuật toán: http://algs4.cs.princeton.edu/65reductions/HopcroftKarp.java.html