正規表現を使用して文字列を一致させるのではなく生成する


107

パフォーマンステスト用にデータのロードを生成するのに役立つJavaユーティリティを書いています。Stringsの正規表現を指定して、これに一致するものをジェネレーターが出力できるようにすることは、本当に素晴らしいことです。これを行うために使用できる焼きたての物はありますか?それとも私をそこまでのほとんどの道に導いてくれる図書館はありますか?

ありがとう


1
ここでは便利であるJavaライブラリそれをチェックアウト(..すべての文字列を生成し、ランダム生成、それのインデックスに基づいて文字列を生成する)文字列を生成するために正規表現を使用するための多くの機能を提供し、ここで
Mifmif

回答:


40

編集: コメントで述べたように、これを達成するためにGoogle Codeで利用可能なライブラリがあります:https : //code.google.com/archive/p/xeger/

Mifmifによって提案されたhttps://github.com/mifmif/Generexも参照してください。

オリジナルメッセージ:

まず、十分に複雑な正規表現では、これは不可能であると私は思います。しかし、あなたは単純な正規表現のために何かを組み合わせることができるはずです。

クラスjava.util.regex.Patternのソースコードを見ると、Nodeインスタンスの内部表現を使用していることがわかります。異なるパターンコンポーネントにはそれぞれ、Nodeサブクラスの独自の実装があります。これらのノードはツリーに編成されます。

このツリーをトラバースするビジターを作成することにより、オーバーロードされたジェネレーターメソッド、または何かをまとめる何らかのビルダーを呼び出すことができるはずです。


2
Xegerがそれほど優れているかどうかはわかりません。文字クラスは扱えません。単純なものを認識できません[\w]彼らのウィキの最後の行を見ると、それがわかります
ジョンレッド

2
また、これらは依存しているdk.brics.automatonので、サードパーティのpom依存関係を追加する準備をしてください。ほとんどの人はそれを気にしませんが、もう少しコンパクトなものがあったらいいのにと思います。
Sridhar Sarnobat

xegerとgenerexの代替手段があります。これらの欠点がすべてなく、時代遅れではありません。私の答えまでスクロールしてください。
Vladislav Varslavans

「最初に、十分に複雑な正規表現では、これは不可能であると私は信じています。」-これは厳密には当てはまりません何かに対してパスする正規表現も有効な入力を生成できます。説明:正規表現はチョムスキー階層のタイプ3です。つまり、正規表現はFSMとして表現できます。FSMをステップ実行するとき、各エッジは次の文字のルールとして解釈されるため、FSMを使用してシーケンスを解析または生成できます。FSMに端末へのパスがある場合、有効なシーケンスを判別できます。したがって、ターミナルへのパスがない場合(これは役に立たない正規表現になります)は「不可能」です。
ローレンスウェーガーフィールド

22

元のポスターを助けるには遅すぎますが、新人を助けることができます。Generexは、正規表現を使用して文字列を生成するための多くの機能を提供する便利なJavaライブラリです(ランダム生成、インデックスに基づいて文字列を生成、すべての文字列を生成...)。

例:

Generex generex = new Generex("[0-3]([a-c]|[e-g]{1,2})");

// generate the second String in lexicographical order that matches the given Regex.
String secondString = generex.getMatchedString(2);
System.out.println(secondString);// it print '0b'

// Generate all String that matches the given Regex.
List<String> matchedStrs = generex.getAllMatchedStrings();

// Using Generex iterator
Iterator iterator = generex.iterator();
while (iterator.hasNext()) {
    System.out.print(iterator.next() + " ");
}
// it prints 0a 0b 0c 0e 0ee 0e 0e 0f 0fe 0f 0f 0g 0ge 0g 0g 1a 1b 1c 1e
// 1ee 1e 1e 1f 1fe 1f 1f 1g 1ge 1g 1g 2a 2b 2c 2e 2ee 2e 2e 2f 2fe 2f 2f 2g
// 2ge 2g 2g 3a 3b 3c 3e 3ee 3e 3e 3f 3fe 3f 3f 3g 3ge 3g 3g 1ee

