我正在寻找并修改围绕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_1
和part_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