【问题标题】:Split a string consisting of letters and numbers into parts将由字母和数字组成的字符串拆分为多个部分
【发布时间】:2014-04-04 12:54:02
【问题描述】:

我有一个由交替的小写字母和数字(整数或浮点数)字符串组成的字符串,它的长度是任意的,我希望将它分成多个部分,每个部分都具有最大可能的大小,这样一个部分将由一个字符串或一个(表示一个的字符串)数字组成。

我不需要考虑特殊形式的数字,例如指数、十六进制等;只是简单的浮点数或整数。

几个例子:

>>> split("")
()
>>> split("p")
('p',)
>>> split("2")
('2',)
>>> split("a2b3")
('a', '2', 'b', '3')
>>> split("a2.1b3")
('a', '2.1', 'b', '3')
>>> split("a.1b3")
('a', '.1', 'b', '3')

但是,以下调用会引发一些错误:

>>> split(3)
>>> split("a0.10.2")
>>> split("ab.c")

我的第一次尝试是使用re.split。然而,这种尝试是相当幼稚的,它并没有保存分隔符,以防我写这些字母:

>>> re.split("[a-z]", "a.1b3")
['', '.1', '3']

我的第二次尝试是使用itertools.groupby。问题是它不关心数字的形式,所以,例如:

>>> islowalpha = labmda s: str.isalpha(s) and str.islower(s)
>>> [''.join(g) for _, g in itertools.groupby("a0.10.2b", islowalpha)]  # should raise
['a', '0.10.2', 'b']

注意:我不关心输出的形式,只要它是可迭代的。

注意:我已阅读 this,但无法根据我的问题调整解决方案。主要区别在于我只需要允许可接受的数字,而不是简单的数字和点列表。

【问题讨论】:

  • "a0.10.2" 有什么问题以及为什么它与"a2.1b3" 不同?
  • 因为0.10.2 不是数字;它也不是数字和小写字符串的交替子串。
  • 知道了,错过了“交替”限制。

标签: python regex string


【解决方案1】:
import re

def split_gen(x):
    for f, s in re.findall(r'([\d.]+)|([^\d.]+)', x):
        if f:
            float(f)
            yield f
        else:
            yield s

def split(x):
    '''
    >>> split("")
    ()
    >>> split("p")
    ('p',)
    >>> split("2")
    ('2',)
    >>> split("a2b3")
    ('a', '2', 'b', '3')
    >>> split("a2.1b3")
    ('a', '2.1', 'b', '3')
    >>> split("a.1b3")
    ('a', '.1', 'b', '3')
    >>> split(3)
    Traceback (most recent call last):
    ...
    TypeError: expected string or buffer
    >>> split("a0.10.2")
    Traceback (most recent call last):
    ...
    ValueError: could not convert string to float: '0.10.2'
    >>> split("ab.c")    
    Traceback (most recent call last):
    ...
    ValueError: could not convert string to float: '.'
    '''
    return tuple(split_gen(x))

if __name__ == '__main__':
    import doctest
    doctest.testmod()

【讨论】:

    【解决方案2】:

    玩一下re.subitertools.cycle

    def split(s):
        res = []
    
        def replace(matchobj):
            res.append(matchobj.group(0))
            return ''
    
        letter = re.compile('^([a-z]+)')
        number = re.compile('^(\.\d|\d+\.\d+|\d+)')
    
        if letter.match(s):
            c = itertools.cycle([letter, number])
        else:
            c = itertools.cycle([number, letter])
    
        for op in c:
            mods = op.sub(replace, s)
            if len(s) == len(mods):
                return
            elif not mods:
                return res
            s = mods
    

    基本思想 - 创建两个交替的re 模式并尝试将输入字符串与它们匹配。

    包含一些示例的演示:

    >>> split("2")
    ['2']
    >>> split("a2b3")
    ['a', '2', 'b', '3']
    >>> split("a.1b3")
    ['a', '.1', 'b', '3']
    >>> split("a0.10.2")
    >>> split("ab.c")
    

    【讨论】:

      【解决方案3】:

      问题是您的问题的前提是合理的。如何区分浮点数和任意字符串?有很多解释的方法。例如,
      0.10.2 这可能意味着 0.1、0.2。或 0、.10、.2
      如果号码是 27.6734.98? 您需要先指定数字的类型和格式。例如:每个数字只有一位小数。

      【讨论】:

      • 我不同意你的批评。我明确提到像 27.6734.98 这样的字符串应该会引发错误,因为它们不包含 alternating 小写字母字符串和数字字符串。
      • 对不起,如果这听起来像批评。我的意思是,如果数字部分类似于“27.6734.98”怎么办。因此,完整的示例输入可能类似于“ab27.6734.98h”。
      【解决方案4】:
      import re
      
      string = 'a.2b3c4.5d'
      
      REG_STR = r'([a-zA-Z])|(\.\d+)|(\d+\.\d+)|(\d+)'
      matches = [m.group() for m in re.finditer(REG_STR, string) if re.finditer(REG_STR, string)]
      

      【讨论】:

      • 呃,不读书。正则表达式应该是'([a-zA-Z])|(\.\d+)|(\d+\.\d+)|(\d+)'
      • 用您建议人们使用的答案编辑您的帖子。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-30
      • 1970-01-01
      • 1970-01-01
      • 2013-06-26
      • 1970-01-01
      相关资源
      最近更新 更多