Podzielić ciąg co n-ty znak?


380

Czy można podzielić ciąg co n-ty znak?

Załóżmy na przykład, że mam ciąg znaków zawierający następujące elementy:

'1234567890'

Jak mogę to wyglądać tak:

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

Odpowiedzi:


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

35
To naprawdę świetna odpowiedź, ponieważ nie jest w żaden sposób skomplikowana, a fakt ten pozwala łatwo zapamiętać metodę ze względu na jej prostotę
Trevor Rudolph

1
@TrevorRudolph Robi dokładnie to, co mówisz. Powyższa odpowiedź jest tak naprawdę tylko pętlą for, ale wyrażona pythonicznie. Ponadto, jeśli chcesz zapamiętać „uproszczoną” odpowiedź, istnieją co najmniej setki tysięcy sposobów, aby je zapamiętać: wstawianie strony na stosie; kopiowanie, a następnie wklejanie do wiadomości e-mail; utrzymywanie „pomocnego” pliku z rzeczami, które chcesz zapamiętać; po prostu używając nowoczesnej wyszukiwarki, gdy czegoś potrzebujesz; używanie zakładek w (prawdopodobnie) każdej przeglądarce internetowej; itd.
dylnmc,

1
Na drugiej jednak wydaje się, że ty jesteś poważny. Mam nadzieję, że mówisz poważnie, ponieważ to naprawdę nie jest skomplikowane.
dylnmc

1
mówiłem poważnie, użyłem tego kodu w moim konwerterze binarnym w emulatorze, podobało mi się, że był to python dla pętli haaha, ale dziękuję za dalsze wyjaśnienie, dlaczego lubię tę metodę!
Trevor Rudolph

5
Jak na ironię, próba użycia słów w sposób, który nie będzie miał ukrytego znaczenia, często prowadzi do zawiłych zdań.
deed02392,

207

Aby być kompletnym, możesz to zrobić za pomocą wyrażenia regularnego:

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

W przypadku nieparzystej liczby znaków możesz to zrobić:

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

Możesz również wykonać następujące czynności, aby uprościć wyrażenie regularne dla dłuższych fragmentów:

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

I możesz użyć, re.finditerjeśli ciąg jest długi, aby wygenerować porcję po porcji.


3
To zdecydowanie najlepsza odpowiedź tutaj i zasługuje na bycie na szczycie. Można nawet napisać, '.'*naby było bardziej jasne. Bez łączenia, bez zipowania, bez pętli, bez zrozumienia listy; po prostu znajdź obok siebie kolejne dwie postacie, dokładnie tak, jak myśli o tym ludzki mózg. Gdyby Monty Python nadal żył, pokochałby tę metodę!
jdk1,0

Jest to najszybsza metoda również na dość długie łańcuchy: gitlab.com/snippets/1908857
Ralph Bolton

To nie zadziała, jeśli ciąg zawiera nowe linie. To potrzebuje flags=re.S.
Aran-Fey

ahhh .... regex .... dlaczego nie pomyślałem o tym XD
Mr PizzaGuy

146

W tym języku jest już wbudowana funkcja.

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

Oto, co mówi dokument do zawijania:

>>> 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)) dzieli ciąg na grupy 3 cyfr, ale zaczyna się od przodu, a nie od tyłu. Wynik: [„123”, „456”, „78”]
Atalanttore,

2
Warto dowiedzieć się o „zawijaniu”, ale nie robi dokładnie tego, o co pytano powyżej. Jest bardziej zorientowany na wyświetlanie tekstu, niż na dzielenie łańcucha na określoną liczbę znaków.
Oren

2
wrapmoże nie zwrócić tego, o co jest poproszony, jeśli ciąg zawiera spację. np. wrap('0 1 2 3 4 5', 2)zwraca ['0', '1', '2', '3', '4', '5'](elementy są rozebrane)
satomacoto,

3
To rzeczywiście odpowiada na pytanie, ale co się stanie, jeśli będą spacje i chcesz, aby były przechowywane w podzielonych postaciach? wrap () usuwa spacje, jeśli spadają bezpośrednio po podzielonej grupie postaci
Iron Attorney

1
Działa to źle, jeśli chcesz podzielić tekst za pomocą łączników (liczba, którą podajesz jako argument, jest w rzeczywistości MAKSYMALNĄ liczbą znaków, a nie dokładną, i łamie się, np. Na łącznikach i białych znakach).
MrVocabulary

80

