2017-10-05 1 views
0

Ich habe SQL-Codes und möchte den Tabellennamen nach dem Schlüsselwort "insert" extrahieren.Python: Wie finden Sie alle Übereinstimmungen in einem mehrzeiligen String, aber nicht von einem bestimmten Wort weiter?

Grundsätzlich würde ich mit den folgenden Regeln extrahieren mag:

  1. Enthält das Wort „Einfügen“
  2. gefolgt von dem Wort „in“, die
  3. optional ausschließen, wenn das ein Thema " - "(das ist ein einzeiliger Kommentar in SQL) irgendwo vor dem Einfügen in (optional) Schlüsselwort.
  4. Ausschließen, wenn Einfügen in (optional) Schlüsselwort ist zwischen "/ *" und "* /" (was mehrzeiliger Kommentar in SQL ist).
  5. Holen Sie sich das nächste Wort (table_name) nach Einsatz in (optional) Stichwort

Beispiel:

import re 

lines = """begin insert into table_1 end 
    begin insert table_2 end 
    select 1 --This is will not insert into table_3 
    begin insert into 
     table_4 
    end 
    /* this is a comment 
    insert into table_5 
    */ 
    insert into table_6 
    """ 

p = re.compile(r'^((?!--).)*\binsert\b\s+(?:into\s*)?.*', flags=re.IGNORECASE | re.MULTILINE) 
for m in re.finditer(p, lines): 
    line = lines[m.start(): m.end()].strip() 

    starts_with_insert = re.findall('insert.*', line, flags=re.IGNORECASE|re.MULTILINE|re.DOTALL) 
    print re.compile('insert\s+(?:into\s+)?', flags=re.IGNORECASE|re.MULTILINE|re.DOTALL).split(' '.join(starts_with_insert))[1].split()[0] 

Tatsächliches Ergebnis:

table_1 
table_2 
table_4 
table_5 
table_6 

Erwartetes Ergebnis: table_5 nicht zurückgegeben werden soll da ist es zwischen/* und */

table_1 
table_2 
table_4 
table_6 

Gibt es eine elegante Möglichkeit, dies zu tun?

Vielen Dank im Voraus.

EDIT: Danke für Ihre Lösungen. Ist es möglich, reine Regex ohne Stripping-Zeilen aus dem Originaltext zu verwenden?

Ich möchte die Zeilennummer anzeigen, wo Tabellenname aus der ursprünglichen Zeichenfolge gefunden werden kann.

Aktualisiert Code unten:

import re 

lines = """begin insert into table_1 end 
    begin insert table_2 end 
    select 1 --This is will not insert into table_3 
    begin insert into 
     table_4 
    end 
    /* this is a comment 
    insert into table_5 
    */ 
    insert into table_6 
    """ 

p = re.compile(r'^((?!--).)*\binsert\b\s+(?:into\s*)?.*', flags=re.IGNORECASE | re.MULTILINE) 
for m in re.finditer(p, lines): 
    line = lines[m.start(): m.end()].strip() 
    line_no = str(lines.count("\n", 0, m.end()) + 1).zfill(6) 

    table_names = re.findall(r'(?:\binsert\s*(?:into\s*)?)(\S+)', line, flags=re.IGNORECASE|re.MULTILINE|re.DOTALL) 
    print '[line number: ' + line_no + '] ' + '; '.join(table_names) 

Versuchte Look-Ahead/Lookbehind mit denen zwischen/* auszuschließen und * /, aber es ist nicht mein erwartetes Ergebnis.

Ich würde Ihre Hilfe zu schätzen wissen. Vielen Dank!

+0

vergessen Sie, dass beide '--' und'/* 'wird wahrscheinlich keine Kommentare starten, wenn es inside ist e eine Zeichenfolge ... –

+0

Ich denke, Sie sollten über 'Lookbehind Behauptung 'wissen –

Antwort

0

in 2 Schritten mit re.sub() und re.findall() Funktionen:

# removing single line/multiline comments 
stripped_lines = re.sub(r'/\*[\s\S]+\*/\s*|.*--.*(?=\binsert).*\n?', '', lines, re.S | re.I) 

# extracting table names preceded by `insert` statement 
tbl_names = re.findall(r'(?:\binsert\s*(?:into\s*)?)(\S+)', stripped_lines, re.I) 
print(tbl_names) 

Der Ausgang:

['table_1', 'table_2', 'table_4', 'table_6'] 
+0

Hallo Roman, Ihre Lösung mit Findall ist einfacher als mein Original. Der obige Code wurde mit Ihrer Lösung aktualisiert. Ist es möglich, das gleiche Ergebnis zu erzielen, ohne den ursprünglichen Text zu streichen? – pren

0
import re 
import string 

lines = """begin insert into table_1 end 
    begin insert table_2 end 
    select 1 --This is will not insert into table_3 
    begin insert into 
     table_4 
    end 
    /* this is a comment 
    insert into table_5 
    */ 
    insert into table_6 
    """ 

# remove all /* */ and -- comments 
comments = re.compile('/\*(?:.*\n)+.*\*/|--.*?\n', flags=re.IGNORECASE | re.MULTILINE) 
for comment in comments.findall(lines): 
    lines = string.replace(lines, comment, '') 

fullSet = re.compile('insert\s+(?:into\s+)*(\S+)', flags=re.IGNORECASE | re.MULTILINE) 
print fullSet.findall(lines) 

gibt

['table_1', 'table_2', 'table_4', 'table_6'] 
+0

Danke Calvin für deine nette Lösung.Das funktioniert wirklich, aber würde gerne wissen, ob es möglich ist, Regex direkt zu verwenden, ohne Zeilen zu entfernen? Aktualisierte Frage oben. Danke – pren

+0

Regex hat keinen Mechanismus zum Entschlüsseln von Kontext. Das Entfernen von Kommentaren gewährleistet, dass Sie sie nie finden. Wie Sie vielleicht zu schätzen wissen, kann Regex ziemlich schnell zu einem unlesbaren Chaos verkommen. Ich werde meine Antwort weiter komprimieren. Ich denke nicht, dass Sie es ohne einen Entfernungsschritt bekommen können. Es gibt einfach zu viele Was-wenn-Szenarien. Ich werde ein bisschen mehr spielen. –

Verwandte Themen