// Generate random String
String randomStr = generex.random();
System.out.println(randomStr);// a random value from the previous String list

開示

この投稿で言及されているプロジェクトは、質問に答えるユーザー(Mifmif)のものです。あたりとしてのルール、この必要が育てられます。


11
Generexはあなた自身のプロジェクトのようです。ここのルールに従って、これはあなた自身のプロジェクトであることを投稿で言及してもらえますか?
Brian McCutchon、2016年


5

そのために自分ライブラリをローリングするルートに行きました(C#ではJava開発者にとって理解しやすいはずです)。

Rxrdgは、実際のプロジェクトのテストデータを作成する問題の解決策として始まりました。基本的な考え方は、既存の(正規表現)検証パターンを活用して、そのようなパターンに適合するランダムデータを作成することです。このようにして、有効なランダムデータが作成されます。

単純な正規表現パターンのパーサーを作成することはそれほど難しくありません。抽象構文ツリーを使用して文字列を生成すると、さらに簡単になります。


リンクはリポジトリを指していません。私はopenhub.net/p/rxrdgを使用します。しかし、ソリューションは構築されませんか?
Veverke 2015

4

Stackoverflowポッドキャスト11:

スポルスキー:うん。新製品もあります。チームシステムを使用したくない場合は、Redgateの友人がSQLデータジェネレータと呼ばれる製品[ http://www.red-gate.com/products/sql_data_generator/index.htm]を使用しています。。295ドルで、現実的なテストデータを生成するだけです。そして、実際に存在する都市列に実際の都市を実際に生成するようなものを行い、それらを生成すると、状態を間違ったり、状態をドイツの都市に入れたりするのではなく、正しい状態になります...ご存知のように、かなりリアルなデータを生成します。私はすべての機能が何であるか本当にわかりません。

これはおそらくあなたが探しているものではありませんが、独自のものを作成するのではなく、出発点としては良いかもしれません。

私はグーグルで何かを見つけることができないようですので、与えられた正規表現を最小の作業単位(\ w、[xx]、\ dなど)に解析し、サポートするいくつかの基本的なメソッドを書くことで問題に取り組むことをお勧めしますそれらの正規表現フレーズ。

したがって、\ wの場合、ランダムな文字を返すメソッドgetRandomLetter()があり、2つの値の間にランダムな文字を与えるgetRandomLetter(char startLetter、char endLetter)もあります。


4

この質問は本当に古いですが、問題は私にとって実際的なものでした。私はxegerGenerexを試してみましたが、私の規制に適合していないようです。それらは実際に一部の正規表現パターン(などa{60000})の処理に失敗するか、その他(例(A|B|C|D|E|F))の場合、すべての可能な値を生成しません。他に適切な解決策が見つからなかったため、自分でライブラリを作成しました。

https://github.com/curious-odd-man/RgxGen

Maven Centralにもアーティファクトがあります。

使用例:

RgxGen rgxGen = new RgxGen(aRegex);                     // Create generator
String s = rgxGen.generate();                           // Generate new random value

3

私はすでに受け入れられた回答があることを知っていますが、私はRedGateのデータジェネレーター(Craigの回答で言及されたもの)を使用しており、私が投げたすべてのものに対して本当にうまく機能します。それは迅速で、同じことを使用して、登録コードなどが出力する実際のデータを生成したいと思っています。

それは次のような正規表現を取ります:

[A-Z0-9]{3,3}-[A-Z0-9]{3,3}

そしてそれは次のようなユニークなコードのトンを生成します:

LLK-32U

これは、RedGateが考え出した大きな秘密のアルゴリズムですか?


3

私は機内にいますが、質問を見たところです。最も簡単だが非効率的で不完全な解決策を書きました。あなた自身のパーサーを書き始めるのに役立つことを願っています:

public static void main(String[] args) {

    String line = "[A-Z0-9]{16}";
    String[] tokens = line.split(line);
    char[] pattern = new char[100];
    int i = 0;
    int len = tokens.length;
    String sep1 = "[{";
    StringTokenizer st = new StringTokenizer(line, sep1);

    while (st.hasMoreTokens()) {
        String token = st.nextToken();
        System.out.println(token);

        if (token.contains("]")) {
            char[] endStr = null;

            if (!token.endsWith("]")) {
                String[] subTokens = token.split("]");
                token = subTokens[0];

                if (!subTokens[1].equalsIgnoreCase("*")) {
                    endStr = subTokens[1].toCharArray();
                }
            }

            if (token.startsWith("^")) {
                String subStr = token.substring(1, token.length() - 1);
                char[] subChar = subStr.toCharArray();
                Set set = new HashSet<Character>();

                for (int p = 0; p < subChar.length; p++) {
                    set.add(subChar[p]);
                }

                int asci = 1;

                while (true) {
                    char newChar = (char) (subChar[0] + (asci++));

                    if (!set.contains(newChar)) {
                        pattern[i++] = newChar;
                        break;
                    }
                }
                if (endStr != null) {
                    for (int r = 0; r < endStr.length; r++) {
                        pattern[i++] = endStr[r];
                    }
                }

            } else {
                pattern[i++] = token.charAt(0);
            }
        } else if (token.contains("}")) {
            char[] endStr = null;

            if (!token.endsWith("}")) {
                String[] subTokens = token.split("}");
                token = subTokens[0];

                if (!subTokens[1].equalsIgnoreCase("*")) {
                    endStr = subTokens[1].toCharArray();
                }
            }

            int length = Integer.parseInt((new StringTokenizer(token, (",}"))).nextToken());
            char element = pattern[i - 1];

            for (int j = 0; j < length - 1; j++) {
                pattern[i++] = element;
            }

            if (endStr != null) {
                for (int r = 0; r < endStr.length; r++) {
                    pattern[i++] = endStr[r];
                }
            }
        } else {
            char[] temp = token.toCharArray();

            for (int q = 0; q < temp.length; q++) {
                pattern[i++] = temp[q];
            }
        }
    }

    String result = "";

    for (int j = 0; j < i; j++) {
        result += pattern[j];
    }

    System.out.print(result);
}

パターン入力として使用される文字列の種類を示すことができます。まず第一に、ソースコードからそのようなことを決定することはそれほど簡単ではありません。第2に、ソースコードに間違いや不明瞭な点がある場合、それらが意図的なものかどうかを確認する方法はありません。
Maarten Bodewes

StringTokenizerはレガシークラスであり、新しいコードではその使用は推奨されませんが、互換性の理由で保持されています。この機能を求める人は、代わりにStringのsplitメソッドまたはjava.util.regexパッケージを使用することをお勧めします。
Rohit

2

String :: Random(Perl)の作者が作成したように、独自のパーサーを作成する必要があります。実際、彼はそのモジュールのどこでも正規表現を使用していません。これは、perl-codersが慣れていることです。

一方、ソースを確認してポインタを取得することもできます。


編集:くそー、ブレアは15秒でパンチに私を打ちました。


1

完全なPCRE正規表現のサポートにはほど遠いですが、正規表現のような文字列を取得して、そのバリエーションを生成するために、次のRubyメソッドを作成しました。(言語ベースのCAPTCHAの場合。)

# q = "(How (much|many)|What) is (the (value|result) of)? :num1 :op :num2?"
# values = { :num1=>42, :op=>"plus", :num2=>17 }
# 4.times{ puts q.variation( values ) }
# => What is 42 plus 17?
# => How many is the result of 42 plus 17?
# => What is the result of 42 plus 17?
# => How much is the value of 42 plus 17?
class String
  def variation( values={} )
    out = self.dup
    while out.gsub!( /\(([^())?]+)\)(\?)?/ ){
      ( $2 && ( rand > 0.5 ) ) ? '' : $1.split( '|' ).random
    }; end
    out.gsub!( /:(#{values.keys.join('|')})\b/ ){ values[$1.intern] }
    out.gsub!( /\s{2,}/, ' ' )
    out
  end
end

class Array
  def random
    self[ rand( self.length ) ]
  end
end

1

この質問は非常に古いですが、自分の検索で偶然見つけたので、他の言語で同じ機能を検索している可能性のある他の人へのリンクをいくつか含めます。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.