Innym popularnym sposobem grupowania elementów w grupy o długości n:

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

Ta metoda pochodzi prosto z dokumentacji dla zip().


2
W [19]: a = „witaj świecie”; list (map ("" .join, zip (* [iter (a)] * 4))) pobierz wynik ['hell', 'o wo'].
truease.com

16
Jeśli komuś zip(*[iter(s)]*2)trudno jest zrozumieć, przeczytaj Jak zip(*[iter(s)]*n)działa Python? .
Grijesh Chauhan

15
Nie uwzględnia to nieparzystej liczby znaków, po prostu upuści te znaki: >>> map(''.join, zip(*[iter('01234567')]*5))->['01234']
Bjorn

3
Aby obsługiwać również nieparzystą liczbę znaków po prostu zastąpić zip()z itertools.zip_longest():map(''.join, zip_longest(*[iter(s)]*2, fillvalue=''))
Paulo Freitas

Przydatne także: docs formaps()
winklerrr

57

Myślę, że jest to krótszy i bardziej czytelny niż wersja 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
ale niezbyt wydajne: w przypadku ciągów: zbyt wiele kopii
Eric

1
To również nie działa, jeśli nast jest generator, który jest co wersja itertools jest za . Nie to, że OP o to prosił, ale niesprawiedliwe jest krytykowanie wersji itertoola, która nie jest tak prosta.
CryingCyclops

24

Podoba mi się to rozwiązanie:

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

24

Używanie narzędzi itertools z PyPI:

>>> from more_itertools import sliced
>>> list(sliced('1234567890', 2))
['12', '34', '56', '78', '90']

11

Możesz użyć grouper()przepisu z 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)

Funkcje te są wydajne pod względem pamięci i działają z dowolnymi iteracjami.


5

Wypróbuj następujący kod:

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

Twoja odpowiedź nie spełnia wymagań OP, musisz użyć, yield ''.join(piece)aby działała zgodnie z oczekiwaniami: eval.in/813878
Paulo Freitas

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

Spróbuj tego:

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

Wynik:

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

2

Jak zawsze dla tych, którzy kochają jedną wkładkę

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

Kiedy uruchamiam to w Pythonie Fiddle z a print(line)otrzymuję this is a line split into n charactersjako wynik. Może być lepiej oddanie: line = [line[i * n:i * n+n] for i,blah in enumerate(line[::n])]? Napraw to i to dobra odpowiedź :).
Co jest w wyszukiwarce Google

Czy możesz wyjaśnić ,blahi dlaczego jest to konieczne? Zauważyłem, że mogę zastąpić blahdowolnym znakiem / literami alfa, ale nie cyframi, i nie mogę usunąć blahprzecinka lub. Mój redaktor sugeruje dodanie spacji po ,: s
toonarmycaptain 17.07.17

enumeratezwraca dwie iterowalne, więc potrzebujesz dwóch miejsc do ich umieszczenia. Ale w tym przypadku tak naprawdę nie potrzebujesz drugiej iteracji.
Daniel F

1
Zamiast blahwolę używać podkreślenia lub podwójnego podkreślenia, patrz: stackoverflow.com/questions/5893163/…
Andy Royal

1

Proste rekurencyjne rozwiązanie dla krótkiego łańcucha:

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

print(split('1234567890', 2))

Lub w takiej formie:

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)

, która bardziej szczegółowo ilustruje typowy wzór dziel i zwyciężaj w podejściu rekurencyjnym (chociaż praktycznie nie jest to konieczne)


1

Utknąłem w tym samym scenariuszu.

To zadziałało dla mnie

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

Wynik

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

0

more_itertools.slicedzostało wspomniane wcześniej. Oto cztery dodatkowe opcje z more_itertoolsbiblioteki:

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

Każda z tych ostatnich opcji generuje następujące dane wyjściowe:

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

Dokumentacja dla omawianych opcji: grouper, chunked, windowed,split_after


-1

Można to osiągnąć za pomocą prostej pętli for.

a = '1234567890a'
result = []

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

Wyjście wygląda następująco: „12”, „34”, „56”, „78”, „90”, „a”]


2
Chociaż ten kod może odpowiedzieć na pytanie, zapewnienie dodatkowego kontekstu dotyczącego tego, dlaczego i / lub jak ten kod odpowiada na pytanie, poprawia jego długoterminową wartość.
β.εηοιτ.βε

2
Jest to to samo rozwiązanie, jak tutaj: stackoverflow.com/a/59091507/7851470
Georgy
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.