Python正则表达式匹配SQL INSERT语句

时间:2017-05-26 13:46:09

标签: python regex django

我正在开发一个django网站,我正在尝试使用从遗留数据库中转储的数据为django创建YAML灯具。

我正在编写一个使用正则表达式的原始SQL解析器(我知道,我知道..但我找不到任何可以帮助我快速完成此操作的内容,所以我必须“自己动手” - 除非有更好的建议)。

“滚动我自己”解决方案的一部分是解析SQL语句 - 这些是自动生成的,因此语句的格式不会改变。

以下是两个示例INSERT语句:

INSERT INTO ref_geographic_region (continent_id,name) VALUES(8,'Europe (Western)');
INSERT INTO ref_currency_group (name) VALUES('Major');

我想将SQL语句转换为以下模式:

INSERT INTO <table_name> VALUES (one_or_more_alphanums_separated_by_comma);

然后我需要匹配以下值:

  • 表名
  • one_or_more_alphanums_separated_by_comma

这是我的正则表达式模式。它匹配,但分组不是我想要的。

pattern_string = r"INSERT INTO ([a-zA-Z\_]+)\s\(((([a-zA-Z\_]+)(\,)*)+)\)\s+VALUES\(([0-9]*)|([a-zA-Z\(\)']+)(\,)*\;"

我如何修改(和简化)上面的模式,所以它只匹配我感兴趣的标记?

3 个答案:

答案 0 :(得分:1)

停止尝试使用正则表达式解析SQL。这与使用正则表达式解析HTML一样糟糕,因为SQL是一种无上下文的语言,正则表达式无法处理。使用适当的解析模块(如PyParsing

)可以更轻松地完成此操作
from pyparsing import Regex, QuotedString, delimitedList

# Object names and numbers match these regular expression
object_name = Regex('[a-zA-Z_]+')
number = Regex('-?[0-9]+')
# A string is just something with quotes around it - PyParsing has a built in
string = QuotedString("'") | QuotedString('"')

# A term is a number or a string
term = number | string

# The values we want to capture are either delimited lists of expressions we know about...
column_list = (delimitedList(object_name)).setResultsName('columns')
term_list = (delimitedList(term)).setResultsName('terms')

# Or just an expression we know about by itself
table_name = object_name.setResultsName('table')

# And an SQL statement is just all of these pieces joined together with some string between them
sql_stmt = "INSERT INTO " + table_name + "(" + column_list + ") VALUES(" + term_list + ");"


if __name__ == '__main__':
    res = sql_stmt.parseString("""INSERT INTO ref_geographic_region (continent_id,name) VALUES(8,'Europe (Western)');""")
    print res.table         # ref_geographic_region
    print list(res.columns) # ['continent_id', 'name']
    print list(res.terms)   # ['8', 'Europe (Western)']

这是一个快速的半小时稻草人 - 我建议阅读它的docs并正确理解它是如何工作的。特别是,PyParsing有一些奇怪的空白行为,在你正确删除之前值得了解。

答案 1 :(得分:0)

如果语句的格式是固定的,那么使用正则表达式几乎没有意义。只需使用简单的字符串解析:

parts = statement.split(' ', 4)

print(parts[2])
print(parts[3][1:-1].split(','))
print(parts[4][7:-2].split(','))

示例输出:

ref_geographic_region
['continent_id', 'name']
['8', "'Europe (Western)'"]

答案 2 :(得分:0)

对我来说,你只想从insert语句中删除字段名。

在这种情况下,您可以匹配语句的第一部分并从中删除字段。

例如:

import re

lines = [ 
  "INSERT INTO ref_geographic_region (continent_id,name) VALUES(8,'Europe (Western)');",
  "INSERT INTO ref_currency_group (name) VALUES('Major');"
]

insertStatementPattern = re.compile(r'^(insert into\s+\S+)\s*\([^()]+\)', re.IGNORECASE)

for i, line in enumerate(lines):
  line = re.sub(insertStatementPattern, r'\1', line)
  print (i, line)

用于捕获字段名和值的更完整的正则表达式:

(?i)^insert into\s*(?P<tablename>\w+)\s*\((?P<fieldnames>[^()]+)\)\s*values\s*\((?P<fieldvalues>.*?)\)\;$

然后可以在逗号上拆分命名捕获组中的结果。