【问题标题】:Split the string into different lengths chunks将字符串拆分为不同长度的块
【发布时间】:2020-12-22 18:11:47
【问题描述】:

为了正确格式化字符串,我需要将它分成不同长度的块。

例如,这是一个字符串 - 25c319f75e3fbed5a9f0497750ea12992b30d565,为了将其拆分为固定长度的块,我会简单地使用步骤和切片:

s = '25c319f75e3fbed5a9f0497750ea12992b30d565'
n = 2
print("-".join([s[i:i+n] for i in range(0, len(s), n)]))

但是,如果n 是要拆分的数字列表,我该怎么办,例如:

s = '25c319f75e3fbed5a9f0497750ea12992b30d565'
n = [8, 4, 4, 4, 4, 12] # edited for consistency - Coldspeed

我提出的唯一解决方案是:

print("-".join([s[0:8], s[8:12], s[12:16], s[16:20], s[20:24], s[24:32]]))

这不是pythonic,更不一定可靠的字符串长度很大。

最后一个代码示例的输出:

25c319f7-5e3f-bed5-a9f0-4977-50ea1299

那么这可以用更 Pythonic 的单线方式完成吗?如果不是,还有什么其他更自动化的方法可以做到这一点?

【问题讨论】:

    标签: python loops


    【解决方案1】:

    从字符串创建一个迭代器并使用itertools.islice进行增量切片:

    from itertools import islice
    
    s = '25c319f75e3fbed5a9f0497750ea12992b30d565'
    it = iter(s)
    n = [8, 4, 4, 12]
    
    s = '-'.join(''.join(islice(it, None, x)) for x in n)
    print(s)
    # 25c319f7-5e3f-bed5-a9f0497750ea
    

    请注意,如果切片的总大小不等于字符串的长度,则字符串的尾部会丢失;迭代器未完全耗尽。

    您可以在最后的预处理阶段附加尾随部分(如果需要):

    s += '-' + ''.join(it)
    print(s)
    # 25c319f7-5e3f-bed5-a9f0497750ea-12992b30d565
    

    这是另一种使用 for 循环的方法,通过增加起始索引来逐步分割字符串:

    start = 0
    d = []
    for i in n:
       d.append(s[start:start+i])
       start += i
    d.append(s[start:])
    print('-'.join(d))
    # 25c319f7-5e3f-bed5-a9f0497750ea-12992b30d565
    

    【讨论】:

    • 感谢回答,没有itertools的帮助是做不到的对吗?
    • @ShellRox 有什么理由不想使用 itertools?
    • @ShellRox 我想你可能想休息一下。我不打算进一步评论。此处提供的所有答案都与您在问题中描述的完全一致。
    • @ShellRox 老实说,我认为您应该选择此答案。它清晰,简洁,非常pythonic,并且完全按照您的示例所要求的。如果不是,您在解释问题时应该更清楚,以免浪费您和我们的时间。
    • @ShellRox 是的。我已经编辑了你的问题以保持一致性。就像我说的,一定要清楚你的问题,你会为每个人省去很多麻烦。干杯。
    【解决方案2】:
    >>> s = '25c319f75e3fbed5a9f0497750ea12992b30d565'
    >>> n = [8, 4, 4, 4, 4, 12]
    >>> print '-'.join([s[sum(n[:i]):sum(n[:i+1])] for i in range(len(n))])
    

    输出

    25c319f7-5e3f-bed5-a9f0-4977-50ea12992b30
    

    【讨论】:

    • 感谢您的回答。输出是 - ---19f75e3f
    • 现在输出是 - --19f7-5e3
    • @ShellRox 有点晚了,但这里有一个没有 itertools 的 1 班轮。请注意它效率低下,因为它一直在调用 sum。但它有效。
    【解决方案3】:

    恐怕不是单线,而是从我的头顶:

    s = '25c319f75e3fbed5a9f0497750ea12992b30d565'
    n = [8, 4, 4, 4, 4, 12]
    res=[]
    for split in n:
        temp=s[:split]
        s=s[split:]
        res.append(temp) 
    print(res)
    

    输出是一个数组,其中包含可以相应操作的相应字符串:

     ['25c319f7', '5e3f', 'bed5', 'a9f0', '4977', '50ea12992b30']
    

    【讨论】:

      【解决方案4】:
      s = '25c319f75e3fbed5a9f0497750ea12992b30d565'
      n = [8, 4, 4, 12]
      
      def make_chunks(s,n):
          result = []
          for length in n:
              result.append(s[:length])
              s = s[length:]
          if s:
              result.append(s)
          return '-'.join(result)
      
      print(make_chunks(s,n))
      

      【讨论】:

        【解决方案5】:

        如果我们从列出的数据开始:

        string = '25c319f75e3fbed5a9f0497750ea12992b30d565'
        lengths = [8, 4, 4, 12]
        

        我们可以使用扫描来查找每个部分的开头或结尾:

        import itertools
        ends = list(itertools.accumulate(lengths))
        

        看来accumulate 是特定于 Python 3 的,因此我们可能需要一种解决方法来在 Python 2 中进行扫描(这个在 O(n²) 时很慢):

        starts = [sum(lengths[:i]) for i in range(len(lengths))]
        

        然后我们可以使用组合来提取部分:

        dashed = '-'.join(string[end-length : end]
                          for end,length in zip(ends,lengths))
        

        所有这些长度/索引操作的优点是它不会创建字符串的副本,只会创建其各个部分。否则,肖恩的解决方案非常简洁。

        【讨论】:

        • 感谢您的回答,绝对有用,但我正在尝试找到仅使用常规 python 的解决方案。
        • 这不是普通的Python吗?都是标准库。
        • 对不起,误解,我的意思是我试图避免在这种情况下使用模块,因为它是可能的。
        • 总是有可能的,但它是否有效或清晰是另一回事。我展示的 Python 2 理解就是一个不使用 itertools 模块的例子。
        • 我需要使用 Python 3 特有的使用 itertools 累积的 ends 数组,python2 有什么替代方法吗?
        【解决方案6】:

        如果可能的字符集已知并且每个块的大小是固定的,也可以使用正则表达式和capturing groups

        import re
        
        s = '25c319f75e3fbed5a9f0497750ea12992b30d565'
        n = [8, 4, 4, 4, 4, 12]
        
        pattern = ''
        for nchars in n:
            pattern += f'([\w]{{{nchars}}})'
        print(pattern)
        # '([\\w]{8})([\\w]{4})([\\w]{4})([\\w]{4})([\\w]{4})([\\w]{12})'
        
        pattern = re.compile(pattern, re.ASCII)
        matches = pattern.search(s)
        
        print(matches.groups())
        # ('25c319f7', '5e3f', 'bed5', 'a9f0', '4977', '50ea12992b30')
        
        print("-".join(matches.groups()))
        # '25c319f7-5e3f-bed5-a9f0-4977-50ea12992b30'
        

        正则表达式模式基本上是以下的重复模式:

        ([\\w]{n})
        

        对于块大小列表中的每个 n 项目,其中:

        • (...) 表示“匹配括号内的任何正则表达式,并指示组的开始和结束;执行匹配后可以检索组的内容,并且可以在字符串中稍后匹配"
        • \w 表示“匹配 ASCII 字符集中被认为是字母数字的字符;这相当于 [a-zA-Z0-9_]。”(假设是 re.ASCII 标志)
        • {n} 表示“指定应该精确匹配前一个 RE 的 n 副本;较少的匹配会导致整个 RE 不匹配。例如,a{6} 将精确匹配六个 'a' 字符,但不匹配五。"

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-11-20
          • 1970-01-01
          • 2012-06-29
          • 1970-01-01
          • 1970-01-01
          • 2011-05-05
          • 2015-10-05
          • 1970-01-01
          相关资源
          最近更新 更多