ใช้ Regex เพื่อสร้าง Strings แทนที่จะจับคู่


110

ฉันกำลังเขียนโปรแกรมอรรถประโยชน์ Java ซึ่งช่วยให้ฉันสร้างข้อมูลจำนวนมากสำหรับการทดสอบประสิทธิภาพ มันจะเจ๋งมากที่สามารถระบุ regex สำหรับ Strings เพื่อให้เครื่องกำเนิดของฉันคายสิ่งที่ตรงกับนี้ออกมา มีอะไรอบอยู่แล้วที่ฉันสามารถใช้ทำสิ่งนี้ได้หรือไม่? หรือมีห้องสมุดที่ทำให้ฉันไปที่นั่นได้มากที่สุด?

ขอบคุณ


1
นี่คือไลบรารี java ที่มีประโยชน์ซึ่งมีคุณสมบัติมากมายสำหรับการใช้ regex เพื่อสร้าง String (การสร้างแบบสุ่มสร้าง String ตามดัชนีสร้าง String ทั้งหมด .. ) ตรวจสอบได้ที่นี่
Mifmif

อีกทางเลือกหนึ่งอาจเป็นเช่นนี้
Vladislav Varslavans

คำตอบ:


42

แก้ไข: ตามที่กล่าวไว้ในความคิดเห็นมีห้องสมุดที่ Google Code เพื่อให้บรรลุสิ่งนี้: https://code.google.com/archive/p/xeger/

ดูhttps://github.com/mifmif/Generexตามที่Mifmif แนะนำ

ข้อความต้นฉบับ:

ประการแรกด้วย regexp ที่ซับซ้อนเพียงพอฉันเชื่อว่าสิ่งนี้เป็นไปไม่ได้ แต่คุณควรจะรวมบางอย่างเข้าด้วยกันสำหรับ regexps ง่ายๆ

หากคุณดูซอร์สโค้ดของคลาส java.util.regex.Pattern คุณจะเห็นว่ามันใช้การแสดงอินสแตนซ์โหนดภายใน ส่วนประกอบรูปแบบที่แตกต่างกันแต่ละส่วนมีการนำคลาสย่อยของโหนดไปใช้งาน โหนดเหล่านี้จัดเป็นต้นไม้

ด้วยการสร้างผู้เยี่ยมชมที่เดินลัดเลาะไปตามต้นไม้นี้คุณควรสามารถเรียกวิธีการกำเนิดไฟฟ้าที่มากเกินไปหรือ Builder บางชนิดที่รวมบางสิ่งเข้าด้วยกัน


2
ฉันไม่แน่ใจว่า Xeger ดีขนาดนั้น ไม่สามารถจัดการคลาสอักขระได้ [\w]มันล้มเหลวในการรับรู้ที่เรียบง่าย ดูที่บรรทัดสุดท้ายของวิกิบอกเราว่า
John Red

2
โปรดทราบว่าสิ่งเหล่านี้ขึ้นอยู่กับdk.brics.automatonดังนั้นโปรดเตรียมที่จะเพิ่มการอ้างอิง pom ของบุคคลที่สาม คนส่วนใหญ่ไม่สนใจ แต่ฉันหวังว่าจะมีอะไรที่กะทัดรัดกว่านี้
Sridhar Sarnobat

มีทางเลือกสำหรับ xeger และ generex ไม่มีข้อบกพร่องเหล่านี้และไม่ล้าสมัย โปรดเลื่อนลงไปที่คำตอบของฉัน
Vladislav Varslavans

"ประการแรกด้วย regexp ที่ซับซ้อนเพียงพอฉันเชื่อว่าสิ่งนี้เป็นไปไม่ได้" - นี่ไม่เป็นความจริงอย่างเคร่งครัด : regex ใด ๆ ที่ส่งผ่านกับบางสิ่งบางอย่างสามารถสร้างอินพุตที่ถูกต้องได้ คำอธิบาย: regexes เป็น type-3 บน Chomsky Hierarchy ซึ่งหมายความว่าสามารถแสดงเป็น FSM ได้ เมื่อก้าวผ่าน FSM แต่ละขอบจะถูกตีความเป็นกฎสำหรับอักขระถัดไปดังนั้น FSM จึงสามารถใช้เพื่อแยกวิเคราะห์หรือสร้างลำดับได้ หาก FSM มีเส้นทางไปยังเทอร์มินัลสามารถกำหนดลำดับที่ถูกต้องได้ ดังนั้นจึงเป็นเพียง "เป็นไปไม่ได้" หากไม่มีเส้นทางไปยังเทอร์มินัล (ซึ่งจะเป็น regex ที่ไร้ประโยชน์)
Lawrence Wagerfield

