繁体   English   中英

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

[英]Python re.findall regex and text processing

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

到目前为止,我尝试使用re.findall(r"\bconvert\b\(.*?,.*\)", l, re.IGNORECASE)选择它们但它只返回了我想要的一小部分,我也在实际操作提到的 A/BI 时遇到麻烦。

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

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 实现,并且当前在每个循环中都有一个代码结构,其中 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)

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

任务:

交换这个给定的所有“转换”函数的参数。 参数可以包含任何字符,包括嵌套的“转换”函数。

一个解法:

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(a,b, c) --> b, c : a
  • 恐怕会出现无法预料的情况而导致失败。 请告诉您是否发现任何缺陷

如果我正确理解了任务,这是一种解决方案:

import re

string="convert(varchar(max), "\
       "'/' || convert(nvarchar, es.Item_ID) "\
       "|| ':' || convert(nvarchar, o.Option_Number) || '/') "\
       "as LocPath"

start,mid_1,mid_2,end=re.search(r'''(\w+?\()
                                    (.+)(?<=\)),(.+)
                                    (\).*)''',string,re.X).groups()
result=start+mid_2.lstrip()+':: '+mid_1+end

start是第一组(\w+?\()函数的名称和开头的 '('

mid_1是第二组(.+) ,包含第一个参数

然后是(?<=\)),它指定了分组被划分的点 ','

mid_2是带有第二个参数的第三组(.+)

end是第四组(\).*) ,其中 ') as LocPath' 部分

然后我合并字符串,但交换 mid_1 和 mid_2。 我在它们之间放置了'::',并从 mid_2 中删除了左侧的空格。

该代码在此示例中有效,但在涉及其他示例时可能仍然存在无法预料的缺陷。 请告诉您是否发现任何错误。

这可能是一个 X/Y 问题,这意味着您正在询问如何使用 Regex 做一些可以通过解析更好地解决的事情(意味着使用/修改/编写 SQL 解析器)。 一个迹象表明,“转换”调用可以嵌套。 我猜从长远来看,如果您正在处理大量文件并且它们非常复杂,那么从长远来看,Regex 将更令人头疼。

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

# 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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM