Dizeleri eşleştirmek yerine oluşturmak için Regex kullanma


110

Performans testi için bir sürü veri oluşturmama yardımcı olan bir Java yardımcı programı yazıyorum. Dizeler için bir normal ifade belirleyebilmek gerçekten harika olurdu, böylece jeneratörüm buna uyan şeyler çıkarır. Bunu yapmak için kullanabileceğim önceden pişirilmiş bir şey var mı? Yoksa beni oraya en çok götüren bir kütüphane var mı?

Teşekkürler


1
Burada, String'i oluşturmak için
regex'i

Başka bir alternatif de bu
Vladislav Varslavans

Yanıtlar:


42

Düzenleme: Yorumlarda belirtildiği gibi, bunu başarmak için Google Code'da bir kitaplık bulunmaktadır: https://code.google.com/archive/p/xeger/

Ayrıca Mifmif tarafından önerildiği üzere https://github.com/mifmif/Generex sayfasına bakın.

Orijinal mesaj:

İlk olarak, yeterince karmaşık bir regexp ile bunun imkansız olabileceğine inanıyorum. Ancak basit regexp'ler için bir şeyler bir araya getirebilmelisiniz.

Java.util.regex.Pattern sınıfının kaynak koduna bakarsanız, Node örneklerinin dahili bir temsilini kullandığını göreceksiniz. Farklı desen bileşenlerinin her biri, bir Düğüm alt sınıfının kendi uygulamasına sahiptir. Bu Düğümler bir ağaç halinde düzenlenmiştir.

Bu ağaçtan geçen bir ziyaretçi üreterek, aşırı yüklenmiş bir jeneratör yöntemini veya bir şeyi bir araya getiren bir tür Oluşturucu çağırabilmelisiniz.


2
Xeger'in o kadar iyi olduğundan emin değilim. Karakter sınıflarını işleyemez. Basit bir şeyi tanımakta başarısız olur [\w]. Wiki'lerinin son satırına bakıldığında bize bunu anlatır.
John Red

2
Bunlara bağlı olduğunu da unutmayın, bu dk.brics.automatonnedenle 3. taraf pom bağımlılıkları eklemeye hazır olun. Çoğu insan bunu umursamıyor ama keşke biraz daha kompakt bir şey olsaydı.
Sridhar Sarnobat

Xeger ve generex için alternatif var. Tüm bu dezavantajlardan yoksundur ve modası geçmiş değildir. Lütfen cevabıma ilerleyin.
Vladislav Varslavans

"Öncelikle, yeterince karmaşık bir normal ifade ile bunun imkansız olabileceğine inanıyorum." - bu kesinlikle doğru değildir : bir şeye karşı geçen herhangi bir normal ifade de geçerli bir girdi oluşturabilir. Açıklama: Düzenli ifadeler Chomsky Hiyerarşisinde tip-3'tür, yani FSM olarak ifade edilebilirler. Bir FSM'den geçerken, her kenar bir sonraki karakter için bir kural olarak yorumlanır, böylece dizileri ayrıştırmak veya oluşturmak için bir FSM kullanılabilir . FSM'nin terminale giden bir yolu varsa, geçerli bir sıra belirlenebilir. Bu nedenle, terminale giden bir yol yoksa (bu işe yaramaz bir normal ifade olur) yalnızca "imkansızdır".
Lawrence Wagerfield

22

Orijinal postere yardım etmek için çok geç, ancak yeni gelenlere yardımcı olabilir. Generex , dizeleri oluşturmak için normal ifadeleri kullanmak için birçok özellik sağlayan kullanışlı bir java kitaplığıdır (rastgele oluşturma, dizinine dayalı bir dize oluşturma, tüm dizeleri oluşturma ...).

Misal :

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

Açıklama

Bu yazıda bahsedilen proje, soruyu cevaplayan (Mifmif) kullanıcıya aittir. Gereğince kurallara , bu ihtiyaç kadar teslim edilmesi.


11
Görünüşe göre Generex sizin kendi projeniz. Eğer kurallara göre, bu kendi proje olduğunu yazınıza söz sakıncası var burada ?
Brian McCutchon


5

