python - 拆分转义分隔符

标签 python regex serialization escaping delimiter

编辑:
重新表述问题以便更好地理解。

对于我正在使用霍夫曼压缩进行的项目,我需要序列化我的霍夫曼树。

以下文字

"bought tickets to ride down a giant spiraling water slide or run through a play maze made from brightly painted plywood. All summer long, the sounds of laughing"



将生成一个 Huffman 树,其序列化如下所示:
'N57|L23, |N34|N16|N8|N4|N2|L1,made|L1,long|N2|L1,bought' \
'|L1,summer|N4|N2|L1,painted|L1,from|N2|L1,|L1,sounds|N8|N4|N2|L1,play|' \
'L1,tickets|N2|L1,All|L1,down|N4|N2|L1,brightly|L1,spiraling|N2|L1,giant|' \
'L1,ride|N18|N8|N4|N2|L1,. |L1,plywood|N2|L1,laughingreplace|L1,water|N4|' \
'N2|L1,the|L1,to|N2|L1,of|L1,through|N10|N4|N2|L1,run|L1,or|L2,a|N6|N3|' \
'L1,slide|N2|L1,maze|L1,, |L3,'

注意:这是分隔树符号的正则表达式:
'(\W+)'

文本也可以是 HTML 并包含字符。
'|' and '\'

为了逃避他们我改变
'|' to '\|'
'\' to '\\'

拆分数据时,我需要忽略转义字符,只删除管道。考虑到下一个输入,这将成为一个问题:
'replace( /(^|\s)client-nojs(\s|$)/, "$1client-js$2" );</script>'

这是序列化的输出
'N19|N8|N4|N2|L1,)|L1," );</|N2|L1,script|L1,client|' \
'N4|N2|L1,2|L1,js|N2|L1,(\|L1,nojs|N11|N4|L2,s|N2|L1,replace|L1,>' \
'|N7|N3|L1,1client|N2|L1,$|L1,( /(^\|\|N4|N2|L1,\|$)/, "$|L1,|L2,-'

现在试图拆分它成为一个问题。我知道我需要删除前面有偶数个斜杠的管道。
['\\|', '|', '\\\\|', ...] Valid delimiters
['\|', '\\\|', ...] Invalid delimiters

当我的序列化字符串末尾包含斜杠时,就会发生这种情况。
'N54, test\\' will turn into 'N54, test\\\\|N44 ...'

到目前为止,我已经到了这个正则表达式的地步
r'(?<!\\)(\\\\)*\|'

