Question

Le len de Python () et les fonctions de remplissage comme string.ljust () ne sont pas tabstop conscients, à savoir qu'ils traitent « \ t » comme tout autre caractère simple largeur, et ne pas ronde len au multiple le plus proche de tabstop . Exemple:

len('Bear\tnecessities\t')

est 17 au lieu de 24 (à savoir 4+ (8-4) +11+ (8-3))

et dire que je veux aussi une pad_with_tabs(s) fonction telle que

pad_with_tabs('Bear', 15) = 'Bear\t\t'

Vous cherchez des implémentations simples de ces - compacité et la lisibilité, l'efficacité première seconde. Ceci est une question fondamentale, mais irritant. @gnibbler - pouvez-vous montrer une solution purement Pythonic, même si elle est moins efficace dire 20x

?

Bien sûr, vous pouvez convertir en arrière en utilisant str.expandtabs (TabWidth), mais c'est maladroit. Importation de mathématiques pour obtenir TABWIDTH * int( math.ceil(len(s)*1.0/TABWIDTH) ) semble aussi overkill massif.

Je ne pouvais pas gérer quoi que ce soit plus élégant que ce qui suit:

TABWIDTH = 8

def pad_with_tabs(s,maxlen):
  s_len = len(s)
  while s_len < maxlen:
    s += '\t'
    s_len += TABWIDTH - (s_len % TABWIDTH)
  return s

et puisque les chaînes de Python sont immuables et à moins que nous voulons singe-patch notre fonction dans le module de chaîne pour l'ajouter comme méthode, nous devons également attribuer au résultat de la fonction:

s = pad_with_tabs(s, ...)

En particulier, je ne pouvais pas obtenir des approches propres à l'aide de la liste-compréhension ou string.join (...)

''.join([s, '\t' * ntabs])

sans-enveloppe spéciale les cas où len (s) est = maxlen déjà.

Quelqu'un peut-il mieux montrer les fonctions len () et pad_with_tabs ()?

Était-ce utile?

La solution

TABWIDTH=8
def my_len(s):
    return len(s.expandtabs(TABWIDTH))

def pad_with_tabs(s,maxlen):
    return s+"\t"*((maxlen-len(s)-1)/TABWIDTH+1)

Pourquoi ai-je utiliser expandtabs()?
Eh bien, il est rapide

$ python -m timeit '"Bear\tnecessities\t".expandtabs()'
1000000 loops, best of 3: 0.602 usec per loop
$ python -m timeit 'for c in "Bear\tnecessities\t":pass'
100000 loops, best of 3: 2.32 usec per loop
$ python -m timeit '[c for c in "Bear\tnecessities\t"]'
100000 loops, best of 3: 4.17 usec per loop
$ python -m timeit 'map(None,"Bear\tnecessities\t")'
100000 loops, best of 3: 2.25 usec per loop

Tout ce qui itère sur votre chaîne va être plus lente, parce que l'itération est ~ 4 fois plus lent que expandtabs même si vous ne faites rien dans la boucle.

$ python -m timeit '"Bear\tnecessities\t".split("\t")'
1000000 loops, best of 3: 0.868 usec per loop

Même diviser simplement sur les onglets prend plus de temps. Vous auriez encore à itérer sur la division et pad chaque élément à la tabstop

Autres conseils

Je crois que ce gnibbler est le meilleur pour la plupart des cas prectical. Mais de toute façon, voici une solution naïve (sans tenir compte CR, LF, etc.) pour calculer la longueur de la chaîne sans créer de copie étendue:

def tab_aware_len(s, tabstop=8):
    pos = -1
    extra_length = 0
    while True:
        pos = s.find('\t', pos+1)
        if pos<0:
            return len(s) + extra_length
        extra_length += tabstop - (pos+extra_length) % tabstop - 1

Probablement il pourrait être utile pour certaines chaînes énormes ou même des fichiers de mémoire mappées. Et voici la fonction de remplissage un peu optimisé:

def pad_with_tabs(s, max_len, tabstop=8):
    length = tab_aware_len(s, tabstop)
    if length<max_len:
        s += '\t' * ((max_len-1)//tabstop + 1 - length//tabstop)
    return s

TABWIDTH * int( math.ceil(len(s)*1.0/TABWIDTH) ) est en effet un massif sur-tuer; vous pouvez obtenir beaucoup plus simplement le même résultat. Pour i positif et n, utilisez:

def round_up_positive_int(i, n):
    return ((i + n - 1) // n) * n

Cette procédure fonctionne à peu près toutes les langues que j'ai jamais utilisé, après la traduction appropriée.

Ensuite, vous pouvez faire next_pos = round_up_positive_int(len(s), TABWIDTH)

Pour une légère augmentation de l'élégance de votre code, au lieu de

while(s_len < maxlen):

utilisez ceci:

while s_len < maxlen:
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top