Bunun için kendi kitaplığımı yuvarlamanın köküne gittim (c # dilinde ancak bir Java geliştiricisi için anlaşılması kolay olmalı).

Rxrdg, gerçek hayattaki bir proje için test verileri oluşturma sorununa bir çözüm olarak başladı. Temel fikir, bu tür modellere uyan rastgele veriler oluşturmak için mevcut (normal ifade) doğrulama modellerinden yararlanmaktır. Bu şekilde geçerli rastgele veriler oluşturulur.

Basit düzenli ifade kalıpları için bir ayrıştırıcı yazmak o kadar da zor değil. Dizeler oluşturmak için soyut bir sözdizimi ağacı kullanmak daha da kolay olmalı.


bağlantı artık depoyu göstermiyor. Openhub.net/p/rxrdg ile giderdim . Ancak çözüm geliştirilmiyor mu?
Veverke

4

Stackoverflow podcast 11'de:

Spolsky: Evet. Ayrıca yeni bir ürün var, Ekip Sistemini kullanmak istemiyorsanız, Redgate'teki arkadaşlarımızın SQL Data Generator [ http://www.red-gate.com/products/sql_data_generator/index.htm] adlı bir ürünü var. . 295 $ 'dır ve sadece bazı gerçekçi test verileri oluşturur. Ve şehir sütununda gerçekten var olan gerçek şehirler yaratmak gibi şeyler yapıyor ve sonra bunları oluşturduğunda, eyaleti yanlış yapmak yerine ya da eyaletleri Alman şehirlerine yerleştirmek yerine doğru eyaleti alacak gibi şeyler yapıyor ... Biliyorsunuz, oldukça gerçekçi görünen veriler üretir. Tüm özelliklerin ne olduğundan gerçekten emin değilim.

Muhtemelen aradığınız şey bu değil, ancak kendinizinkini oluşturmak yerine iyi bir başlangıç ​​noktası olabilir.

Google'da hiçbir şey bulamıyorum, bu yüzden belirli bir normal ifadeyi en küçük çalışma birimlerine (\ w, [xx], \ d, vb.) Ayırarak ve destek için bazı temel yöntemler yazarak sorunu çözmenizi öneririm o normal ifade cümleleri.

Yani \ w için herhangi bir rasgele harf döndüren getRandomLetter () yönteminiz ve ayrıca size iki değer arasında rastgele bir harf veren getRandomLetter (char startLetter, char endLetter) 'e sahip olursunuz.


4

Sorun benim için gerçek olsa da, bu soru gerçekten eski. Xeger ve Generex'i denedim ve şartlarımı karşılamıyor gibi görünüyorlar. Aslında bazı normal ifade kalıplarını (gibi a{60000}) veya diğerleri için (örneğin (A|B|C|D|E|F)) işleyemezler, sadece olası tüm değerleri üretmezler. Başka uygun bir çözüm bulamadığım için kendi kitaplığımı oluşturdum.

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

Maven merkezinde de bir eser var.

Kullanım örneği:

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

3

Zaten kabul edilmiş bir cevap olduğunu biliyorum, ancak RedGate'in Veri Oluşturucusunu (Craig'in cevabında bahsedilen) kullanıyorum ve attığım her şey için GERÇEKTEN iyi çalışıyor. Hızlıdır ve bu, bu şeyin tükürdüğü kayıt kodları gibi şeyler için gerçek verileri oluşturmak üzere aynı normal ifadeyi kullanmak istememe neden olur.

Şunun gibi bir normal ifade gerekir:

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

ve aşağıdakiler gibi tonlarca benzersiz kod üretir:

LLK-32U

Bu RedGate'in bulduğu ve hepimizin şanssız olduğu büyük bir gizli algoritma mı yoksa sadece ölümlülerin gerçekten yapabileceği bir şey mi?


3

Uçuş halindeyim ve şu soruyu gördüm: En kolay ama verimsiz ve eksik çözümü yazdım. Umarım kendi ayrıştırıcınızı yazmaya başlamanıza yardımcı olabilir:

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

Desen girişi olarak ne tür dizelerin kullanıldığını belirtmek isteyebilirsiniz. Her şeyden önce, bu tür şeyleri kaynak koddan belirlemek o kadar da kolay değil. İkincisi, kaynak kodda herhangi bir hata veya belirsizlik varsa, bunların kasıtlı olup olmadıklarını görmenin bir yolu yoktur.
Maarten Bodewes

StringTokenizer, yeni kodda kullanılması tavsiye edilmese de uyumluluk nedenleriyle tutulan eski bir sınıftır. Bu işlevi arayan herkesin bunun yerine String'in split yöntemini veya java.util.regex paketini kullanması önerilir.
Rohit

2

String :: Random (Perl) yazarının yaptığı gibi kendi ayrıştırıcınızı yazmanız gerekecek. Aslında, o modülün hiçbir yerinde regex kullanmıyor, sadece perl-kodlayıcıların alışkın olduğu şey bu.

Öte yandan, bazı ipuçları almak için kaynağa da bakabilirsiniz .


DÜZENLEME: Lanet olsun, Blair beni 15 saniye kadar yumrukladı.


1

Tam bir PCRE regexp'i desteklemekten uzaktır, ancak aşağıdaki Ruby yöntemini regexp benzeri bir dizge alıp üzerinde bir varyasyon oluşturmak için yazdım. (Dil tabanlı CAPTCHA için.)

# 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


0

"Kritik" dizeler oluşturmak istiyorsanız, şunları göz önünde bulundurabilirsiniz:

Normal ifadelerinizi kapsayan "kötü" dizeler oluşturan http://elarson.pythonanywhere.com/ EGRET

MUTREX http://cs.unibg.it/mutrex/ , normal ifade mutasyonu ile hata tespit eden dizeler oluşturur

Her ikisi de akademik araçlardır (ben ikincisinin yazarlarından biriyim) ve oldukça iyi çalışıyor.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.