捕获前面有偶数个斜杠的管道。但是当使用
re.split() 我总会遇到两个问题之一
  • 斜线将与管道一起移除。
  • 斜线将包含在列表中它们自己的单元格中。

  • 两者都破坏了我的反序列化。
    我需要移除管道,同时忽略斜线。

    如果可能的话,我想知道如何用 re.split() 做到这一点,尽管我开始认为只有 re.findall() 才有可能

    编辑
    说明:拆分数据不应包含空字符串。

    最佳答案

    描述:

    I need to remove the pipes, while ignoring the slashes.

    If possible, I'd like to know how to do this with re.split() although I'm starting to think it's only possible with re.findall()



    理论上不可能简单地使用re.split()因为,正如您所说,将发生以下任一情况:编辑 (在 Patrick Maupin 在他的 answer 中展示的出色方法之后的澄清)。

    理论上是不可能的,实际上匹配 | ”分隔符使用纯正则表达式解决方案,以便使用 Python 的标准 re package 在该字符上拆分.如您所说,将发生以下任一情况:
  • 斜线将与管道一起移除。
  • 斜线将包含在列表中它们自己的单元格中。

  • 这样做的原因是您需要一个向后断言来使匹配失败且转义次数为奇数,同时不消耗匹配的字符。然而,lookbehind assertions在 python 中必须是固定宽度的(并且在大多数正则表达式中)。

    替代方案:

    以下列表侧重于可以实际匹配分隔符的纯正则表达式解决方案。它们基于使用不同的策略来生成树,或者使用不同的正则表达式进行解析。
  • 使用后缀符号或转义:
    '|' to '|\'
    '\' to '\\'
    
  • 使用不能成为符号一部分的分支定界符(因此不需要转义)。
    symbol 1{next}symbol 2{next}...
    
  • 调用允许 resetting the match 的正则表达式库(例如 \K 中的 regex package by Matthew BarnettPCRE 中)。 Demo .
  • 导入 regex package并使用正则表达式控制动词 (*SKIP)(*FAIL) (也在 PCRE 中实现)。 Demo .
  • 代码在 .net (lookbehinds 允许可变宽度的子模式)。 Demo
  • 在解析之前反转字符串,然后反转以规范化。 Demo
  • 定义可能位于分隔符之前的最大反斜杠数。 Demo .
    regex = r'(?<!(?<!\\)\\)(?<!(?<!\\)\\\\\\)(?<!(?<!\\)\\\\\\\\\\)(?<!(?<!\\)\\\\\\\\\\\\\\)[|]'
                # Up to 8 preceding backslashes
    


  • 解决方案1:
  • 假设没有空符号( token ),或者可以忽略空符号。

  • 而不是 split ,匹配 每个 token 。这是匹配(或断言)前面的转义同时将它们作为 Python 中标记的一部分的唯一方法。

    代码:
    regex = r'(?:[^|\\]+|\\.)+'
    data = '|1 \\|2 \\\\|3 \\\\\\|4 \\\\\\\\|5 \\\\\\|\\\\|6'
    result = re.findall(regex, data)
    
    print (result)
    

    这将匹配除 | 之外的任何字符或 \ , 它还将匹配一个反斜杠后跟任何字符。

    输出:
    ['1 \\|2 \\\\', '3 \\\\\\|4 \\\\\\\\', '5 \\\\\\|\\\\', '6']
    

    DEMO

    解决方案2:

    如果您还想包含空标记,则需要使用捕获组并循环每个匹配项。这是为了保证如果最后一场比赛以“|”结束,它将被视为一个空标记。否则,将无法区分 a|ba|b| .

    代码:
    import re
    
    regex = re.compile(r'((?:[^|\\]+|\\.)*)([|])?')
    data = '|1 \\|2 \\\\|3 \\\\\\|4 \\\\\\\\|5 \\\\\\|\\\\|6'
    result = []
    
    for m in regex.finditer(data):
        result.append(m.group(1))
        if (not m.group(2)):
            break
    
    print (result)
    

    输出:
    ['', '1 \\|2 \\\\', '3 \\\\\\|4 \\\\\\\\', '5 \\\\\\|\\\\', '6']
    

    DEMO

    编辑:

    上述解决方案的重点是提供一个清晰的例子,说明人们如何使用正则表达式来解决这个问题。既不解析目标字符串也不解析结果。然而,正如 Patrick Maupin 在他的 outstanding solution 中所展示的那样,他们缺乏性能。这就是为什么我提供另一种证明 ~30% faster 的解决方案的原因。比使用 split() .上述解决方案的主要问题是如何处理处于领先或落后位置的空 token 。这可以通过一个小技巧来解决。

    最终解决方案:

    为了避免检查是否有空标记,我们可以在 | 前面加上一个“data”分隔符。 .因此,我们可以使用 findall()使用在每个标记之前需要一个分隔符的模式。

    代码:
    import re
    
    # the delimiter must precede each token
    regex = r'[|]((?:[^|\\]|\\.)*)'
    data = '|1 \\|2 \\\\|3 \\\\\\|4 \\\\\\\\|5 \\\\\\|\\\\|6'
    
    # the data is prefixed with a '|' before it's passed to findall()
    result = re.findall( regex, '|' + data)
    
    print(result)
    

    输出:
    ['', '1 \\|2 \\\\', '3 \\\\\\|4 \\\\\\\\', '5 \\\\\\|\\\\', '6']
    

    DEMO

    关于python - 拆分转义分隔符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32707334/

    相关文章:

    java - MicroStream(反序列化)如何工作?

    python - 错误 : ipykernel requires Python version 3. 4 或以上

    python - YouTube api 获得 Python 观看次数最多的前 10 名

    python - 尝试 groupby 聚合 pandas 中的多行时缺少列

    python - 如何将 3D 数组转换为 python 中的 2D 数组列表?

    regex - 解析简单查询语法

    c# 子类的 XML 序列化 - 删除 xmlns :p1 and p1:type attributes from root node

    java - 有没有一种方法可以在序列化期间存储静态变量的值?

    java - 正则表达式返回进程的 pid

    javascript - 使用 RegEx 创建带有类名的 div