2010-06-28 6 views
5

Ich möchte eine Zeichenfolge eingeben und einen regulären Ausdruck zurückgeben, mit dem die Struktur der Zeichenfolge beschrieben werden kann. Die Regex wird verwendet, um mehr Strings der gleichen Struktur wie die erste zu finden.Programmgesteuert leiten Sie einen regulären Ausdruck von einer Zeichenfolge ab

Dies ist absichtlich mehrdeutig, weil ich sicherlich einen Fall vermissen werde, dass jemand in der SO-Community fangen wird.

Bitte posten Sie alle möglichen Möglichkeiten, dies zu tun.

+4

Der Mensch kann nicht einmal dies, dass auch alle tun (nur auf einige schauen der schrecklich präsentiert regex Fragen auf Stackoverflow) und wir soll gut in der Erkennung von Mustern sein. Ich bezweifle, dass dies möglich ist, es sei denn, Sie haben eine ziemlich anspruchsvolle KI mit einem großen Trainingsdatensatz. Es kann jedoch kaum etwas Nützliches aus nur einer Eingabestring kommen. – polygenelubricants

+0

@polygeneLubricants Die Idee besteht darin, diese Muster beim Training der KI zu verwenden. – smothers

Antwort

12

Die triviale Antwort, und wahrscheinlich nicht was Sie wollen, ist: geben Sie die Eingabezeichenfolge zurück (mit Regex-Sonderzeichen maskiert). Dies ist immer ein regulärer Ausdruck, der der Zeichenfolge entspricht.

Wenn Sie möchten, dass bestimmte Strukturen identifiziert werden, müssen Sie mehr Informationen über die Art von Strukturen angeben, die Sie identifizieren möchten. Ohne diese Information wird das Problem auf zweideutige Weise angegeben und es gibt viele mögliche Lösungen. Zum Beispiel kann die Eingabezeichenfolge 'aba' als

beschrieben

'aba'

'aba *'

'aba?'

'ab \ w'

'\ w {3}'

'(.) B \ 1'

usw.

+8

Noch schlimmer, die Eingabe '' aba' 'kann am einfachsten wie '/.*/' beschrieben werden - wie auch jede andere Zeichenkette. – Chuck

+0

@Chuck - außer '" \ n "'. Du meintest wahrscheinlich '/.*/ s'. – Adrian

+0

@Adrian: Oder nur '//' –

5

Sorry für die Länge dieses. Ich nahm die Prämisse davon als eine kleine Herausforderung und kam mit einem Proof of Concept in Ruby.

Ich ging davon aus, dass Sie eine Anzahl von Zeichenfolgen liefern können, die mit dem regulären Ausdruck (HITS) übereinstimmen sollten und eine Zahl, die nicht übereinstimmen sollte (MISSES).

Ich basierte den Code auf einer naiven Implementierung eines genetischen Algorithmus. In den Anmerkungen unten finden Sie meine Gedanken über den Erfolg dieses Konzepts.

LOOP_COUNT = 100 

