Розділити рядок кожного n-го символу?


380

Чи можна розділити рядок кожного n-го символу?

Наприклад, припустимо, у мене є рядок, що містить таке:

'1234567890'

Як я можу зробити так, щоб це виглядало так:

['12','34','56','78','90']

Відповіді:


549
>>> line = '1234567890'
>>> n = 2
>>> [line[i:i+n] for i in range(0, len(line), n)]
['12', '34', '56', '78', '90']

35
Це дійсно чудова відповідь, оскільки її жодним чином не перекручують, і цей факт дозволяє легко запам'ятати метод завдяки його простоті
Тревор Рудольф,

1
@TrevorRudolph Це лише те, що ти йому кажеш. Наведена відповідь насправді є лише циклом for, але виражена пітонічно. Крім того, якщо вам потрібно запам’ятати «спрощену» відповідь, існує щонайменше сотні тисяч способів їх запам'ятати: зіставити сторінку в stackoverflow; копіювання та вставлення в електронний лист; зберігання "корисного" файлу з речами, які ви хочете запам'ятати; просто використовувати сучасну пошукову систему, коли вам щось потрібно; використання закладок у (можливо) кожному веб-переглядачі; пр.
dylnmc

1
На другому , хоча, здається , ніби ви перебуваєте серйозно. Я справді сподіваюся, що ви серйозно, оскільки це насправді не перекручено.
dylnmc

1
я був серйозним, я використовував цей код у своєму бінарному перетворювачі в емуляторі, мені сподобалось, що це пітонік для циклу хааха, але дякую за подальше розбиття, чому мені подобається цей метод!
Тревор Рудольф

5
За іронією долі, намагання вживати слова таким чином, які не матимуть прихованого значення, часто призводить до складних речень.
deed02392

207

Просто для завершення можна зробити це за допомогою регулярного вираження:

>>> import re
>>> re.findall('..','1234567890')
['12', '34', '56', '78', '90']

Для непарної кількості символів ви можете це зробити:

>>> import re
>>> re.findall('..?', '123456789')
['12', '34', '56', '78', '9']

Ви також можете зробити наступне, щоб спростити регулярний вираз для більш довгих фрагментів:

>>> import re
>>> re.findall('.{1,2}', '123456789')
['12', '34', '56', '78', '9']

І ви можете використовувати, re.finditerякщо рядок довгий, щоб генерувати шматок за допомогою шматка.


3
Це, безумовно, найкраща відповідь тут і заслуговує на те, щоб бути на вершині. Можна навіть написати, '.'*nщоб зробити це більш зрозумілим. Ні з'єднання, ні блискавки, ні циклів, ні розуміння списків; просто знайдіть наступних двох персонажів один біля одного, саме так думає людський мозок про це. Якби Монті Пітон був ще живий, він би любив цей метод!
jdk1.0

Це найшвидший метод і для досить довгих струн: gitlab.com/snippets/1908857
Ральф Болтон

Це не працюватиме, якщо рядок містить нові рядки. Це потрібно flags=re.S.
Аран-Фей

аааа .... regex .... чому я не подумав про це XD
містер PizzaGuy

146

Для цього вже існує вбудована функція в python.

>>> from textwrap import wrap
>>> s = '1234567890'
>>> wrap(s, 2)
['12', '34', '56', '78', '90']

Ось що говорить docstring для обгортання:

>>> help(wrap)
'''
Help on function wrap in module textwrap:

wrap(text, width=70, **kwargs)
    Wrap a single paragraph of text, returning a list of wrapped lines.

    Reformat the single paragraph in 'text' so it fits in lines of no
    more than 'width' columns, and return a list of wrapped lines.  By
    default, tabs in 'text' are expanded with string.expandtabs(), and
    all other whitespace characters (including newline) are converted to
    space.  See TextWrapper class for available keyword args to customize
    wrapping behaviour.
'''

2
print (wrap ('12345678', 3)) розбиває рядок на групи з 3 цифр, але починається спереду, а не ззаду. Результат: ['123', '456', '78']
Atalanttore

2
Цікаво дізнатися про «обгортання», але це не те, що саме було запропоновано вище. Він більше орієнтований на показ тексту, а не на розділення рядка на фіксовану кількість символів.
Орен

2
wrapможе не повернути те, що запитується, якщо рядок містить пробіл. наприклад wrap('0 1 2 3 4 5', 2)повернення ['0', '1', '2', '3', '4', '5'](елементи позбавлені)
satomacoto

3
Це дійсно відповідає на питання, але що відбувається, якщо пробіли є, і ви хочете, щоб вони зберігалися в розділених символах? wrap () видаляє пробіли, якщо вони потрапляють відразу після
Залізний адвокат

1
Це погано спрацьовує, якщо ви хочете розділити текст дефісами (число, яке ви наводите як аргумент, насправді МАКСИМАЛЬНЕ число символів, а не точне, і воно розбивається, тобто на дефіси та пробіли).
MrVocabulary

80

Ще один поширений спосіб групування елементів у групи n-довжини:

>>> s = '1234567890'
>>> map(''.join, zip(*[iter(s)]*2))
['12', '34', '56', '78', '90']

Цей метод походить прямо з документів для zip().


2
В [19]: a = "привіт світ"; список (map ("" .join, zip (* [iter (a)] * 4))) отримати результат ['пекло', 'o wo'].
truease.com

16
Якщо комусь zip(*[iter(s)]*2)важко зрозуміти, прочитайте, як zip(*[iter(s)]*n)працює в Python? .
Гріеш Чаухан

15
Це не враховує непарну кількість символів, вона просто скине ці символи: >>> map(''.join, zip(*[iter('01234567')]*5))->['01234']
Bjorn

