Répétez la chaîne à une certaine longueur

205

Quel est un moyen efficace de répéter une chaîne à une certaine longueur? Par exemple:repeat('abc', 7) -> 'abcabca'

Voici mon code actuel:

def repeat(string, length):
    cur, old = 1, string
    while len(string) < length:
        string += old[cur-1]
        cur = (cur+1)%len(old)
    return string

Existe-t-il une meilleure façon (plus pythonique) de procéder? Peut-être en utilisant la compréhension de liste?

John Howard
la source

Réponses:

73
def repeat_to_length(string_to_expand, length):
   return (string_to_expand * ((length/len(string_to_expand))+1))[:length]

Pour python3:

def repeat_to_length(string_to_expand, length):
    return (string_to_expand * (int(length/len(string_to_expand))+1))[:length]
Jason Scheirer
la source
5
On dirait que cela profite de la division entière. Cela n'a-t-il pas besoin d'être //en Python 3? Ou laisser tomber +1et utiliser un appel explicite à une fonction de plafond suffirait. Aussi, une remarque: la chaîne générée a en fait une répétition supplémentaire lorsqu'elle se divise uniformément; le surplus est coupé par l'épissure. Cela m'a dérouté au début.
jpmc26
int()fait la même chose ici, mais oui, //pourrait être microscopiquement plus rapide, car il fait la division et le plancher en une seule commande au lieu de deux.
Doyousketch2
667

La réponse de Jason Scheirer est correcte mais pourrait utiliser un peu plus d'exposé.

Tout d'abord, pour répéter une chaîne un nombre entier de fois, vous pouvez utiliser une multiplication surchargée:

>>> 'abc' * 7
'abcabcabcabcabcabcabc'

Donc, pour répéter une chaîne jusqu'à ce qu'elle soit au moins aussi longue que la longueur souhaitée, vous calculez le nombre approprié de répétitions et la placez sur le côté droit de cet opérateur de multiplication:

def repeat_to_at_least_length(s, wanted):
    return s * (wanted//len(s) + 1)

>>> repeat_to_at_least_length('abc', 7)
'abcabcabc'

Ensuite, vous pouvez le couper à la longueur exacte que vous souhaitez avec une tranche de tableau:

def repeat_to_length(s, wanted):
    return (s * (wanted//len(s) + 1))[:wanted]

>>> repeat_to_length('abc', 7)
'abcabca'

Alternativement, comme suggéré dans la réponse de pillmod que personne ne fait probablement défiler suffisamment pour remarquer, vous pouvez utiliser divmodpour calculer le nombre de répétitions complètes nécessaires et le nombre de caractères supplémentaires, à la fois:

def pillmod_repeat_to_length(s, wanted):
    a, b = divmod(wanted, len(s))
    return s * a + s[:b]

Ce qui est mieux? Essayons de le comparer:

>>> import timeit
>>> timeit.repeat('scheirer_repeat_to_length("abcdefg", 129)', globals=globals())
[0.3964178159367293, 0.32557755894958973, 0.32851039397064596]
>>> timeit.repeat('pillmod_repeat_to_length("abcdefg", 129)', globals=globals())
[0.5276265419088304, 0.46511475392617285, 0.46291469305288047]

Donc, la version de pillmod est quelque chose comme 40% plus lente, ce qui est dommage, car personnellement, je pense que c'est beaucoup plus lisible. Il y a plusieurs raisons possibles à cela, à commencer par sa compilation avec environ 40% d'instructions de bytecode supplémentaires.

Remarque: ces exemples utilisent l' //opérateur new-ish pour tronquer la division entière. Ceci est souvent appelé une fonctionnalité Python 3, mais selon PEP 238 , elle a été introduite tout au long de Python 2.2. Vous n'avez qu'à l'utiliser en Python 3 (ou dans les modules qui l'ont from __future__ import division) mais vous pouvez l' utiliser malgré tout.

zwol
la source
8
Non, OP veut que le résultat soit de longueur 7 (qui n'est pas un multiple de 3).
IanS
1
Je suis un peu en conflit car ce n'est pas la bonne réponse pour OP mais c'est la bonne réponse pour moi et 489 autres personnes ...
Matt Fletcher
2
@MattFletcher Vous venez de me pousser sur la ligne de "Je devrais réécrire ceci comme explicateur de la réponse acceptée" à "Je vais réécrire ceci ..." ;-)
zwol
60

C'est assez pythonique:

newstring = 'abc'*5
print newstring[0:6]
Helen K
la source
14
0:7si vous voulez 7 caractères comme OP.
Mad Physicist
34
def rep(s, m):
    a, b = divmod(m, len(s))
    return s * a + s[:b]
pillmuncher
la source
14
from itertools import cycle, islice
def srepeat(string, n):
   return ''.join(islice(cycle(string), n))
kennytm
la source
C'est ce que j'utilise lorsque j'ai seulement besoin d'itérer sur la chaîne (aucune jointure n'est alors nécessaire). Laissez les bibliothèques python faire le travail.
wihlke
7

Peut-être pas la solution la plus efficace, mais certainement courte et simple:

def repstr(string, length):
    return (string * length)[0:length]

repstr("foobar", 14)

Donne "foobarfoobarfo". Une chose à propos de cette version est que si la longueur <len (chaîne) alors la chaîne de sortie sera tronquée. Par exemple:

repstr("foobar", 3)

Donne "foo".

Edit: à ma grande surprise, c'est plus rapide que la solution actuellement acceptée (la fonction 'repeat_to_length'), du moins sur les chaînes courtes:

from timeit import Timer
t1 = Timer("repstr('foofoo', 30)", 'from __main__ import repstr')
t2 = Timer("repeat_to_length('foofoo', 30)", 'from __main__ import repeat_to_length')
t1.timeit()  # gives ~0.35 secs
t2.timeit()  # gives ~0.43 secs

Vraisemblablement, si la chaîne était longue ou si la longueur était très élevée (c'est-à-dire si le gaspillage de la string * lengthpièce était élevé), elle fonctionnerait mal. Et en fait, nous pouvons modifier ce qui précède pour vérifier cela:

from timeit import Timer
t1 = Timer("repstr('foofoo' * 10, 3000)", 'from __main__ import repstr')
t2 = Timer("repeat_to_length('foofoo' * 10, 3000)", 'from __main__ import repeat_to_length')
t1.timeit()  # gives ~18.85 secs
t2.timeit()  # gives ~1.13 secs
Adam Parkin
la source
1
Vous pouvez ajouter un commutateur entre les deux versions en fonction des longueurs d'entrée et de sortie pour une optimisation maximale.
Mad Physicist
6

Que diriez-vous string * (length / len(string)) + string[0:(length % len(string))]

murgatroid99
la source
length / len(string)doit être enveloppé entre parenthèses, et vous manquez le dernier ].
MikeWyatt
1
Le plus lisible / intuitif jusqu'à présent, à mon avis. Je pense que vous devez utiliser //pour la division entière en Python 3. Le 0dans l'épissure est facultatif. (Le colon est obligatoire, bien sûr.)
jpmc26
6

j'utilise ceci:

def extend_string(s, l):
    return (s*l)[:l]
김민준
la source
5

Non pas qu'il n'y ait pas eu assez de réponses à cette question, mais il y a une fonction de répétition; suffit de faire une liste puis de rejoindre la sortie:

from itertools import repeat

def rep(s,n):
  ''.join(list(repeat(s,n))
Amy Platt
la source
Cela ne répond pas à la question. Celui-ci répète la chaîne X fois, il ne la répète pas avant la longueur X. Par exemple, "abc", 4je m'attendrais "abca". Cela créeraitabcabcabcabc
Marcus Lind
3

Ouais récursivité!

def trunc(s,l):
    if l > 0:
        return s[:l] + trunc(s, l - len(s))
    return ''

N'augmentera pas pour toujours, mais c'est bien pour les petites cordes. Et c'est joli.

J'avoue que je viens de lire le Little Schemer et j'aime la récursivité en ce moment.

Triptyque
la source
1

C'est une façon de le faire en utilisant une compréhension de liste, bien que cela devienne de plus en plus inutile à mesure que la longueur de la rptchaîne augmente.

def repeat(rpt, length):
    return ''.join([rpt for x in range(0, (len(rpt) % length))])[:length]
vezult
la source
0

Une autre approche de la PF:

def repeat_string(string_to_repeat, repetitions):
    return ''.join([ string_to_repeat for n in range(repetitions)])
Aleš Kotnik
la source
0
def extended_string (word, length) :

    extra_long_word = word * (length//len(word) + 1)
    required_string = extra_long_word[:length]
    return required_string

print(extended_string("abc", 7))
Aditya Verma
la source