重复标记=值块的正则表达式

时间:2017-05-30 21:54:54

标签: python regex

我有一个数据集,其中包含由|分隔的tag = value对行(\ X01)。以下是单行示例:

120 = 3 | 162 = 0 | 181 = 1 | 72 = 24842 | 23 = 125 | 40 = 119 | 155 = 2321 | 130 = 3 | 105 = 1 | 4562 = 1 | 162 = 2 | 181 = 1 | 72 = 24842 | 23 = 125 | 40 = 120 | 155 = 2322 | 130 = 5 | 105 = 1 | 4562 = 2 | 162 = 0 | 181 = 1 | 72 = 24842 | 23 = 125 | 40 = 121 | 155 = 2326 | 130 = 2 | 105 = 1 | 4562 = 10 | 100 = 087 | \ n

我想要做的是使用正则表达式捕获每个重复块 - 每个重复块以标记162开头,以标记4562结束,中间标记也始终相同 - 并将其放在列表中。

以上示例的输出应如下:

['162=0 181=1 72=24842 23=125 40=119 155=2321 130=3 105=1 4562=1',     
'162=2 181=1 72=24842 23=125 40=120 155=2322 130=5 105=1 4562=2', 
'162=0 181=1 72=24842 23=125 40=121 155=2326 130=2 105=1 4562=10']

我已尝试使用以下表达式的变体:

re.findall("(?:^|\x01)(162)=(.*?)(?=\x01)", line)

它正确捕获了各个tag = value对,但我还没能找到正确的表达式来粘合"它们在一起以获得上述输出。

请注意,每一行都以一个标记开头,该标记通知我们所包含的重复块的数量(1到N)。在这个特定情况下,从标签120 = 3看,它是3。

感谢您的帮助。

2 个答案:

答案 0 :(得分:2)

使用一点正则表达式,然后使用str.replace()

>>> result = re.findall(r'\b162=\d+(?:\|\d+=\d+)+?\|4562=\d+', line)
>>> result = [l.replace('|', ' ') for l in result]
>>> print(result)
['162=0 181=1 72=24842 23=125 40=119 155=2321 130=3 105=1 4562=1', 
 '162=2 181=1 72=24842 23=125 40=120 155=2322 130=5 105=1 4562=2', 
 '162=0 181=1 72=24842 23=125 40=121 155=2326 130=2 105=1 4562=10']

正则表达式验证问题中描述的格式。然后str.replace有助于将|转换为单个空格' '

正则表达式:\b162=\d+(?:\|\d+=\d+)+?\|4562=\d+

  • \b162匹配字边界,后跟162,以便2162等不匹配
  • =\d+匹配等于=,后跟至少一个数字
  • (?:\|\d+=\d+)+?是一个非捕获组,允许任意数量的|后跟数字=,数字,格式为| N = N
  • \|4562=\d+匹配最后一部分,即|后跟4562,=和数字

Regex101 Demo

答案 1 :(得分:1)

纯Python解决方案,无需正则表达式:

def parse_data(data):
    result, current = [], []  # storage for our final result and current sublist
    pairs = data.split("|")  # lets first turn everything to key-value pairs
    for pair in pairs:
        if current or pair[:4] == "162=":
            current.append(pair)  # add to the current sublist
            if pair[:5] == "4562=":  # end tag, finalize the block
                result.append(current)  # add the sublist to our main result
                current = []  # reinitialize sublist
    return [" ".join(current) for current in result]  # finally, space separate the pairs

parsed = parse_data(line)
# ['162=0 181=1 72=24842 23=125 40=119 155=2321 130=3 105=1 4562=1',
#  '162=2 181=1 72=24842 23=125 40=120 155=2322 130=5 105=1 4562=2',
#  '162=0 181=1 72=24842 23=125 40=121 155=2326 130=2 105=1 4562=10']

你可能应该选择regex,我认为CPython会更快。作为一个程序,纯粹的Python'比regex引擎必须做的更简单,但regex引擎在C层上运行,而上面的大部分代码都被解释为......

更新 - 但是,我们可以在程序上仍然击败regex,考虑一下:

def parse_data_optimized(data):
    result = []  # storage for our result
    start = 0  # where to start searching our string
    while True:
        start = data.find("162=", start)  # find the next 162 tag
        end = data.find("4562=", start)  # find the following 4562 tag
        end = data.find("|", end)  # find the key-value separator after the end tag
        if start == -1 or end == -1:  # if either search failed nothing more to search
            break
        result.append(data[start:end].replace("|", " "))  # slice's '|'->' ', add to result
        start = end  # set our next search to start from the end of the current one
    return result  # return the result

您数据的基准(使用degant'正则表达式进行比较,所有这些都在CPython上):

# Python 3.5.1
zwer_1: 100,000 loops: 1.138 seconds
zwer_2: 100,000 loops: 0.515 seconds
regexp: 100,000 loops: 0.772 seconds

# Python 2.7.11
zwer_1: 100,000 loops: 0.833 seconds
zwer_2: 100,000 loops: 0.431 seconds
regexp: 100,000 loops: 0.763 seconds