Fractionner la chaîne tous les n caractères?


382

Est-il possible de diviser une chaîne tous les n caractères?

Par exemple, supposons que j'ai une chaîne contenant les éléments suivants:

'1234567890'

Comment puis-je le faire ressembler à ceci:

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

Réponses:


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

35
C'est une très bonne réponse car elle n'est pas compliquée en aucune façon et ce fait vous permet de vous souvenir facilement de la méthode en raison de sa simplicité
Trevor Rudolph

1
@TrevorRudolph Il ne fait exactement ce que vous lui dites. La réponse ci-dessus n'est vraiment qu'une boucle for mais exprimée de manière python. De plus, si vous devez vous souvenir d'une réponse "simpliste", il existe au moins des centaines de milliers de façons de les mémoriser: en mettant la page en vedette sur stackoverflow; copier puis coller dans un e-mail; garder un fichier "utile" avec des choses dont vous voulez vous souvenir; en utilisant simplement un moteur de recherche moderne chaque fois que vous avez besoin de quelque chose; utiliser des signets dans (probablement) tous les navigateurs Web; etc.
dylnmc

1
Le deuxième cependant, il semble que vous soyez sérieux. J'espère vraiment que vous êtes sérieux car ce n'est vraiment pas compliqué.
dylnmc

1
j'étais sérieux, j'ai utilisé ce code dans mon convertisseur binaire dans un émulateur, j'ai aimé que ce soit un pythonic pour la boucle haaha mais merci de m'avoir expliqué pourquoi j'apprécie la méthode!
Trevor Rudolph

5
Ironiquement, essayer d'utiliser des mots d'une manière qui n'aura pas de sens caché entraînera souvent des phrases alambiquées.
deed02392

208

Juste pour être complet, vous pouvez le faire avec une expression régulière:

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

Pour un nombre impair de caractères, vous pouvez le faire:

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

Vous pouvez également effectuer les opérations suivantes, pour simplifier l'expression régulière pour des segments plus longs:

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

Et vous pouvez utiliser re.finditersi la chaîne est longue pour générer morceau par morceau.


3
C'est de loin la meilleure réponse ici et mérite d'être au top. On pourrait même écrire '.'*npour le rendre plus clair. Pas de jointure, pas de fermeture éclair, pas de boucles, pas de compréhension de liste; il suffit de trouver les deux personnages suivants l'un à côté de l'autre, ce qui est exactement la façon dont le cerveau humain y pense. Si Monty Python était encore en vie, il adorerait cette méthode!
jdk1.0

C'est aussi la méthode la plus rapide pour les cordes raisonnablement longues: gitlab.com/snippets/1908857
Ralph Bolton

Cela ne fonctionnera pas si la chaîne contient des sauts de ligne. Cela a besoin flags=re.S.
Aran-Fey

ahhh .... regex .... pourquoi n'ai-je pas pensé à ça XD
Mr PizzaGuy

148

Il existe déjà une fonction intégrée en python pour cela.

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

Voici ce que dit la docstring pour wrap:

>>> 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)) divise la chaîne en groupes de 3 chiffres, mais commence devant et non derrière. Résultat: ['123', '456', '78']
Atalanttore

2
Il est intéressant d'en savoir plus sur le «wrap», mais il ne fait pas exactement ce qui a été demandé ci-dessus. Il est plus orienté vers l'affichage de texte, plutôt que de diviser une chaîne en un nombre fixe de caractères.
Oren

2
wrappeut ne pas retourner ce qui est demandé si la chaîne contient de l'espace. par exemple wrap('0 1 2 3 4 5', 2)retours ['0', '1', '2', '3', '4', '5'](les éléments sont dépouillés)
satomacoto

3
Cela répond en effet à la question, mais que se passe-t-il s'il y a des espaces et que vous souhaitez les conserver dans les caractères séparés? wrap () supprime les espaces s'ils tombent directement après un groupe de personnages divisé
Iron Attorney

1
Cela fonctionne mal si vous voulez diviser du texte avec des tirets (le nombre que vous donnez comme argument est en fait le nombre MAXIMUM de caractères, pas exact, et il se casse par exemple sur les tirets et les espaces blancs).
MrVocabulary

