Python 拆分转义分隔符
编辑: 为了更好地理解,重新措辞了这个问题 对于我正在使用哈夫曼压缩的项目,我需要序列化我的哈夫曼树 下文 “买了一张沿着巨大的螺旋形滑梯或跑步的票 整个夏天,穿过一个由色彩鲜艳的胶合板制成的游戏迷宫 长长的,笑声” 将生成一个哈夫曼树,其序列化如下所示:Python 拆分转义分隔符,python,regex,serialization,escaping,delimiter,Python,Regex,Serialization,Escaping,Delimiter,编辑: 为了更好地理解,重新措辞了这个问题 对于我正在使用哈夫曼压缩的项目,我需要序列化我的哈夫曼树 下文 “买了一张沿着巨大的螺旋形滑梯或跑步的票 整个夏天,穿过一个由色彩鲜艳的胶合板制成的游戏迷宫 长长的,笑声” 将生成一个哈夫曼树,其序列化如下所示: '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|N
'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,'
注意:这是分隔树符号的正则表达式:
文本也可以是HTML并包含字符
'|' and '\'
为了逃避他们,我改变了主意
'|' to '\|'
'\' to '\\'
分割数据时,我需要忽略转义字符,只删除管道。鉴于下一个输入,这将成为一个问题:
'replace( /(^|\s)client-nojs(\s|$)/, "$1client-js$2" );</script>'
当序列化字符串的末尾包含斜杠时,就会发生这种情况
'N54, test\\' will turn into 'N54, test\\\\|N44 ...'
到目前为止,我已经到了这个正则表达式
r'(?<!\\)(\\\\)*\|'
r'(?
编辑
澄清:拆分的数据不应该有空字符串。我对您试图执行的操作有点困惑。您只想拆分一个由|
字符分隔的序列化数据字符串
>>> import re
>>> data = '|1 \\|2 \\\\|3 \\\\\\|4 \\\\\\\\|5 \\\\\\|\\\\|6'
>>> re.split(r'\|', data)
['', '1 \\', '2 \\\\', '3 \\\\\\', '4 \\\\\\\\', '5 \\\\\\', '\\\\', '6']
说明:
我需要移除管道,同时忽略斜线
如果可能的话,我想知道如何使用re.split()实现这一点
我开始认为只有使用re.findall()才有可能
从理论上讲,简单地使用re.split()
是不可能的,因为正如您所说,会出现以下任一情况:EDIT(在Patrick Maupin的优秀方法之后进行了澄清,如图所示)
理论上不可能将“|
”分隔符与纯正则表达式解决方案进行匹配,以便使用Python的标准重新打包
在该字符上拆分。正如您所说,将出现以下任一情况:
斜线将随管道一起移除
斜杠将包含在列表中它们自己的单元格中
原因是你需要一个反向断言,在不使用匹配字符的情况下,通过奇数个转义使匹配失败
备选方案:
regex = r'(?:[^|\\]+|\\.)+'
data = '|1 \\|2 \\\\|3 \\\\\\|4 \\\\\\\\|5 \\\\\\|\\\\|6'
result = re.findall(regex, data)
print (result)
['1 \\|2 \\\\', '3 \\\\\\|4 \\\\\\\\', '5 \\\\\\|\\\\', '6']
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']
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']
下面的列表主要关注可能与分隔符匹配的纯正则表达式解决方案。它们基于使用不同的策略生成树,或者使用不同的正则表达式风格进行解析
使用后缀符号por escapes:
'|' to '|\'
'\' to '\\'
使用不能作为符号一部分的分支分隔符(因此不需要转义)
调用允许(如\K
in或in)的正则表达式库
导入并使用正则表达式控制谓词(*跳过)(*失败)
(也在PCRE中实现)
代码输入(lookbehind允许可变宽度子模式)
解析前反转字符串,解析后反转以进行规范化
定义分隔符前面的最大反斜杠数
这将匹配除|
或\
之外的任何字符,并且还将匹配后跟任何字符的反斜杠
输出:
regex = r'(?:[^|\\]+|\\.)+'
data = '|1 \\|2 \\\\|3 \\\\\\|4 \\\\\\\\|5 \\\\\\|\\\\|6'
result = re.findall(regex, data)
print (result)
['1 \\|2 \\\\', '3 \\\\\\|4 \\\\\\\\', '5 \\\\\\|\\\\', '6']
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']
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']
解决方案2:
如果您还希望包含空令牌,则需要使用捕获组并循环每个匹配。这是为了保证,如果最后一次匹配以“<代码> <代码> >结束,则将被认为是空令牌。否则,将无法区分<代码> a b b/COD>和<代码> a b b < /c> > /p>
代码:
regex = r'(?:[^|\\]+|\\.)+'
data = '|1 \\|2 \\\\|3 \\\\\\|4 \\\\\\\\|5 \\\\\\|\\\\|6'
result = re.findall(regex, data)
print (result)
['1 \\|2 \\\\', '3 \\\\\\|4 \\\\\\\\', '5 \\\\\\|\\\\', '6']
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']
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']
输出:
regex = r'(?:[^|\\]+|\\.)+'
data = '|1 \\|2 \\\\|3 \\\\\\|4 \\\\\\\\|5 \\\\\\|\\\\|6'
result = re.findall(regex, data)
print (result)
['1 \\|2 \\\\', '3 \\\\\\|4 \\\\\\\\', '5 \\\\\\|\\\\', '6']
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']
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']
编辑:
regex = r'(?:[^|\\]+|\\.)+'
data = '|1 \\|2 \\\\|3 \\\\\\|4 \\\\\\\\|5 \\\\\\|\\\\|6'
result = re.findall(regex, data)
print (result)
['1 \\|2 \\\\', '3 \\\\\\|4 \\\\\\\\', '5 \\\\\\|\\\\', '6']
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']
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']
上面的解决方案着重于提供一个清晰的示例,说明如何使用正则表达式解决此问题。目标字符串和结果都不会被解析。但是,正如Patrick Maupin在他的文章中所示,它们缺乏性能。这就是为什么我提供的另一个解决方案比使用split()的解决方案提高了约30%
。上述解决方案的主要问题是如何处理处于前导或尾随位置的空令牌。这可以通过一个小技巧解决
最终解决方案:
为了避免检查是否有空令牌,我们可以在数据
前面加一个“|
”分隔符。因此,我们可以使用findall()
模式,在每个令牌之前加一个分隔符
代码:
regex = r'(?:[^|\\]+|\\.)+'
data = '|1 \\|2 \\\\|3 \\\\\\|4 \\\\\\\\|5 \\\\\\|\\\\|6'
result = re.findall(regex, data)
print (result)
['1 \\|2 \\\\', '3 \\\\\\|4 \\\\\\\\', '5 \\\\\\|\\\\', '6']
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']
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']
输出:
regex = r'(?:[^|\\]+|\\.)+'
data = '|1 \\|2 \\\\|3 \\\\\\|4 \\\\\\\\|5 \\\\\\|\\\\|6'
result = re.findall(regex, data)
print (result)
['1 \\|2 \\\\', '3 \\\\\\|4 \\\\\\\\', '5 \\\\\\|\\\\', '6']
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']
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']
我编写了一个酷刑测试,它创建并组合了几个小字符串——我认为它应该能够处理大多数极端情况
Mariano的finditer()
但是,他有一个新的findall()
解决方案,在将字符串传递给re
之前,他会修改该字符串,这比这里显示的split()
解决方案更快、更简单
注意,最近澄清了OP在管道字符之间不会有任何空字符串,Mariano提出的原始findall()
示例(无需初始字符串修改)最适合原始海报
Mariano新的findall()
预修改字符串解决方案可能最适合一般情况。split()
排在第二位,但这正是我所关注的,因为它是原始问题的焦点:-)
以下代码同时适用于Python2和Python3
import re
import itertools
import time
def use_finditer(data):
regex = re.compile(r'((?:[^|\\]+|\\.)*)([|])?')
result = []
for m in regex.finditer(data):
result.append(m.group(1))
if (not m.group(2)):
break
return result
def use_split(data):
regex = re.compile(r'(?:\|)?((?:[^|\\]|\\.)*)')
result = regex.split(data)
start_delete = data.startswith('|') * 2 if data else 1
del result[start_delete::2]
return result
def check_split(split_func):
values = '', '', '', ' ', ' ', '|', '|', '\\', '\\\\', 'abc', 'd|ef', 'ghi\\'
values = [x.replace('\\', '\\\\').replace('|', '\\|') for x in values]
stuff = [], []
for i in range(1, 6):
srclist = list(itertools.permutations(values, i))
for src in srclist:
src = tuple(src)
dst = tuple(split_func('|'.join(src)))
stuff[dst != src].append((src, dst))
if not stuff[1]:
print("Successfully executed %d splits" % len(stuff[0]))
return
print(len(stuff[0]), len(stuff[1]))
stuff[1].sort(key=lambda x: (len(x), x))
for x, y in stuff[1][:20]:
z = '|'.join(x)
print(x, repr(z), y)
def check_loop(func, count=20):
start = time.time()
for i in range(count):
check_split(func)
print('Execution time: %0.2f' % (time.time() - start))
print('\nUsing finditer')
check_loop(use_finditer)
print('\nUsing split')
check_loop(use_split)
您的预期输出是什么?这似乎更像是一个问题,您需要扩展原始输入和您想要的预期输出。我不太关心问题的解决方案,而更关心re.split的工作方式,因此标题为。我将添加我的输入和预期的输出。太好了!两个问题:(1)是否有任何理由使用[|]
而不是\\\\
(2)是否