【问题标题】:Python regex to globally replace trailing zeros with spacesPython正则表达式用空格全局替换尾随零
【发布时间】:2015-11-27 16:23:46
【问题描述】:

作为将浮点数与表格数字数据的小数分隔符对齐的解决方法,我尝试找到一个正则表达式来用空格替换(全局后验)尾随零,规则如下:

  1. 小数位后无尾随零
  2. 如果小数点后的第一位为零,则保留它

由于 Python 正则表达式引擎对后视需要固定宽度模式的限制,我无法找到令人满意的解决方案。这是我尝试的一个工作示例(Python 3.x); 不要在您的解决方案中依赖竖线,它们在示例中只是为了清晰起见:

import re
# formatmany is just a way to speed up building of multiline string of tabular data
formatmany=lambda f:lambda *s:'\n'.join(f.format(*x) for x in s)

my_list = [[12345, 12.345, 12.345, 12.345],
           [12340, 12.34 , 12.34 , 12.34 ],
           [12345, 12.005, 12.005, 12.005],
           [12340, 12.04 , 12.04 , 12.04 ],
           [12300, 12.3  , 12.3  , 12.3  ],
           [12000, 12.0  , 12.0  , 12    ]]
my_format = formatmany('|{:8d}|{:8.2f}|{:8.3f}|{:8.4f}|')
my_string = my_format(*my_list) # this is the formatted multiline string with trailing zeros

print('\nOriginal string:\n')
print(my_string)
print('\nTry 1:\n')
print(re.sub(r'(?<!\.)0+(?=[^0-9\.]|$)',lambda m:' '*len(m.group()),my_string))
print('\nTry 2:\n')
print(re.sub(r'(\d)0+(?=[^\d]|$)',r'\1',my_string))

打印出来的

Original string:

|   12345|   12.35|  12.345| 12.3450|
|   12340|   12.34|  12.340| 12.3400|
|   12345|   12.01|  12.005| 12.0050|
|   12340|   12.04|  12.040| 12.0400|
|   12300|   12.30|  12.300| 12.3000|
|   12000|   12.00|  12.000| 12.0000|

Try 1:

|   12345|   12.35|  12.345| 12.345 |
|   1234 |   12.34|  12.34 | 12.34  |
|   12345|   12.01|  12.005| 12.005 |
|   1234 |   12.04|  12.04 | 12.04  |
|   123  |   12.3 |  12.3  | 12.3   |
|   12   |   12.0 |  12.0  | 12.0   |

Try 2:

|   12345|   12.35|  12.345| 12.345|
|   1234|   12.34|  12.34| 12.34|
|   12345|   12.01|  12.005| 12.005|
|   1234|   12.04|  12.04| 12.04|
|   123|   12.3|  12.3| 12.3|
|   12|   12.0|  12.0| 12.0|

尝试 1 也替换整数中的尾随零,尝试 2 取自另一个解决方案,用于替换单个浮点中的尾随零。两者都不令人满意,因为所需的输出应该是:

|   12345|   12.35|  12.345| 12.345 |
|   12340|   12.34|  12.34 | 12.34  |
|   12345|   12.01|  12.005| 12.005 |
|   12340|   12.04|  12.04 | 12.04  |
|   12300|   12.3 |  12.3  | 12.3   |
|   12000|   12.0 |  12.0  | 12.0   |

为什么这不是重复的问题

  1. Python 正则表达式引擎与其他语言引擎略有不同,因此为其他语言提供的解决方案不会自动应用
  2. 尾随零将被替换,而不是被删除
  3. 这是关于全局替换多行字符串中的许多事件,而不仅仅是单个事件

【问题讨论】:

  • 预期输出是什么?
  • @vks 有问题。
  • @mmj 什么规则将 12 变成 12.0 但保持 12345 不变?
  • @yurib 规则由格式字符串指定:'|{:8d}|{:8.2f}|{:8.3f}|{:8.4f}|'

标签: python regex string-formatting text-alignment


