Python re.findall正则表达式和文本处理



我正在寻找并修改围绕convert函数的一些sql语法。基本上,我希望所有文件中的任何转换(A,B)convert(A,B)::A

到目前为止,我尝试用re.findall(r"bconvertb(.*?,.*)", l, re.IGNORECASE)选择它们,但它只返回了我想要的一小部分,而且我在实际操作我提到的a/B时也遇到了困难。

例如,一条样本线(注意这里的嵌套结构无关紧要,我只是在可能的情况下让外层工作)

convert(varchar, '/' || convert(nvarchar, es.Item_ID) || ':' || convert(nvarchar, o.Option_Number) || '/') as LocPath

应该变成。。。

'/' || es.Item_ID::nvarchar || ':' || o.Option_Number::nvarchar || '/' :: varchar as LocPath

示例2:

SELECT LocationID AS ItemId, convert(bigint, -1),

应该变成。。。

SELECT LocationID AS ItemId, -1::bigint,

我认为这应该可以通过某种带有组的re.sub实现,并且目前在for each循环中有一个代码结构,其中line是文件中的每一行:

matchConvert = ["convert(", "CONVERT("]
a = next((a for a in matchConvert if a in line), False)
if a:
print("convert() line")
#line = re.sub(re.escape(a) + r'', '', line)

编辑:最后,我采用了一个非重复解决方案,通过识别每个块并相应地操作它们来处理每一行。

这可能是一个X/Y问题,这意味着您正在询问如何使用Regex做一些事情,而解析(意味着使用/修改/编写SQL解析器)可能会更好地解决这些问题。这种情况的一个迹象是"convert"调用可以嵌套。从长远来看,如果你要处理很多文件,而且它们都很复杂,我猜Regex会比它更令人头疼。

任务:

交换此给定中所有"convert"函数的参数。参数可以包含任何字符,包括嵌套的"convert"函数。

解决方案:

def convert_py(s):
#capturing start:
left=s.index('convert')
start=s[:left]
#capturing part_1:
c=0
line=''
for n1,i in enumerate(s[left+8:],start=len(start)+8):
if i==',' and c==0:
part_1=line
break
if i==')':
c-=1
if i=='(':
c+=1
line+=i
#capturing part_2:
c=0
line=''
for n2,i in enumerate(s[n1+1:],start=n1+1):
if i==')':
c-=1
if i=='(':
c+=1
if c<0:
part_2=line
break
line+=i
#capturing end:
end=s[n2+1:]
#capturing result:
result=start+part_2.lstrip()+' :: '+part_1+end
return result
def multi_convert_py(s):
converts=s.count('convert')
for n in range(converts):
s=convert_py(s)
return s

注意:

  • 与另一个答案中提供的基于re模块的解决方案不同,如果给定字符串中的"convert"函数中有两个以上的参数,则此版本不应失败。但是,它将只交换它们一次,例如:convert(a,b, c)->b, c : a
  • 我担心可能会出现无法预料的情况,导致失败。如果发现任何瑕疵,请告知

任务:

交换给定字符串中所有"convert"函数的参数。参数可以包含任何字符,包括嵌套的"convert"函数。

基于re模块的解决方案:

def convert_re(s):
import re
start,part_1,part_2,end=re.search(r'''
(.*?)   
convert(
([^,)(]+(.+?)[^,)(]*|[^,)(]+)
,
([^,)(]+(.+?)[^,)(]*|[^,)(]+)
)
(.*)                                     
''',s,re.X).groups()

result=start+part_2.lstrip()+' :: '+part_1+end
return result
def multi_convert_re(s):
converts=s.count('convert')
for n in range(converts):
s=convert_re(s)
return s

"convert_re"函数的描述:

正则表达式:

  • 启动是第一组在"转换"之前出现的内容

  • 然后跟在convert()后面,CCD_6没有组,包含函数的名称和开头的"(">

  • part_1是第二组([^,)(]+(.+?)[^,)(]*|[^,)(]+)。这应该与第一个参数匹配。它可以是除-,)(之外的任何东西,也可以是前面有除,)(之外的任何内容的函数,后面有除CCD10之外的所有内容,并且在内部有任何内容(除了新行)

  • 然后跟在逗号,后面,它没有组

  • part_2是第三组,它的作用与第二组类似,但应该捕获外部函数中剩下的所有内容

  • 然后跟随),它没有组

  • end是第四组(.*),新行之前还有剩余内容。

然后通过交换part_1part_2,在它们之间放置':',从part_2中删除左侧的空格,并将start添加到开头,将end添加到结尾,来创建结果字符串。

"multi_convert_re"函数的描述

反复调用'convert_re'函数,直到没有";转换";左边

注意:

  • N。B.:代码暗示字符串中的"convert"函数正好有两个参数
  • 该代码适用于给定的示例,但对于其他示例,恐怕仍存在无法预见的缺陷。如果你发现任何瑕疵,请告诉我
  • 我在另一个答案中提供了另一个不基于re模块的解决方案。结果可能会有所不同

这是我基于@Иван-Балвн代码的解决方案。将这个结构分解为块使进一步的规范比我之前想象的要容易得多,我也将在许多其他操作中使用这个方法。

# Check for balanced brackets
def checkBracket(my_string):
count = 0
for c in my_string:
if c == "(":
count+=1
elif c == ")":
count-=1
return count

# Modify the first convert in line
# Based on suggestions from stackoverflow.com/questions/73040953
def modifyConvert(l):
# find the location of convert()
count = l.index('convert(')
# select the group before convert() call
before = l[:count]
group=""
n1=0
n2=0
A=""
B=""
operate = False
operators = ["|", "<", ">", "="]
# look for A group before comma
for n1, i in enumerate(l[count+8:], start=len(before)+8):
# find current position in l
checkIndex = checkBracket(l[count+8:][:n1-len(before)-8])
if i == ',' and checkIndex == 0:
A = group
break
group += i
# look for B group after comma
group = ""
for n2, i in enumerate(l[n1+1:], start=n1+1):
checkIndex = checkBracket(l[count+n1-len(before):][:n2-n1+1])
if i == ',' and checkIndex == 0:
return l
elif checkIndex < 0:
B = group
break
group += i

# mark operators
if i in operators:
operate = True
# select the group after convert() call
after = l[n2+1:]
# (B) if it contains operators
if operate:
return before + "(" + B.lstrip() + ') :: ' + A + after
else:
return before + B.lstrip() + '::' + A + after

# Modify cast syntax with convert(a,b). return line.
def convertCast(l):
# Call helper for nested cases
i = l.count('convert(')
while i>0:
i -= 1
l = modifyConvert(l)
return l

最新更新