81

Une autre façon courante de regrouper des éléments en groupes de n longueurs:

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

Cette méthode provient directement de la documentation de zip().


2
Dans [19]: a = "bonjour le monde"; list (map ("" .join, zip (* [iter (a)] * 4))) obtient le résultat ['hell', 'o wo'].
truease.com

16
Si quelqu'un trouve zip(*[iter(s)]*2)difficile à comprendre, lisez Comment zip(*[iter(s)]*n)fonctionne en Python? .
Grijesh Chauhan

15
Cela ne tient pas compte d'un nombre impair de caractères, cela supprimera simplement ces caractères: >>> map(''.join, zip(*[iter('01234567')]*5))->['01234']
Bjorn

3
Pour gérer également un nombre impair de caractères, il suffit de remplacer zip()par itertools.zip_longest():map(''.join, zip_longest(*[iter(s)]*2, fillvalue=''))
Paulo Freitas

Également utile: documents pourmaps()
winklerrr

58

Je pense que c'est plus court et plus lisible que la version 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
mais pas vraiment efficace: appliqué aux cordes: trop de copies
Eric

1
Elle ne fonctionne pas si seq est un générateur, qui est ce que la version itertools est pour . Non pas qu'OP l'ait demandé, mais il n'est pas juste de critiquer la version d'itertool qui n'est pas aussi simple.
CryingCyclops

25

J'aime cette solution:

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


12

Vous pouvez utiliser la grouper()recette de 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)

Ces fonctions sont efficaces en mémoire et fonctionnent avec tous les itérables.


6

Essayez le code suivant:

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

Votre réponse ne répond pas aux exigences de l'OP, vous devez utiliser yield ''.join(piece)pour le faire fonctionner comme prévu: eval.in/813878
Paulo Freitas

5
>>> 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']

4

Essaye ça:

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

Production:

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

3

Comme toujours, pour ceux qui aiment un liners

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

Lorsque je lance cela en Python Fiddle avec un print(line)je reçois this is a line split into n characterscomme sortie. Pourriez-vous mieux mettre line = [line[i * n:i * n+n] for i,blah in enumerate(line[::n])]:? Corrigez cela et c'est une bonne réponse :).
Contenu d'une recherche Google

Pouvez-vous expliquer le ,blahet pourquoi c'est nécessaire? Je remarque que je peux remplacer blahpar n'importe quel caractère alpha, mais pas de chiffres, et je ne peux pas supprimer le blah/ ou la virgule. Mon éditeur suggère d'ajouter un espace après ,: s
toonarmycaptain

enumeraterenvoie deux itérables, vous avez donc besoin de deux emplacements pour les mettre. Mais vous n'avez en fait pas besoin du deuxième itérable pour quoi que ce soit dans ce cas.
Daniel F

1
Plutôt que blahje préfère utiliser un trait de soulignement ou un double trait de soulignement, voir: stackoverflow.com/questions/5893163/…
Andy Royal

2

Une solution récursive simple pour chaîne courte:

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

print(split('1234567890', 2))

Ou sous une telle forme:

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)

, qui illustre le modèle typique de division et de conquête dans une approche récursive de manière plus explicite (bien qu'il ne soit pas nécessaire de le faire de cette façon)


2

J'étais coincé dans le même scénario.

Cela a fonctionné pour moi

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

Production

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

1

more_itertools.sliceda déjà été mentionné . Voici quatre autres options de la more_itertoolsbibliothèque:

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

Chacune de ces dernières options produit la sortie suivante:

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

Documentation pour les options discutées: grouper, chunked, windowed,split_after


0

Ceci peut être réalisé par une simple boucle for.

a = '1234567890a'
result = []

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

La sortie ressemble à ['12', '34', '56', '78', '90', 'a']


2
Bien que ce code puisse répondre à la question, fournir un contexte supplémentaire concernant pourquoi et / ou comment ce code répond à la question améliore sa valeur à long terme.
β.εηοιτ.βε

2
C'est la même solution qu'ici: stackoverflow.com/a/59091507/7851470
Georgy
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.