Python:如何在多行字符串中查找所有匹配但不按特定单词继续?

时间:2017-10-05 10:44:06

标签: python regex findall

我有SQL代码,我想在“insert”关键字后提取表名。

基本上,我想使用以下规则进行提取:

  1. 包含“插入”一词
  2. 后跟单词“into”这是可选的
  3. 如果在插入(可选)关键字之前的任何位置都有“ - ”(这是SQL中的单行注释),则排除。
  4. 如果插入(可选)关键字位于“/ *”和“* /”之间(这是SQL中的多行注释),则排除。
  5. 在插入(可选)关键字
  6. 后获取下一个单词(table_name)

    示例:

    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]
    

    实际结果:

    table_1
    table_2
    table_4
    table_5
    table_6
    

    预期结果:不应返回table_5,因为它介于/ *和* /

    之间
    table_1
    table_2
    table_4
    table_6
    

    有优雅的方法吗?

    提前致谢。

    编辑:感谢您的解决方案。是否可以使用纯正则表达式而不从原始文本中删除线条?

    我想显示可以从原始字符串中找到表名的行号。

    以下更新的代码:

    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)
    

    尝试使用lookahead / lookbehind来排除/ *和* /之间的那些但是它没有产生我预期的结果。

    非常感谢您的帮助。谢谢!

2 个答案:

答案 0 :(得分:0)

使用re.sub()re.findall()函数的两个步骤:

# 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)

输出:

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

答案 1 :(得分: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)

给出

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