22

สายเกินไปที่จะช่วยเหลือผู้โพสต์ต้นฉบับ แต่อาจช่วยผู้มาใหม่ได้ Generexเป็นไลบรารี java ที่มีประโยชน์ซึ่งมีคุณสมบัติมากมายสำหรับการใช้ regexes เพื่อสร้างสตริง (การสร้างแบบสุ่มสร้างสตริงตามดัชนีสร้างสตริงทั้งหมด ... )

ตัวอย่าง:

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

20

Xeger (Java)สามารถทำได้เช่นกัน:

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

1
Xeger ทำงานได้ดี แต่ตรวจสอบให้แน่ใจว่าคุณมีโถอัตโนมัติบนเส้นทางชั้นเรียนหรือในปอม / เกรเดิ้ลของคุณ
Delicia Brummitt

5

ฉันใช้รูทของการรีดไลบรารีของตัวเองไปแล้ว (ใน c # แต่ควรจะเข้าใจง่ายสำหรับนักพัฒนา Java)

Rxrdg เริ่มต้นจากการแก้ปัญหาในการสร้างข้อมูลทดสอบสำหรับโครงการในชีวิตจริง แนวคิดพื้นฐานคือการใช้ประโยชน์จากรูปแบบการตรวจสอบความถูกต้อง (นิพจน์ทั่วไป) ที่มีอยู่เพื่อสร้างข้อมูลแบบสุ่มที่สอดคล้องกับรูปแบบดังกล่าว วิธีนี้สร้างข้อมูลสุ่มที่ถูกต้อง

การเขียนพาร์เซอร์สำหรับรูปแบบนิพจน์ทั่วไปไม่ใช่เรื่องยาก การใช้โครงสร้างไวยากรณ์นามธรรมเพื่อสร้างสตริงควรจะง่ายกว่านี้


ลิงก์ไม่ชี้ไปที่ที่เก็บอีกต่อไป ฉันจะไปกับopenhub.net/p/rxrdg วิธีแก้ปัญหาไม่ได้สร้างอย่างไร?
Veverke

4

ใน stackoverflow podcast 11:

Spolsky: ใช่ นอกจากนี้ยังมีผลิตภัณฑ์ใหม่หากคุณไม่ต้องการใช้ระบบทีมที่นั่นเพื่อนของเราที่ Redgate มีผลิตภัณฑ์ที่เรียกว่า SQL Data Generator [ http://www.red-gate.com/products/sql_data_generator/index.htm] . เป็นเงิน 295 เหรียญและสร้างข้อมูลการทดสอบที่เป็นจริง และมันทำสิ่งต่างๆเช่นสร้างเมืองจริงในคอลัมน์เมืองที่มีอยู่จริงจากนั้นเมื่อสร้างเมืองเหล่านั้นมันจะทำให้รัฐถูกต้องแทนที่จะทำให้รัฐผิดพลาดหรือใส่รัฐลงในเมืองเยอรมันและสิ่งต่างๆเช่น ... คุณรู้ไหมว่ามันสร้างข้อมูลที่ดูสมจริง ฉันไม่แน่ใจจริงๆว่าคุณสมบัติทั้งหมดคืออะไร

นี่อาจไม่ใช่สิ่งที่คุณกำลังมองหา แต่อาจเป็นจุดเริ่มต้นที่ดีแทนที่จะสร้างขึ้นมาเอง

ฉันไม่พบสิ่งใดใน Google ดังนั้นฉันขอแนะนำให้แก้ไขปัญหาโดยการแยกวิเคราะห์นิพจน์ทั่วไปที่กำหนดให้เป็นหน่วยงานที่เล็กที่สุด (\ w, [xx], \ d, ฯลฯ ) และเขียนวิธีการพื้นฐานเพื่อสนับสนุน วลีนิพจน์ทั่วไปเหล่านั้น

ดังนั้นสำหรับ \ w คุณจะมีเมธอด getRandomLetter () ซึ่งจะส่งกลับตัวอักษรแบบสุ่มใด ๆ และคุณจะมี getRandomLetter (char startLetter, char endLetter) ซึ่งจะให้ตัวอักษรสุ่มระหว่างค่าสองค่า


4

คำถามนี้เก่ามากแม้ว่าปัญหาจะเกิดขึ้นจริงสำหรับฉัน ฉันได้ลองใช้xegerและGenerexแล้วและดูเหมือนว่าจะไม่เป็นไปตามข้อกำหนดของฉัน พวกเขาล้มเหลวในการประมวลผลรูปแบบนิพจน์ทั่วไป (เช่น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

ฉันรู้ว่ามีคำตอบที่ยอมรับอยู่แล้ว แต่ฉันใช้Data Generator ของ RedGate (คำที่กล่าวถึงในคำตอบของ Craig) และใช้งานได้ดีกับทุกสิ่งที่ฉันเคยพูดไป มันรวดเร็วและทำให้ฉันอยากใช้ regex เดียวกันเพื่อสร้างข้อมูลจริงสำหรับสิ่งต่างๆเช่นรหัสการลงทะเบียนที่สิ่งนี้คายออกมา

ใช้ regex เช่น:

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

คุณอาจต้องการระบุชนิดของสตริงที่ใช้เป็นอินพุตรูปแบบ ประการแรกไม่ใช่เรื่องง่ายที่จะระบุสิ่งเหล่านี้จากซอร์สโค้ด ประการที่สองหากมีข้อผิดพลาดหรือความไม่ชัดเจนในซอร์สโค้ดไม่มีทางที่จะดูได้ว่าเจตนาหรือไม่
Maarten Bodewes

StringTokenizer เป็นคลาสดั้งเดิมที่คงไว้ด้วยเหตุผลด้านความเข้ากันได้แม้ว่าจะไม่สนับสนุนการใช้งานในโค้ดใหม่ ขอแนะนำให้ทุกคนที่กำลังมองหาฟังก์ชันนี้ใช้วิธีการแยกสตริงหรือแพ็คเกจ java.util.regex แทน
Rohit

2

คุณจะต้องเขียน parser ของคุณเองเช่นเดียวกับผู้เขียน String :: Random (Perl) ในความเป็นจริงเขาไม่ได้ใช้ regexes ที่ใดก็ได้ในโมดูลนั้นมันเป็นเพียงสิ่งที่ perl-coders ใช้

ในทางกลับกันบางทีคุณอาจดูที่มาเพื่อหาคำแนะนำ


แก้ไข: ประณามแบลร์เอาชนะฉันด้วยหมัด 15 วินาที


1

มันยังห่างไกลจากการสนับสนุน regexp PCRE เต็มรูปแบบ แต่ฉันเขียนวิธี Ruby ต่อไปนี้เพื่อรับสตริงที่เหมือน regexp และสร้างรูปแบบที่แตกต่างกัน (สำหรับ 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

คำถามนี้เก่ามาก แต่ฉันเจอคำถามนี้ในการค้นหาของตัวเองดังนั้นฉันจะรวมลิงก์สองสามลิงก์สำหรับคนอื่น ๆ ที่อาจกำลังค้นหาฟังก์ชันเดียวกันในภาษาอื่น ๆ


0

หากคุณต้องการสร้างสตริง "สำคัญ" คุณอาจต้องพิจารณา:

EGRET http://elarson.pythonanywhere.com/ ที่สร้างสตริง "ชั่วร้าย" ครอบคลุมนิพจน์ทั่วไปของคุณ

MUTREX http://cs.unibg.it/mutrex/ ที่สร้างสตริงตรวจจับข้อผิดพลาดโดยการกลายพันธุ์ของ regex

ทั้งสองเป็นเครื่องมือทางวิชาการ (ฉันเป็นหนึ่งในผู้เขียนในยุคหลัง) และทำงานได้ดีพอสมควร

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.