Menggunakan Regex untuk menghasilkan String daripada mencocokkannya


108

Saya menulis utilitas Java yang membantu saya menghasilkan banyak data untuk pengujian kinerja. Akan sangat keren untuk dapat menentukan regex untuk Strings sehingga generator saya mengeluarkan hal-hal yang cocok dengan ini. Apakah ada sesuatu di luar sana yang sudah dipanggang yang dapat saya gunakan untuk melakukan ini? Atau apakah ada perpustakaan yang membuat saya hampir sepanjang perjalanan ke sana?

Terima kasih


1
Berikut adalah pustaka java berguna yang menyediakan banyak fitur untuk menggunakan regex untuk menghasilkan String (pembuatan acak, buat String berdasarkan indeksnya, buat semua String ..) lihat di sini
Mifmif

Alternatif lain adalah ini
Vladislav Varslavans

Jawaban:


40

Sunting: Seperti yang disebutkan di komentar, ada perpustakaan yang tersedia di Google Code untuk mencapai ini: https://code.google.com/archive/p/xeger/

Lihat juga https://github.com/mifmif/Generex seperti yang disarankan oleh Mifmif

Pesan asli:

Pertama, dengan regexp yang cukup kompleks, saya yakin ini tidak mungkin. Tapi Anda harus bisa menggabungkan sesuatu untuk ekspresi reguler.

Jika Anda melihat kode sumber kelas java.util.regex.Pattern, Anda akan melihat bahwa ia menggunakan representasi internal dari instance Node. Setiap komponen pola yang berbeda memiliki implementasi subkelas Node mereka sendiri. Node-node ini disusun menjadi sebuah pohon.

Dengan menghasilkan pengunjung yang melintasi pohon ini, Anda seharusnya dapat memanggil metode generator yang kelebihan beban atau semacam Builder yang merakit sesuatu bersama-sama.


2
Saya tidak yakin Xeger sebagus itu. Itu tidak dapat menangani kelas karakter. Gagal mengenali yang sederhana [\w]. Sebuah tampilan di baris terakhir wiki mereka memberi tahu kita hal itu.
John Red

2
Perhatikan juga bahwa ini bergantung pada dk.brics.automatonjadi bersiaplah untuk menambahkan dependensi pom pihak ketiga. Kebanyakan orang tidak keberatan tapi saya berharap ada sesuatu yang lebih kompak.
Sridhar Sarnobat

Ada alternatif untuk xeger dan generex. Itu tidak memiliki semua kekurangan ini dan tidak usang. Silakan gulir ke bawah ke jawaban saya.
Vladislav Varslavans

"Pertama, dengan regexp yang cukup kompleks, saya yakin ini tidak mungkin." - ini tidak sepenuhnya benar : regex apa pun yang meneruskan sesuatu juga dapat menghasilkan input yang valid. Penjelasan: regex adalah tipe-3 pada Chomsky Hierarchy, yang berarti dapat diekspresikan sebagai FSM. Saat melangkah melalui FSM, setiap tepi diinterpretasikan sebagai aturan untuk karakter berikutnya, sehingga FSM dapat digunakan untuk mengurai atau menghasilkan urutan. Jika FSM memiliki jalur ke terminal, urutan yang valid dapat ditentukan. Jadi, ini hanya "tidak mungkin" jika tidak ada jalur ke terminal (yang merupakan regex yang tidak berguna).
Lawrence Wagerfield

22

Sudah terlambat untuk membantu pengepos asli, tapi bisa membantu pendatang baru. Generex adalah pustaka java berguna yang menyediakan banyak fitur untuk menggunakan regex untuk menghasilkan string (pembuatan acak, menghasilkan string berdasarkan indeksnya, menghasilkan semua string ...).

Contoh:

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

Penyingkapan

Proyek yang disebutkan di posting ini milik pengguna yang menjawab (Mifmif) pertanyaan itu. Sesuai aturan , ini perlu diangkat.


11
Sepertinya Generex adalah proyek Anda sendiri. Maukah Anda menyebutkan dalam posting Anda bahwa ini adalah proyek Anda sendiri, sesuai aturan di sini ?
Brian McCutchon

20

Xeger (Java) mampu melakukannya juga:

String regex = "[ab]{4,6}c";
Xeger generator = new Xeger(regex);
String result = generator.generate();
assert result.matches(regex);

1
Xeger bekerja dengan baik. TAPI pastikan Anda memiliki toples otomat di jalur kelas atau di pom / gradle Anda
Delicia Brummitt

5

