Python正则表达式全局用空格替换尾随零

时间:2015-09-02 08:41:00

标签: python regex string-formatting text-alignment

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

  1. 十进制数后没有尾随零
  2. 如果小数点分隔符后面的第一个数字为零,请保留
  3. 由于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. 这是关于多线字符串中的许多事件的全局替换,而不仅仅是单个字符串

5 个答案:

答案 0 :(得分:4)

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

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

答案 1 :(得分:3)

您需要更改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 (?<=\.)([0-9]+?)(0+)(?=\D|$) regex matches

正则表达式匹配:

  • (?<=\.)([0-9]+?) - 一个或多个数字,但尽可能少如果前面有文字.(小数点分隔符)
  • (0+) - 1个或多个零......
  • (?=\D|$) - 最多为非数字\D或字符串$的结尾。

答案 2 :(得分:1)

这是另一种方法:

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   |

答案 3 :(得分:0)

你能尝试使用它,看看它是否有效? ([0-9]+(\.[0-9]+[1-9])?)(\.?0+$)

答案 4 :(得分:0)

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

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