【解决方案1】:

stribizhev(以前但不满意)的回答让我想到了一个通用的解决方案:

re.sub(r'(?<=\.)(\d+?)(0+)(?=[^\d]|$)',lambda m:m.group(1)+' '*len(m.group(2))

【讨论】:

    【解决方案2】:

    您需要更改sub,如下所示:

    print(re.sub(r'(?<=\.)([0-9]+?)(0+)(?=\D|$)',lambda m:m.group(1)+' '*len(m.group(2)), my_string))
    

    IDEONE demo

    这里是a demo of what (?&lt;=\.)([0-9]+?)(0+)(?=\D|$) regex matches

    正则表达式匹配:

    • (?&lt;=\.)([0-9]+?) - 1 个或多个数字,但尽可能少 if 前面有文字 .(小数分隔符)
    • (0+) - 1 个或多个零 ...
    • (?=\D|$) - 最多为非数字 \D 或字符串结尾 $

    【讨论】:

    • 经过一些尝试,您的解决方案按预期工作。感谢您的努力并感谢您的贡献,尽管我不禁注意到它与我已经给出的解决方案相同。
    【解决方案3】:

    这是另一种方法:

    my_list = [[12345, 12.345, 12.345, 12.345],
               [12340, 12.340, 12.340, 12.340],
               [12300, 12.300, 12.300, 12.300],
               [12000, 12.000, 12.000, 12.000]]
    
    format_list = ["{:8d}", "{:8.2f}", "{:8.3f}", "{:8.4f}"]
    
    for row in my_list:
        line = ["{:<8}".format(re.sub(r'(\.\d+?)0+', r'\1', y.format(x))) for x,y in zip(row, format_list)]
        print("|{}|".format("|".join(line)))
    

    给出输出:

    |   12345|   12.35|  12.345| 12.345 |
    |   12340|   12.34|  12.34 | 12.34  |
    |   12300|   12.3 |  12.3  | 12.3   |
    |   12000|   12.0 |  12.0  | 12.0   |
    

    【讨论】:

    • 谢谢,结果符合预期,但不符合全球更换要求,因此该方案不符合验收条件。
    【解决方案4】:

    您可以尝试使用它并查看它是否有效吗? ([0-9]+(\.[0-9]+[1-9])?)(\.?0+$)

    【讨论】:

    • 如果您指定替换字符串,我很乐意尝试。
    【解决方案5】:

    我建议使用字符串格式而不是正则表达式:

    int_fmt = '{:>8d}'
    general_fmt = '{:>8.5g}'
    float_fmt = '{:>8.1f}'
    for l in my_list:
        print '|'.join([int_fmt.format(l[0])] + [(float_fmt if int(x)==x else general_fmt).format(x) for x in l[1:]])
    

    输出:

       12345|  12.345|  12.345|  12.345
       12340|   12.34|   12.34|   12.34
       12300|    12.3|    12.3|    12.3
       12000|    12.0|    12.0|    12.0
    

    【讨论】:

    • 看起来不错,不应该被否决。在我看来,它只是存在一些小问题:首先,格式定义与代码混合太多(我相信这可以改进),其次但并非最不重要的一点是,如果我想用小数点格式化列,我必须采取注意传递浮点数,因为如果我传递一个整数,它将被格式化而没有小数分隔符。
    • @mmj int/float 问题正是 OP 所要求的。当然也可以重构为更加灵活,这只是一个POC
    • 问题已更新以反映最终格式不应依赖于整数或浮点值(请参阅最后一个值的行为)。尽管如此,如果您不需要该功能,我认为这是一个有趣的解决方案。
    • @mmj 那么为什么 12000 仍然是 12000 而 12 变成 12.0 呢?
    • 因为是在格式字符串中指定的:'|{:8d}|{:8.2f}|{:8.3f}|{:8.4f}|'.
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-24
    • 1970-01-01
    • 1970-01-01
    • 2019-03-10
    • 1970-01-01
    相关资源
    最近更新 更多