Pattern
2 つのオブジェクトを比較する簡単な方法はありますか?
コード内のコメントをチェックするためPattern
に正規表現を使用してコンパイルしたものがあります。"//"
コメントを記述する正規表現がいくつかあるので、それらを区別する方法を見つけたいと思います。
どうすればそれができますか?クラスはメソッドを実装しPattern
ませんequals
。
orPattern
を呼び出した結果を比較することでオブジェクトを比較できますが、これはあなたが望むことをしません(私があなたの質問を正しく理解していれば)。具体的には、ファクトリ メソッドに渡された文字列を比較します。ただし、これは、パターン文字列に個別に渡されるフラグを考慮していません。pattern()
toString
Pattern.compile(...)
2 つの同一でない正規表現が等しいかどうかをテストする簡単な方法はありません。たとえば、".+"
と"..*"
は同等の正規表現を表しますが、Pattern
API を使用してこれを判断する簡単な方法はありません。
問題が理論的に解決可能かどうかはわかりません...一般的な場合。 @Akimコメント:
正規表現の同等性に対する有限の公理化はないため、短い答えは「これは正規表現自体のツリー変換では実行できません」です。ただし、2 つのオートマトンの言語を比較 (同等性をテスト) できるため、2 つの正規表現が同等かどうかを計算できます。合理的な言語の領域、つまりオートマトンの領域から逃れるキャプチャ グループへの後方参照などの拡張機能を持たない「本物の」正規表現について言及していることに注意してください。
また、受け入れられた回答についてコメントしたいと思います。作者は、Pattern のメソッドが から継承されていることを示していると主張するコードをいくつか提供しています。実際、彼が見ている出力はそれと一致しています...しかし、それは示されていません。equals
Object
これが事実であるかどうかを知る正しい方法は、equals
メソッドが継承されたメソッドのリストにリストされている javadoc ... を見ることです。それは決定的です。
では、この例が著者の言うことを示していないのはなぜでしょうか?
2 つのメソッドが同じように動作しても、実装方法が異なる場合があります。クラスをブラック ボックスとして扱う場合Pattern
、これが発生していないことを示すことはできません。(または、少なくとも ... リフレクションを使用せずにではありません。)
著者はこれを 1 つのプラットフォームでのみ実行しました。他のプラットフォームは異なる動作をする可能性があります。
Pattern
2 番目の点については、 (Java 1.4 での)の以前の実装では、Pattern.compile(...)
メソッドが最近コンパイルされたパターン オブジェクトのキャッシュを保持していたことを思い出します1。特定のパターン文字列を 2 回コンパイルした場合、2 回目は最初に返されたものと同じオブジェクトを取得する可能性があります。これにより、テスト コードは次のように出力されます。
true
true
true
true
しかし、それは何を示していますか?Pattern
オーバーライドすることを示していますObject.equals
か?いいえ!
ここでの教訓は、主に javadocs を見て、Java ライブラリ メソッドがどのように動作するかを理解する必要があるということです。
「ブラック ボックス」テストを作成すると、誤った結論を導き出す可能性があります。少なくとも、すべてのプラットフォームに当てはまるとは限らない結論です。
「コードを読む」ことに基づいて結論を下すと、他のプラットフォームでは無効な結論を導き出すリスクがあります。
1 - 私の記憶が間違っていたとしても、そのような実装はPattern.compile(...)
メソッドの javadoc と一致します。彼らは、各呼び出しが新しいオブジェクトcompile
を返すとは言いません。Pattern
多分私は質問を完全に理解していません。ただし、次の例でわかるように、java.lang.Object.equals(Object)
すべての Java オブジェクトにはデフォルトのメソッドがあります。このメソッドは、オブジェクトへの参照を比較します。つまり、==
演算子を使用します。
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()));
}
}
出力:
true
false
true
true
不思議な理由で、Pattern オブジェクトは equals() を実装していません。たとえば、次の単純な単体テストは失敗します。
@Test
public void testPatternEquals() {
Pattern p1 = Pattern.compile("test");
Pattern p2 = Pattern.compile("test");
assertEquals(p1, p2); // fails!
}
これに対する最も一般的な回避策は、パターン オブジェクトの文字列表現を比較することです (パターンの作成に使用された文字列を返します)。
@Test
public void testPatternEquals() {
Pattern p1 = Pattern.compile("test");
Pattern p2 = Pattern.compile("test");
assertEquals(p1.toString(), p2.toString()); // succeeds!
}
Pattern
しませんが、String
します。Pattern
s がコンパイルされた元の正規表現を比較してみませんか?
他の答えは問題を解決するかもしれませんが、それらが問題に対する本当の答えだとは思いません。
本当に 2 つのパターンを比較したい場合は、基本的に2 つの通常の言語を比較する必要があります。
これを行うには、cs stackexchange が既にソリューションを投稿しています: https://cs.stackexchange.com/questions/12876/equivalence-of-regular-expressions
通常の言語の同等性をチェックするための高速な方法は、Hopcroft and Karp アルゴリズム (HK) です。
アルゴリズムの Java 実装は次のとおりです: http://algs4.cs.princeton.edu/65reductions/HopcroftKarp.java.html
私は質問のアイデアを得たと思います.sを比較する方法を探したので、Pattern
ここに行き着きました(おそらく2年遅すぎます.申し訳ありません...)。
私はテストを書いていますが、私のメソッドが期待されるパターンを返すかどうかを知る必要があります。toString()
またはを介したテキストpattern()
は同じかもしれませんが、フラグは異なる可能性があり、パターンを使用したときの結果は予期しないものになるでしょう。
しばらく前に、私は の独自の一般的な実装を書きましたtoString()
。フィールドを含むすべてのフィールドを収集しprivate
、ログ記録や明らかにテストに使用できる文字列を作成します。2 つの等しいパターンをコンパイルすると、フィールドroot
とmatchRoot
が異なることが示されました。これらの 2 つは同等性にそれほど関係がないと仮定すると、 field があるため、完全ではないにしてもflag
、私の解決策は非常に優れています。
/**
* 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() + "'";
}
テストは緑色です。
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);
パターンが作成された文字列表現を比較できます。
Pattern p1 = getPattern1();
Pattern p2 = getPattern2();
if (p1.pattern().equals(p2.pattern())){
// your code here
}