class Attempt 

    # let's try email 
    HITS = %w[[email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected]] 
    MISSES = %w[[email protected] [email protected]@.com j.com @domain.com nochance [email protected] [email protected] username-at-domain-dot-com linux.org eff.org microsoft.com sjobs.apple.com www.apple.com] 

    # odd mixture of numbers and letters, designed to confuse 
    # HITS = %w[a123 a999 a600 a545 a100 b001 b847 a928 c203] 
    # MISSES = %w[abc def ghi jkl mno pqr stu vwx xyz h234 k987] 

    # consonants versus vowels 
    # HITS = %w[bcd cdb fgh ghf jkl klj mnp npm qrs srq tvw vwt xzb bzx] 
    # MISSES = %w[aei oyu oio euu uio ioe aee ooo] 

    # letters < 11 chars and no numbers 
    # HITS = %w[aaa aaaa abaa azaz monkey longstring stringlong] 
    # MISSES = %w[aa aa1 aa0 b9b 6zz longstringz m_m ff5 666 anotherlongstring] 

    MAX_SUCCESSES = HITS.size + MISSES.size 

    # Setup the various Regular Expression operators, etc.. 
    RELEMENTS = %w[. ? * + () \[ \] - |^$ \\ : @/{ }] 
    %w[A b B d D S s W w z Z].each do |chr| 
    RELEMENTS << "\\#{chr}" 
    end 
    %w[alnum alpha blank cntrl digit lower print punct space upper xdigit].each do |special| 
    RELEMENTS << "[:#{special}:]" 
    end 
    ('a'..'z').each do |chr| 
    RELEMENTS << chr 
    end 
    ('A'..'Z').each do |chr| 
    RELEMENTS << chr 
    end 
    (0..9).each do |chr| 
    RELEMENTS << chr.to_s 
    end 

    START_SIZE = 8 

    attr_accessor :operators, :successes 

    def initialize(ary = []) 
    @operators = ary 
    if ary.length < 1 
     START_SIZE.times do 
     @operators << random_op 
     end 
    end 
    @score = 0 
    @decay = 1 
    make_regexp 
    end 

    def make_regexp 
    begin 
     @regexp = Regexp.new(@operators.join("")) 
    rescue 
     # "INVALID Regexp" 
     @regexp = nil 
     @score = -1000 
    end 
    end 

    def random_op 
    RELEMENTS[rand(RELEMENTS.size)] 
    end 

    def decay 
    @decay -= 1 
    end 

    def test 
    @successes = 0 
    if @regexp 
     HITS.each do |hit| 
     result = (hit =~ @regexp) 
     if result != nil 
      reward 
     end 
     end 
     MISSES.each do |miss| 
     result = (miss =~ @regexp) 
     if result == nil 
      reward 
     end 
     end 
    end 
    @score = @successes 
    self 
    end 

    def reward 
    @successes += 1 
    end 

    def cross other 
    len = size 
    olen = other.size 
    split = rand(len) 
    ops = [] 
    @operators.length.times do |num| 
     if num < split 
     ops << @operators[num] 
     else 
     ops << other.operators[num + (olen - len)] 
     end 
    end 
    Attempt.new ops 
    end 

    # apply a random mutation, you don't have to use all of them 
    def mutate 
    send [:flip, :add_rand, :add_first, :add_last, :sub_rand, :sub_first, :sub_last, :swap][rand(8)] 
    make_regexp 
    self 
    end 

    ## mutate methods 
    def flip 
    @operators[rand(size)] = random_op 
    end 
    def add_rand 
    @operators.insert rand(size), random_op 
    end 
    def add_first 
    @operators.insert 0, random_op 
    end 
    def add_last 
    @operators << random_op 
    end 
    def sub_rand 
    @operators.delete_at rand(size) 
    end 
    def sub_first 
    @operators.delete_at 0 
    end 
    def sub_last 
    @operators.delete_at size 
    end 
    def swap 
    to = rand(size) 
    begin 
     from = rand(size) 
    end while to == from 
    @operators[to], @operators[from] = @operators[from], @operators[to] 
    end 

    def regexp_to_s 
    @operators.join("") 
    end 

    def <=> other 
    score <=> other.score 
    end 

    def size 
    @operators.length 
    end 

    def to_s 
    "#{regexp_to_s} #{score}" 
    end 

    def dup 
    Attempt.new @operators.dup 
    end 

    def score 
    if @score > 0 
     ret = case 
     when (size > START_SIZE * 2) 
     @score-20 
     when size > START_SIZE 
     @score-2 
     else 
     @score #+ START_SIZE - size 
     end 
     ret + @decay 
    else 
     @score + @decay 
    end 
    end 

    def == other 
    to_s == other.to_s 
    end 

    def stats 
    puts "Regexp #{@regexp.inspect}" 
    puts "Length #{@operators.length}" 
    puts "Successes #{@successes}/#{MAX_SUCCESSES}" 
    puts "HITS" 
    HITS.each do |hit| 
     result = (hit =~ @regexp) 
     if result == nil 
     puts "\tFAIL #{hit}" 
     else 
     puts "\tOK #{hit} #{result}" 
     end 
    end 
    puts "MISSES" 
    MISSES.each do |miss| 
     result = (miss =~ @regexp) 
     if result == nil 
      puts "\tOK #{miss}" 
     else 
      puts "\tFAIL #{miss} #{result}" 
     end 
    end 
    end 