Saya telah melakukan root untuk meluncurkan perpustakaan saya sendiri untuk itu (Dalam c # tetapi harus mudah dipahami untuk pengembang Java).

Rxrdg dimulai sebagai solusi untuk masalah pembuatan data uji untuk proyek kehidupan nyata. Ide dasarnya adalah untuk memanfaatkan pola validasi (ekspresi reguler) yang ada untuk membuat data acak yang sesuai dengan pola tersebut. Dengan cara ini data acak yang valid dibuat.

Tidaklah sulit untuk menulis parser untuk pola regex sederhana. Menggunakan pohon sintaksis abstrak untuk menghasilkan string seharusnya lebih mudah.


link tidak mengarah ke repositori lagi. Saya akan menggunakan openhub.net/p/rxrdg . Namun, solusinya tidak membangun?
Veverke

4

Di podcast stackoverflow 11:

Spolsky: Ya. Ada produk baru juga, jika Anda tidak ingin menggunakan Sistem Tim, teman-teman kami di Redgate memiliki produk yang disebut SQL Data Generator [ http://www.red-gate.com/products/sql_data_generator/index.htm] . Ini $ 295, dan itu hanya menghasilkan beberapa data uji realistis. Dan itu melakukan hal-hal seperti benar-benar menghasilkan kota nyata di kolom kota yang benar-benar ada, dan kemudian ketika menghasilkan kota itu akan membuat negara bagiannya benar, alih-alih membuat negara bagian salah, atau menempatkan negara bagian menjadi kota-kota Jerman dan hal-hal seperti ... Anda tahu, ini menghasilkan data yang tampak cukup realistis. Saya tidak begitu yakin apa semua fiturnya.

Ini mungkin bukan yang Anda cari, tetapi mungkin ini titik awal yang baik, daripada membuatnya sendiri.

Saya tidak dapat menemukan apa pun di Google, jadi saya sarankan untuk mengatasi masalah ini dengan mengurai ekspresi reguler yang diberikan ke dalam unit kerja terkecil (\ w, [xx], \ d, dll) dan menulis beberapa metode dasar untuk mendukung frasa ekspresi reguler tersebut.

Jadi untuk \ w Anda akan memiliki metode getRandomLetter () yang mengembalikan huruf acak apa pun, dan Anda juga akan memiliki getRandomLetter (char startLetter, char endLetter) yang memberi Anda huruf acak di antara dua nilai.


4

Pertanyaan ini sangat tua, meskipun masalahnya sebenarnya bagi saya. Saya sudah mencoba xeger dan Generex dan sepertinya tidak sesuai dengan ketentuan saya. Mereka sebenarnya gagal memproses beberapa pola regex (seperti a{60000}) atau untuk orang lain (misalnya (A|B|C|D|E|F)) mereka tidak menghasilkan semua nilai yang mungkin. Karena saya tidak menemukan solusi lain yang sesuai - saya telah membuat perpustakaan saya sendiri.

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

Ada juga artefak di pusat maven yang tersedia.

Contoh penggunaan:

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

3

Saya tahu sudah ada jawaban yang diterima, tetapi saya telah menggunakan RedGate's Data Generator (yang disebutkan dalam jawaban Craig) dan ini bekerja SANGAT baik untuk semua yang saya berikan. Ini cepat dan itu membuat saya ingin menggunakan regex yang sama untuk menghasilkan data nyata untuk hal-hal seperti kode registrasi yang dikeluarkan oleh benda ini.

Dibutuhkan regex seperti:

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

dan menghasilkan banyak kode unik seperti:

LLK-32U

Apakah ini beberapa algoritme rahasia besar yang diketahui RedGate dan kita semua kurang beruntung atau apakah itu sesuatu yang sebenarnya bisa dilakukan oleh kita yang hanya manusia biasa?


3

Saya sedang dalam penerbangan dan baru melihat pertanyaan: Saya telah menulis solusi yang paling mudah tetapi tidak efisien dan tidak lengkap. Saya harap ini dapat membantu Anda untuk mulai menulis parser Anda sendiri:

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);
}

Anda mungkin ingin menunjukkan jenis string yang digunakan sebagai input pola. Pertama-tama, tidak mudah untuk menentukan hal-hal seperti itu dari kode sumber. Kedua, jika ada kesalahan atau ketidakjelasan dalam kode sumber, tidak ada cara untuk melihat apakah itu disengaja atau tidak.
Maarten Bodewes

StringTokenizer adalah kelas lama yang dipertahankan untuk alasan kompatibilitas meskipun penggunaannya tidak disarankan dalam kode baru. Direkomendasikan bahwa siapa pun yang mencari fungsionalitas ini menggunakan metode split String atau paket java.util.regex sebagai gantinya.
Rohit

2

Anda harus menulis parser Anda sendiri, seperti yang dilakukan oleh penulis String :: Random (Perl). Faktanya, dia tidak menggunakan regex di mana pun di modul itu, hanya itu yang digunakan oleh pembuat kode perl.

Di sisi lain, mungkin Anda bisa melihat sumbernya , untuk mendapatkan beberapa petunjuk.


EDIT: Sial, blair mengalahkanku dengan 15 detik.


1

Ini jauh dari mendukung regexp PCRE penuh, tetapi saya menulis metode Ruby berikut untuk mengambil string seperti regexp dan menghasilkan variasi di atasnya. (Untuk CAPTCHA berbasis bahasa.)

# 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


Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.