2

次の形式の文字列があります。

canonical_class_name[key1="value1",key2="value2",key3="value3",...] 

目的は、グループ内の canonical_class_name をキャプチャしてから、key=value グループを交互に取得することです。現在、これはテスト文字列と一致しません (次のプログラムでは、testString)。

少なくとも 1 つのキーと値のペアが必要ですが、そのようなペアは多数存在する場合があります。

質問: 現在、正規表現は標準クラス名と最初のキーを正しく取得しますが、最後の二重引用符まですべてを飲み込んでしまいます。キーと値のペアを遅延取得するにはどうすればよいですか?

次のプログラムがまとめた正規表現は次のとおりです。

(\S+)\[\s*(\S+)\s*=\s*"(.*)"\s*(?:\s*,\s*(\S+)\s*=\s*"(.*)"\s*)*\]

好みによっては、プログラムのバージョンの方が読みやすいかもしれません。

私のプログラムが文字列を渡された場合:

org.myobject[key1=\"value1\", key2=\"value2\", key3=\"value3\"]

...これらは私が得るグループです:

Group1 contains: org.myobject<br/>
Group2 contains: key1<br/>
Group3 contains: value1", key2="value2", key3="value3<br/>

もう 1 つ注意してください。I を使用String.split()すると式を簡略化できますが、正規表現の理解を深めるための学習経験としてこれを使用しているため、このようなショートカットは使用したくありません。

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class BasicORMParser {
     String regex =
            "canonicalName\\[ map (?: , map )*\\]"
            .replace("canonicalName", "(\\S+)")
            .replace("map", "key = \"value\"")
            .replace("key", "(\\S+)")
            .replace("value", "(.*)")
            .replace(" ", "\\s*"); 

    List<String> getGroups(String ormString){
        List<String> values = new ArrayList();
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(ormString);
        if (matcher.matches() == false){
            String msg = String.format("String failed regex validiation. Required: %s , found: %s", regex, ormString);
            throw new RuntimeException(msg);
        }
        if(matcher.groupCount() < 2){
            String msg = String.format("Did not find Class and at least one key value.");
            throw new RuntimeException(msg);
        }
        for(int i = 1; i < matcher.groupCount(); i++){
            values.add(matcher.group(i));
        }
        return values;
    }
}
4

2 に答える 2

4

あなたは実際に自分で質問に答えました:彼らを怠惰にします。つまり、怠惰な(貪欲でないまたは消極的な)数量詞を使用します。(\S+)それぞれを(\S+?)に、それぞれ(.*)をに変更するだけ(.*?)です。しかし、それが私なら、貪欲に関係なく、それらがあまり一致しないように、それらの部分式を変更します。たとえば([^\s\[]+)、クラス名、([^\s=]+)キー、および"([^"]*)"値に使用できます。

しかし、それであなたの本当の問題が解決することはないと思います。すべてのキーと値のペアに正しく一致するように取得すると、最初のペア(グループ#2と#3)と最後のペア(グループ#4と#5)のみがキャプチャされることがわかります。これ(?:\s*,\s*(\S+)\s*=\s*"(.*)"\s*)*は、繰り返されるたびに、これら2つのグループの内容が上書きされ、前の反復でキャプチャしたものがすべて失われるためです。それを回避することはできません。これは少なくとも2段階の操作です。たとえば、すべてのキーと値のペアを1つのブロックとして照合してから、個々のペアを分割することができます。

もう一つ。この行:

if(matcher.groupCount() < 2){

...おそらくあなたが思っていることをしていないでしょう。 groupCount()Patternオブジェクトの静的プロパティです。正規表現にキャプチャグループがいくつあるかを示します。一致が成功するか失敗するかにかかわらず、groupCount()常に同じ値(この場合は5)が返されます。試合が成功した場合、一部のキャプチャグループはnullになる可能性があります(試合に参加しなかったことを示します)が、常に5つあります。


編集:私はこれがあなたが最初に試みていたものだと思います:

Pattern p = Pattern.compile(
    "(?:([^\\s\\[]+)\\[|\\G)([^\\s=]+)=\"([^\"]*)\"[,\\s]*");

String s = "org.myobject[key1=\"value1\", key2=\"value2\", key3=\"value3\"]";
Matcher m = p.matcher(s);
while (m.find())
{
  if (m.group(1) != null)
  {
    System.out.printf("class : %s%n", m.group(1));
  }
  System.out.printf("key : %s, value : %s%n", m.group(2), m.group(3));
}

出力:

class : org.myobject
key : key1, value : value1
key : key2, value : value2
key : key3, value : value3

正規表現を理解するための鍵は、次の部分です(?:([^\s\[]+)\[|\G)。最初のパスでは、クラス名と角かっこが一致します。その後、\G引き継ぎ、次の試合を前の試合が終了した位置に固定します。

于 2011-07-19T04:09:46.657 に答える
2

貪欲ではないマッチングの場合は?、パターンの後に a を追加します。たとえば、.*?可能な限り少ない文字数に一致します。

于 2011-07-19T01:35:31.787 に答える