end 

$stderr.reopen("/dev/null", "w") # turn off stderr to stop streams of bad rexexp messages 

# find some seed attempt values 
results = [] 
10000.times do 
    a = Attempt.new 
    a.test 
    if a.score > 0 
    # puts "#{a.regexp_to_s} #{a.score}" 
    results << a 
    end 
end 

results.sort!.reverse! 

puts "SEED ATTEMPTS" 
puts results[0..9] 

old_result = nil 

LOOP_COUNT.times do |i| 
    results = results[0..9] 
    results.map {|r| r.decay } 
    3.times do 
    new_results = results.map {|r| r.dup.mutate.test} 
    results.concat new_results 
    new_results = results.map {|r| r.cross(results[rand(10)]).test } 
    results.concat new_results 
    end 
    new_results = [] 
    20.times do 
    new_results << Attempt.new.test 
    end 
    results.concat new_results 
    results.sort!.reverse! 
    if old_result != results[0].score 
    old_result = results[0].score 
    end 
    puts "#{i} #{results[0]}" 
end 
puts "\n--------------------------------------------------" 
puts "Winner! #{results[0]}" 
puts "--------------------------------------------------\n" 
results[0].stats 

Lessons vom Spielen mit diesem Code gelernt.

Insgesamt scheint es, dass das Ausführen kürzerer Schleifen mehrere Male höchstwahrscheinlich zu einem brauchbaren Ergebnis führt. Dies könnte jedoch eher auf meine Implementierung als auf die Natur genetischer Algorithmen zurückzuführen sein.

Sie erhalten Ergebnisse, die funktionieren, aber immer noch Teile enthalten, die Kauderwelsch sind.

Sie werden ein ziemlich festes Verständnis von regulären Ausdrücken benötigen, um zu verstehen, wie viele der Ergebnisse tatsächlich erreichen, was sie tun.

Letztendlich ist es wahrscheinlich besser, reguläre Ausdrücke zu lernen, als zu versuchen, diesen Code als Abkürzung zu verwenden. Ich weiß, dass der Fragesteller dieses Motiv vielleicht nicht hatte und der Grund, warum ich das versuchte, war, dass es eine interessante Idee war.

Es gibt viele Kompromisse in den Ergebnissen. Je vielfältiger HITS und MISSES Sie liefern, desto länger dauert es, ein Ergebnis zu produzieren und desto mehr Loops müssen Sie durchlaufen.Wenn Sie weniger davon haben, wird wahrscheinlich ein Ergebnis erzeugt, das entweder massiv für Ihre gelieferten Strings oder so allgemein ist, dass es in einer realen Situation nicht nützlich wäre.

Ich habe auch einige Annahmen festgeschrieben, wie zum Beispiel die Markierung von Ausdrücken, die zu lang werden.

+0

Sehr schlau. Ich wollte ein Werkzeug, das einen regulären Ausdruck erzeugen kann, um die Unterschiede in den Eingaben zu beschreiben, aber nie weiter als Confusion's erste Antwort "return the string". Wie du, ich bin mir nicht sicher, ob das Tool es wirklich wert ist, aber trotzdem hat es Spaß gemacht, es zu lesen. :) – sarnold

0

import hat gerade ein kostenloses Tool zum Ableiten von Regex-Mustern aus Sätzen von Beispielstrings freigegeben: "Geben Sie Beispiele für Daten, die Sie herausziehen möchten, und es wird programmgesteuert einen regulären Ausdruck generieren und testen."

https://regexpgen.import.io/

Sein freies aber einen Login benötigen, es zu benutzen.

enter image description here

Disclaimer: Ich an import.io arbeiten (das ist, wie ich weiß, dass diese existiert)

0

Diese site tatsächlich können Sie einen regulären Ausdruck aus dem Beispieltext erzeugen. Sie wählen den Teil einer Textzeichenfolge aus, für den der reguläre Ausdruck verwendet werden soll, und er generiert ihn für Sie in der Sprache Ihrer Wahl.

Werfen Sie einen Blick auf sie, hat es ein Beispiel in seiner FAQ erklärt - https://txt2re.com/index.php3?d=faq

Verwandte Themen