更简单/更快/更优雅的方法在自定义位置拆分字符串

4
我一直在寻找更简单的方法来完成这个任务,但我不确定要使用什么搜索参数。我有一个浮点数,想要将其四舍五入,转换为字符串,然后在字符串上指定自定义格式。我已经阅读了.format文档,但无法确定是否可以使用普通字符串格式化来完成此操作。
我想要的输出只是一个普通的字符串,每三个字符之间有空格,除了最后几个字符,在末尾前四个字符处有一个空格。
例如,我制作了这个复杂的函数,以一种低效的方式实现了我想要的功能:
def my_formatter(value):
    final = []
    # round float and convert to list of strings of individual chars
    c = [i for i in '{:.0f}'.format(value)]
    if len(c) > 3:
        final.append(''.join(c[-4:]))
        c = c[:-4]
    else:
        return ''.join(c)
    for i in range(0, len(c) // 3 + 1, 1):
        if len(c) > 2:
            final.insert(0, ''.join(c[-3:]))
            c = c[:-3]
        elif len(c) > 0:
            final.insert(0, ''.join(c))
    return(' '.join(final))

e.g.

>>> my_formatter(123456789.12)
>>> '12 345 6789'
>>> my_formatter(12345678912.34)
>>> '1 234 567 8912'

我希望您能指导如何以更简单/更高效的方式完成此操作。


1
你想要输出的字符串格式是什么? - Keyur Potdar
2
你是想匹配印度风格的货币/大数字分组吗?如果是这样,最近在python-ideas或python-dev上有一次讨论,要么添加能够处理这种情况的更一般的指定数字分组的方法,要么将其添加到Python 3.7或3.8中。我不知道提案的状态,显然你不能只是等待3.8并希望该功能存在,但是...我敢打赌,讨论中有多个人说“这是不必要的,因为任何白痴都可以编写这个微不足道的代码”(然后是恰好符合您要求的代码)。 - abarnert
@abarnert,如果方便的话,能否分享链接?如果需要搜索就不用麻烦了。 - Keyur Potdar
1
@KeyurPotdar 它将在 python-ideas archive 中。如果您更喜欢通过 NNTP 或其中一个更好的第三方存档进行搜索,我认为您可以在社区部分的某个地方找到有关它们的信息,也许是这里。讨论始于大约12月和1月左右,主题是...我想是印地语中的10000或类似的东西,但我不确定记得清楚。 - abarnert
1
@KeyurPotdar 抱歉;不管怎样,任何能够生成 2-2-2-…-3 的代码都可以很快地调整为生成2-2-2-…-4。此外,我已经找到了最初的帖子,你可以从那里点击下一个线程。 - abarnert
显示剩余7条评论
5个回答

3
采用了略微不同的方法,但是这个方法使用了第三方函数partition_all。简而言之,我使用它将字符串分组成3个字符一组,如果字符数少于3个,则将其作为最后一组。你可能会更喜欢这种方法,因为它没有使用for循环或条件语句,不过基本上只是视觉上的差异。
from toolz.itertoolz import partition_all
def simpleformat(x):
    x = str(round(x))
    a, b = x[:-4], x[-4:]
    strings = [''.join(x[::-1]) for x in reversed(list(partition_all(3, a[::-1])))]
    return ' '.join(strings + [b])

感谢分享! - fzzylogic

3

试试这个:

def my_formatter(x):
    # round it as text
    txt = "{:.0f}".format(x)

    # find split indices
    splits = [None] + list(range(-4, -len(txt), -3)) + [None]

    # slice and rejoin
    return " ".join(
        reversed([txt[i:j] for i, j in zip(splits[1:], splits[:-1])]))

那么

>>> my_formatter(123456789.1)
12 345 6789
>>> my_formatter(1123456789.1)
112 345 6789
>>> my_formatter(11123456789.1)
1 112 345 6789

很好,使用反向范围和一个反转而不是两个反转。 - abarnert

2

这里有一个相当简单的解决方案,使用反向循环元素,以便更容易计算索引:

num = 12345678912.34

temp = []
for ix, c in enumerate(reversed(str(round(num)))):
    if ix%3 == 0 and ix !=0: temp.extend([c, ' '])
    else: temp.extend(c)

''.join(list(reversed(temp)))

输出:

'1 234 567 8912'

使用列表推导式,我们可以在一行中完成此操作,但可能会很难理解,如下:

num = 12345678912.34

''.join(list(reversed(list(''.join([c+' ' if(ix%3 == 0 and ix!=0) else c for ix, c in enumerate(reversed(str(round(num))))])))))

'1 234 567 8912' 可以表示为 "12 3456 7891",这是一种常见的数字格式。

我会用列表推导式来简化它。 - JahKnows
1
如果ix == 3且ix%3 == 0,则第一部分为真,则第二部分也自动为真。 - abarnert
@JahKnows 不错的方法,谢谢分享!注意,在处理 1234567890 时,它附加了一个空格在前面,但在我目前的状态下,我有一个很好的主意,就是直接 .strip() 返回值。 - fzzylogic
@abarnert,我尝试了您提出的关于x==3x%3的建议,但这是必要的,因为我们只想对第一组进行前4个字符的分组。请注意,ix = 3是第四个字符。 - JahKnows
@JahKnows,你需要在另一行使用ix%3,但不需要ix == 3行。但无论如何,这都无关紧要;新版本没有那段代码,而且更好。太遗憾了,我不能再次点赞它。 :) 不过我仍然认为最好将temp制作成一个字符串列表,在最后拼接起来。 - abarnert
显示剩余2条评论

2
另一种方法是使用系统上可用的语言环境,然后使用格式化。
import locale

for v in ('fr_FR.UTF-8', 'en_GB.UTF-8'):
    locale.setlocale(locale.LC_NUMERIC, v)
    print(v, '>> {:n}'.format(111222333999))

1

我不妨分享另一种略有不同的变体,但仍然感觉有某种崇高的方式我们无法看到。目前还没有将任何答案标记为正确,因为我相信Python可以以某种更简单的方式完成这个任务。让我疯狂的是,如果我没记错的话,VB的格式命令可以处理这个问题(使用类似“### ####0”的模式)。也许只是因为我不理解如何正确使用Python的.format。

以下内容接受一个浮点数或十进制数和一个指示分割位置的列表。如果在消耗了最后一个分割位置后仍然存在数字,则重新应用该位置,直到达到字符串的开头。

def format_number(num, pat, sep=' '):
    fmt = []
    strn = "{:.0f}".format(num)
    while strn:
        p = pat.pop() if pat else p
        fmt.append(strn[-p:])
        strn = strn[:-p] if len(strn) > p else ''
    return sep.join(fmt[::-1])

>>> format_number(123456789, [3, 4])
>>> '12 345 6789'
>>> format_number(1234567890, [3])
>>> '1 234 567 890'

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接