【问题标题】:How to elegantly transform '1-3,6-8' to '1 2 3 6 7 8' within a list?如何优雅地将列表中的“1-3,6-8”转换为“1 2 3 6 7 8”?
【发布时间】:2017-03-19 08:41:00
【问题描述】:

问题

背景:

我有一个包含大约 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']

【问题讨论】:

  • 您熟悉正则表达式捕获组吗?不用split
  • 把它分解成更小的问题。你问错了问题,实际上根本不需要正则表达式来做到这一点。
  • Bryan,我对它们并不十分熟悉,但我想我已经阅读了足够多的内容来了解​​如何在不使用 split() 的情况下处理它
  • jonrsharpe,我正在使用正则表达式来识别处理不规则数据的情况。没有正则表达式如何识别案例?

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


【解决方案1】:

捕获组更容易。

然后将组列表转换为整数,并在列表推导中对它们进行 2×2 处理,并与 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]

不过,不确定您是否称其为“优雅”。它仍然很复杂,主要是因为大多数对象返回需要显式转换为 list 的生成器。

【讨论】:

  • 并不是说它更优雅,但是 list(...) 调用都可以通过解包来代替:print(*(chain(*(range(groups[i],groups[i+1]+1) for i in range(0,len(groups),2)))))
【解决方案2】:

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

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)

【讨论】:

  • Python 3 也可以做到这一点。 result = list(range(...))result += range(...) 在 Python 3 中都可以正常工作。
【解决方案3】:

具体取决于您希望看到的数据。一般来说,最好的方法是编写一个以块为单位解析字符串的函数。比如:

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】:

    我假设您也想处理更长的序列,例如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
    

    【讨论】:

    • 是的,我想处理扩展案例。我在开始时使用正则表达式来识别将提交给函数的数据。我有一个包含大约 10,000 个列表的列表,其中包含各种数据,并且这些特定数据需要就地转换,因为这只是我正在处理的众多案例之一。
    【解决方案5】:

    这是我的解决方案:

    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)]
    

    【讨论】:

      【解决方案6】:
      s_tmp = s.split(",")
      
      [*range(x.split("-")int([0]),x.split("-")int(x[1])) for x in s_tmp]
      

      如有语法错误,请见谅。我正在用我的手机输入这个。基本上拆分,然后拆分 - 然后从范围中解压缩条目

      【讨论】:

        猜你喜欢
        • 2022-10-08
        • 2021-05-30
        • 2011-09-08
        • 1970-01-01
        • 2021-02-15
        • 2012-10-19
        • 2013-07-26
        • 2020-03-31
        • 1970-01-01
        相关资源
        最近更新 更多