将字符串拆分为不同长度的块

时间:2017-06-20 08:47:42

标签: python loops

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

作为一个例子,这是一个字符串 - 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的单线方式来完成吗?如果没有,还有什么其他更自动的方法可以完成?

6 个答案:

答案 0 :(得分:5)

使用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

答案 1 :(得分:1)

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

答案 2 :(得分:1)

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

答案 3 :(得分:0)

不是一个单线性的我害怕,但从我的头顶开始:

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 :(得分:0)

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

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

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

答案 5 :(得分:0)

如果已知可能的字符集并且每个块的大小是固定的,也可以使用正则表达式和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”字符,但不是五个。"