3
Щоб також обробити непарну кількість символів, просто замініть zip()на itertools.zip_longest():map(''.join, zip_longest(*[iter(s)]*2, fillvalue=''))
Пауло Фрейтас

Також корисні: docs formaps()
winklerrr

57

Я думаю, що це коротше і читабельніше, ніж версія itertools:

def split_by_n(seq, n):
    '''A generator to divide a sequence into chunks of n units.'''
    while seq:
        yield seq[:n]
        seq = seq[n:]

print(list(split_by_n('1234567890', 2)))

7
але не дуже ефективно: коли застосовується до рядків: занадто багато копій
Ерік

1
Він також не працює , якщо слід є генератором, який є те , що версія itertools є для . Не те, щоб ОП просило про це, але справедливо критикувати версію itertool за не так просто.
CryingCyclops

24

Мені подобається таке рішення:

s = '1234567890'
o = []
while s:
    o.append(s[:2])
    s = s[2:]


11

Ви можете використовувати grouper()рецепт із itertools:

Python 2.x:

from itertools import izip_longest    

def grouper(iterable, n, fillvalue=None):
    "Collect data into fixed-length chunks or blocks"
    # grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx
    args = [iter(iterable)] * n
    return izip_longest(fillvalue=fillvalue, *args)

Python 3.x:

from itertools import zip_longest

def grouper(iterable, n, fillvalue=None):
    "Collect data into fixed-length chunks or blocks"
    # grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    return zip_longest(*args, fillvalue=fillvalue)

Ці функції ефективні в пам'яті та працюють з будь-якими ітерабелями.


5

Спробуйте наступний код:

from itertools import islice

def split_every(n, iterable):
    i = iter(iterable)
    piece = list(islice(i, n))
    while piece:
        yield piece
        piece = list(islice(i, n))

s = '1234567890'
print list(split_every(2, list(s)))

Ваша відповідь не відповідає вимозі ОП, ви повинні використовувати, yield ''.join(piece)щоб вона працювала так, як очікувалося: eval.in/813878
Пауло Фрейтас

4
>>> from functools import reduce
>>> from operator import add
>>> from itertools import izip
>>> x = iter('1234567890')
>>> [reduce(add, tup) for tup in izip(x, x)]
['12', '34', '56', '78', '90']
>>> x = iter('1234567890')
>>> [reduce(add, tup) for tup in izip(x, x, x)]
['123', '456', '789']

3

Спробуйте це:

s='1234567890'
print([s[idx:idx+2] for idx,val in enumerate(s) if idx%2 == 0])

Вихід:

['12', '34', '56', '78', '90']

2

Як завжди, для тих, хто любить один лайнер

n = 2  
line = "this is a line split into n characters"  
line = [line[i * n:i * n+n] for i,blah in enumerate(line[::n])]

Коли я запускаю це в Python Fiddle з a, print(line)я отримую this is a line split into n charactersяк вихід. Можливо, вам буде краще ставити line = [line[i * n:i * n+n] for i,blah in enumerate(line[::n])]:? Виправте це, і це хороша відповідь :).
Що є в пошуку Google

Чи можете ви пояснити, ,blahі чому це потрібно? Я помічаю, що можу замінити blahбудь-які альфа-символи / і, але не цифри, і не можу видалити blahабо / і кому. Мій редактор пропонує додати пробіли після ,: s
toonarmycaptain

enumerateповертає два ітерабелі, тож вам потрібно помістити два місця. Але вам насправді не потрібен другий ітерабельний для нічого в цьому випадку.
Даніель Ф

1
Замість того, як blahя вважаю за краще використовувати підкреслення або подвійне підкреслення, дивіться: stackoverflow.com/questions/5893163/…
Andy Royal

1

Просте рекурсивне рішення для короткої струни:

def split(s, n):
    if len(s) < n:
        return []
    else:
        return [s[:n]] + split(s[n:], n)

print(split('1234567890', 2))

Або в такій формі:

def split(s, n):
    if len(s) < n:
        return []
    elif len(s) == n:
        return [s]
    else:
        return split(s[:n], n) + split(s[n:], n)

, що більш наочно ілюструє типовий зразок поділу та підкорення в рекурсивному підході (хоча практично не потрібно робити це таким чином)


1

Я застряг у тому ж сценіорі.

Це працювало для мене

x="1234567890"
n=2
list=[]
for i in range(0,len(x),n):
    list.append(x[i:i+n])
print(list)

Вихідні дані

['12', '34', '56', '78', '90']

0

more_itertools.slicedбуло згадано раніше. Ось ще чотири варіанти з more_itertoolsбібліотеки:

s = "1234567890"

["".join(c) for c in mit.grouper(2, s)]

["".join(c) for c in mit.chunked(s, 2)]

["".join(c) for c in mit.windowed(s, 2, step=2)]

["".join(c) for c in  mit.split_after(s, lambda x: int(x) % 2 == 0)]

Кожен з останніх варіантів дає такий результат:

['12', '34', '56', '78', '90']

Документація для обговорюваних варіантів: grouper, chunked, windowed,split_after


-1

Цього можна досягти простим для циклу.

a = '1234567890a'
result = []

for i in range(0, len(a), 2):
    result.append(a[i : i + 2])
print(result)

Вихід виглядає як ['12', '34', '56', '78', '90', 'a']


2
Хоча цей код може відповісти на питання, надаючи додатковий контекст щодо того, чому та / або як цей код відповідає на питання, покращує його довгострокове значення.
β.εηοιτ.βε

2
Це те саме рішення, що і тут: stackoverflow.com/a/59091507/7851470
Георгій
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.