Python tabstop-aware len()和padding函数

Python tabstop-aware len()和padding函数,python,function,padding,tabstop,Python,Function,Padding,Tabstop,Python的len()和string.ljust()等填充函数不支持tabstop,也就是说,它们将“\t”视为任何其他单宽字符,并且不会将len四舍五入到tabstop的最近倍数。 例如: len('Bear\tnecessities\t') 是17而不是24(即4+(8-4)+11+(8-3)) 假设我还需要一个功能pad\u,带有选项卡 pad_with_tabs('Bear', 15) = 'Bear\t\t' 寻找这些的简单实现——紧凑性和可读性第一,效率第二。 这是一个基本但令

Python的len()和string.ljust()等填充函数不支持tabstop,也就是说,它们将“\t”视为任何其他单宽字符,并且不会将len四舍五入到tabstop的最近倍数。 例如:

len('Bear\tnecessities\t')
是17而不是24(即4+(8-4)+11+(8-3))

假设我还需要一个功能
pad\u,带有选项卡

pad_with_tabs('Bear', 15) = 'Bear\t\t'
寻找这些的简单实现——紧凑性和可读性第一,效率第二。 这是一个基本但令人恼火的问题。 @gnibbler-你能展示一个纯粹的Pythonic解决方案吗,即使它的效率降低了20倍

当然,您可以使用str.expandtabs(TABWIDTH)来回转换,但这太笨重了。 导入math以获得
TABWIDTH*int(math.ceil(len(s)*1.0/TABWIDTH))
似乎也有点过分了

我没有比下面更优雅的了:

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
特别是,我无法使用列表理解或string.join(…)获得干净的方法

在没有特殊外壳的情况下,len(s)小于TABWIDTH的整数倍,或者len(s)>=maxlen

有人能用_tabs()函数更好地显示len()和pad_吗

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)
为什么我要使用
expandtabs()

嗯,很快

$ 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
任何对字符串进行迭代的操作都会变慢,因为即使在循环中什么都不做,迭代的速度也会比
expandtabs
慢4倍

$ python -m timeit '"Bear\tnecessities\t".split("\t")'
1000000 loops, best of 3: 0.868 usec per loop
即使只是在标签上拆分也需要更长的时间。您仍然需要迭代分割并将每个项目填充到tabstop中

TABWIDTH*int(math.ceil(len(s)*1.0/TABWIDTH))
确实是一个巨大的过度杀伤力;你可以更简单地得到同样的结果。对于正
i
n
,使用:

def round_up_positive_int(i, n):
    return ((i + n - 1) // n) * n
经过适当的翻译后,这个过程几乎可以用我用过的任何语言

然后您可以执行
next\u pos=round\u up\u positive\u int(len(s),TABWIDTH)

稍微增加代码的优雅度,而不是

while(s_len < maxlen):
while(s_len
使用以下命令:

while s_len < maxlen:
s_len
我相信gnibbler's最适合大多数临床前病例。但无论如何,这里有一个简单的(不考虑CR、LF等)解决方案,可以在不创建扩展副本的情况下计算字符串的长度:

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
def tab_aware_len(s,tabstop=8):
位置=-1
额外长度=0
尽管如此:
pos=s.find('\t',pos+1)

如果pos不幸的是,我无法使用接受的答案“原样”,所以这里有一个稍微修改的版本,以防有人遇到同样的问题并通过搜索发现此帖子:

from decimal import Decimal, ROUND_HALF_UP
TABWIDTH = 4

def pad_with_tabs(src, max_len):
    return src + "\t" * int(
        Decimal((max_len - len(src.expandtabs(TABWIDTH))) / TABWIDTH + 1).quantize(0, ROUND_HALF_UP))


def pad_fields(input):
    result = []
    longest = max(len(x) for x in input)
    for row in input:
        result.append(pad_with_tabs(row, longest))
    return result

输出列表包含正确填充的行,这些行的制表符计数是四舍五入的,因此结果数据将具有相同的缩进级别,而不考虑角。5种情况下,原始答案中没有添加制表符。

不清楚您想要什么,也不清楚str.expandtabs如何与账单不符。一些输入和输出示例有助于澄清问题。我在第一行中说了我想要的:len()和string.ljust()的实现,它们都是tabstop感知的。str.expandtabs()不符合要求,因为它会将选项卡炸成空格。如果我们只想测量len(),我们就不想这样。通过获取len(string.expandtabs)生成一次性副本似乎是浪费。“通过获取len(string.expandtabs)生成一次性副本似乎是浪费。”为什么?对我来说,它似乎简单而干净。您是否有任何特定的配置文件编号表明这是应用程序中的瓶颈?如果您真的需要性能,只需使用CFolks编写函数-让我们忽略效率,转而使用紧凑可读的Pythonic代码?(即使效率降低了20倍)gnibbler有一个很好的解决方案,任何其他条目?
pad_with_tabs
都应该调用
my_len
,而不是
len
,以防字符串中有嵌入的制表符要进行制表符填充。
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
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
from decimal import Decimal, ROUND_HALF_UP
TABWIDTH = 4

def pad_with_tabs(src, max_len):
    return src + "\t" * int(
        Decimal((max_len - len(src.expandtabs(TABWIDTH))) / TABWIDTH + 1).quantize(0, ROUND_HALF_UP))


def pad_fields(input):
    result = []
    longest = max(len(x) for x in input)
    for row in input:
        result.append(pad_with_tabs(row, longest))
    return result