如何在列表中优雅地将“1-3,6-8”转换为“1 2 3 6 7 8”?

时间:2016-11-05 16:58:45

标签: python python-3.x pandas python-3.5

问题

背景

我有一个包含不规则数据的~10,000个列表的列表,需要将其转换为特定格式。转换后,这些数据将被摄入到pandas数据帧中。

TL / DR; 如何在列表中优雅地转换以下正则表达式的匹配字符串?

正则表达式 '\ d {1,3} - \ d {1,3},\ d {1,3} - \ d {1,3}'

示例: '1-3,6-8'到'1 2 3 6 7 8'

当前解决方案: 使用列表推导需要多个类型转换来转换字符串,并且不适合作为持久解决方案。

pat = re.compile('\d{1,3}-\d{1,3},\d{1,3}-\d{1,3}')

row = ['sss-www,ddd-eee', '1-3,6-8', 'XXXX', '0-2,3-7','234','1,5']

lst = [((str(list(range(int(x.split(',')[0].split('-')[0]), 
    int(x.split(','[0].split('-')[1])+1))).strip('[]').replace(',', '')+' '
    +str(list(range(int(x.split(',')[1].split('-')[0]), 
    int(x.split(',')[1].split('-')[1]) + 1))).strip('[]').replace(',', ''))) 
    if pat.match(str(x)) else x for x in row]

结果

    ['sss-www,ddd-eee', '1 2 3 6 7 8', 'XXXX', '0 1 2 3 4 5 6 7', '234', '1,5']

6 个答案:

答案 0 :(得分:1)

抓住群组会更容易。

然后将组列表转换为整数,并在列表推导中逐个处理它们,与itertools.chain链接

import re,itertools

pat = re.compile('(\d{1,3})-(\d{1,3}),(\d{1,3})-(\d{1,3})')

z='1-3,6-8'

groups = [int(x) for x in pat.match(z).groups()]

print(list(itertools.chain(*(list(range(groups[i],groups[i+1]+1)) for i in range(0,len(groups),2)))))

结果:

[1, 2, 3, 6, 7, 8]

不确定你是否会称之为#34;优雅"它仍然很复杂,主要是因为大多数对象返回需要明确转换为list的生成器。

答案 1 :(得分:1)

有几种方法可以做到这一点,这是我的:

import re

txt =  '1-3,6-8'

# Safer to use a raw string
pat  = re.compile(r'(\d{1,3})-(\d{1,3}),(\d{1,3})-(\d{1,3})')
m = pat.match(txt)
if m:
    start1, end1, start2, end2 = m.groups()
    result  = [i for i in range(int(start1), int(end1)+1)]
    result += [i for i in range(int(start2), int(end2)+1)]
    print(result)

给出:

[1, 2, 3, 6, 7, 8]

我在这里假设Python 3(如问题中所述)。

Python 2可以使用:

result  = range(int(start1), int(end1)+1)   
result += range(int(start2), int(end2)+1)

答案 2 :(得分:1)

我假设你也想处理更长的序列,比如1-10,15,23-25?你真的不需要正则表达式,常规字符串处理函数也能正常工作。

def parse_sequence(seq):
    result = []
    for part in seq.split(','):
        points = [int(s) for s in part.split('-')]
        if len(points) == 2:
            result.extend(range(points[0], points[1]+1))
        elif len(points) == 1:
            result.append(points[0])
        else:
            raise ValueError('invalid sequence')
    return result

答案 3 :(得分:0)

取决于您期望看到的确切数据。一般来说,最好的方法是编写一个以块的形式解析字符串的函数。类似的东西:

def parse(string):
    chunks = string.split(',')
    for chunk in chunks:
        match = re.match('(\d+)-(\d+)', chunk)
        if match:
            start = int(match.group(1))
            end = int(match.group(2))
            yield range(start:end+1)
        else:
            yield int(chunk)

答案 4 :(得分:0)

这是我的解决方案:

import re
from itertools import chain

s = '1-3, 6 - 8, 12-14, 20 -22'

rslt = list(chain(*[range(int(tup[0]), int(tup[1]) + 1)
                    for tup in re.findall(r'(\d+)\s*?-\s*?(\d+)', s)]))

输出:

In [43]: rslt
Out[43]: [1, 2, 3, 6, 7, 8, 12, 13, 14, 20, 21, 22]

一步一步:

In [44]: re.findall(r'(\d+)\s*?-\s*?(\d+)', s)
Out[44]: [('1', '3'), ('6', '8'), ('12', '14'), ('20', '22')]

In [45]: [range(int(tup[0]),int(tup[1])+1) for tup in re.findall(r'(\d+)\s*?-\s*?(\d+)', s)]
Out[45]: [range(1, 4), range(6, 9), range(12, 15), range(20, 23)]

答案 5 :(得分:0)

s_tmp = s.split(",")

[*range(x.split("-")int([0]),x.split("-")int(x[1])) for x in s_tmp]
如果存在语法错误,请

道歉。我是通过手机输入的。基本上拆分,然后拆分 - 然后解压缩范围

中的条目
相关问题