【问题标题】:Python regex for select/extract from nested groups用于从嵌套组中选择/提取的 Python 正则表达式
【发布时间】:2013-12-14 17:38:14
【问题描述】:

我正在尝试使用 CHAR(int) 和 NCHAR(int) 处理字符串,以将这些实例与它们的 ASCII 对应部分进行转换。一个例子是这样的:

CHAR(124) + (SELECT TOP 1 CAST(name AS VARCHAR(8000)) FROM (SELECT TOP 1 colid, name FROM [Projects]..[syscolumns]
WHERE xtype=char(85)
AND id = OBJECT_ID(NCHAR(69)+NCHAR(78)+NCHAR(95)+NCHAR(69)+NCHAR(109)+NCHAR(112)+NCHAR(108))

请注意,我不想对 VARCHAR(int) 做任何事情,只对 CHAR(int) 和 NCHAR(int) 部分做任何事情。以上内容应翻译为:

|(SELECT TOP 1 CAST(name AS VARCHAR(8000)) FROM (SELECT TOP 1 colid, name FROM [Projects]..[syscolumns] WHERE xtype=U AND id = OBJECT_ID(EN_Empl)

请注意,应删除 CHAR(int) 或 NCHAR(int) 两侧的任何“+”。我尝试了以下方法:

def conv(m):
    return chr(int(m.group(2)))

print re.sub(r'([\+ ]?n?char\((.*?)\)[\+ ]?)', conv, str, re.IGNORECASE)

其中str=必须处理的原始字符串。

不知何故,VARCHAR(8000) 正在被拾取。如果我调整正则表达式,xtype 之后的“=”就会消失,而不仅仅是 CHAR(int) 或 NCHAR(int) 实例两侧的空格和“+”。

希望有人能把我拉出来。

其他示例字符串:

字符串"char(124)+(Select Top 1 cast(name as varchar(8000)) from (Select Top 1 colid,name From [Projects]..[syscolumns] Where id = OBJECT_ID(NCHAR(69)+NCHAR(78)+NCHAR(95)+NCHAR(69)+NCHAR(109)+NCHAR(112)+NCHAR(108)))"

正则表达式:r'(\bn?char\((\d+)\)(?:\s*\+\s*)?)'

结果:"|(Select Top 1 cast(name as varchar(8000)) from (Select Top 1 colid,name From [Projects]..[syscolumns] Where id = OBJECT_ID(ENCHAR(78)+NCHAR(95)+NCHAR(69)+NCHAR(109)+NCHAR(112)+NCHAR(108)))"

【问题讨论】:

    标签: python regex non-greedy


    【解决方案1】:

    你有三个问题:

    1. 您需要在re.sub 中使用flags=re.IGNORECASE 而不仅仅是re.IGNORECASE。那是一个关键字参数。
    2. 您需要使用\b 来查找单词边界。
    3. 你不应该使用str作为名字,因为你会用相同的名字覆盖内置

    这行得通:

    import re
    
    tgt='''\
    CHAR(124) + (SELECT TOP 1 CAST(name AS VARCHAR(8000)) FROM (SELECT TOP 1 colid, name FROM [Projects]..[syscolumns]
    WHERE xtype=char(85)
    AND id = OBJECT_ID(NCHAR(69)+NCHAR(78)+NCHAR(95)+NCHAR(69)+NCHAR(109)+NCHAR(112)+NCHAR(108))'''
    
    pat=r'(\bn?char\((\d+)\)(?:\s*\+\s*)?)'
    
    def conv(m):
        return chr(int(m.group(2)))
    
    print re.sub(pat, conv, tgt, flags=re.IGNORECASE)    
    

    更完整:

    import re
    
    tgt='''\
    CHAR(124) + (SELECT TOP 1 CAST(name AS VARCHAR(8000)) FROM (SELECT TOP 1 colid, name FROM [Projects]..[syscolumns]
    WHERE xtype=char(85)
    AND id = OBJECT_ID(NCHAR(69)+NCHAR(78)+NCHAR(95)+NCHAR(69)+NCHAR(109)+NCHAR(112)+NCHAR(108))'''
    
    pat=r'(\bn?char\((\d+)\)(?:\s*\+\s*)?)'
    
    def conv(m):
        return chr(int(m.group(2)))
    
    print re.sub(r'''
                  (                                 # group 1
                  \b                                # word boundary
                  n?char                            # nchar or char
                  \(                                # literal left paren
                  (\s*\d+\s*)                       # digits surrounded by spaces
                  \)                                # literal right paren
                  (?:\s*\+\s*)?                     # optionally followed by a concating '+' 
                  )                                 '''
                , conv, tgt, flags=re.VERBOSE | re.IGNORECASE)   
    

    打印:

    |(SELECT TOP 1 CAST(name AS VARCHAR(8000)) FROM (SELECT TOP 1 colid, name FROM [Projects]..[syscolumns]
    WHERE xtype=U
    AND id = OBJECT_ID(EN_Empl)
    

    【讨论】:

    • 我用了你的表情,我觉得我更近了一步!但是,如果您查看我在问题中提供的新示例以及您提供的正则表达式,您会观察到一系列由“+”分隔的 nchar(int) 实例似乎只转换第一个实例,其余实例是原样保存。有什么建议么? NCHAR(69) 被替换为“E”,但其他一切都保持不变。
    • 您是否使用了关键字re.sub(..., flags=re.IGNORECASE)
    • 是的,我使用了 IGNORECASE 标志。 @Tim Peters 的回答对我有用。我认为您的答案与他的答案之间的区别在于他在之前和之后添加了“+”和空格部分,这是必需的。谢谢你的帮助!!
    【解决方案2】:

    你可以通过添加单词边界(\b)断言来走很长的路,但我建议你(1)使用re.VERBOSE编写一个以后可以理解的正则表达式; (2) 编译正则表达式以减少调用现场的混乱; (3) 收紧一些匹配标准。像这样:

    def conv(m):
        return chr(int(m.group(1)))
    
    pat = re.compile(r"""[+\s]*    # optional whitespace or +
                         \b        # word boundary
                         n?char    # NCHAR or CHAR
                         \(        # left paren
                         ([\d\s]+) # digits or spaces - group 1
                         \)        # right paren
                         [+\s]*    # optional whitespace or +
                      """, re.VERBOSE | re.IGNORECASE)
    print pat.sub(conv, data)
    

    请注意,我将您的 str 更改为 datastr 是一个大量使用的内置函数的名称,创建具有相同名称的变量是一个非常糟糕的主意。

    【讨论】:

    • 谢谢@Tim Peters。欣赏提高可读性的建议;它也对我有帮助(更不用说其他人了!)。我确实尝试过,它似乎工作!关于分组+搜索+替换在您的正则表达式中起作用的一件事我不明白。我最初是在另一个组(封装 [N]CHAR(int) 实例周围的“+”和空格)中创建一个组(用于将值转换为其 ASCII 等效项)。您的正则表达式会删除任何周围的“+”或空格,即使它不是该组的一部分。我必须花更多时间在正则表达式基础上。感谢您的帮助!
    • 不客气 :-) sub() 替换了正则表达式匹配的整个子字符串,因此实际上不需要最外面的组。这就是我删除它的原因。不过,我们仍然需要一个组来隔离数字,以便conv() 可以轻松找到它们。但是conv() 的输出替换了正则表达式匹配的 entire 子字符串。一开始可能有点微妙,但你很快就会习惯的;-)
    • 感谢@Tim Peters 非常有帮助的解释和回答!
    • 我有这些字符串的另一个实例,其中包含类似_char(75)_nchar(65) 的转换正在跳过。我理解这是因为“_”被视为单词边界标准的一部分,即[a-zA-Z0-9_]。如何使用适用于大多数情况的上述模式来处理这个问题?
    • _? 添加到\b 行之后的正则表达式。并注意,当正则表达式以多行的小块编写时,更改它是多么容易;-)
    【解决方案3】:

    你只需要使用单词边界\b

    def conv(m):
        return chr(int(m.group(1)))
    
    print re.sub(r'\bn?char\(([^)]+)\)(?:\s*\+\s*)?', conv, str, re.IGNORECASE)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-05
      • 1970-01-01
      • 2018-07-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多