Python 正则表达式全局用空格替换尾随零
Python regex to globally replace trailing zeros with spaces
作为将浮点数与表格数字数据的小数点分隔符对齐的解决方法,我试图找到一个正则表达式来用空格替换(全局后验)尾随零,规则如下:
- 小数位后无尾随零
- 如果小数点后的第一个数字为零,则保留它
还由于 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'',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 |
为什么这不是重复的问题
- Python正则表达式引擎与其他语言引擎略有不同,因此为其他语言提供的解决方案不会自动应用
- 尾随零将被替换,而不是被删除
- 这是关于多行字符串中多次出现的全局替换,而不仅仅是单个出现
您需要将 sub
更改如下:
print(re.sub(r'(?<=\.)([0-9]+?)(0+)(?=\D|$)',lambda m:m.group(1)+' '*len(m.group(2)), my_string))
这里是a demo of what (?<=\.)([0-9]+?)(0+)(?=\D|$)
regex matches.
正则表达式匹配:
(?<=\.)([0-9]+?)
- 1 位或更多位数字,但尽可能少 if 前面有文字 .
(小数点分隔符)
(0+)
- 1 个或多个零 ...
(?=\D|$)
- 直到非数字 \D
或字符串结尾 $
.
你可以尝试使用它看看它是否有效?
([0-9]+(\.[0-9]+[1-9])?)(\.?0+$)
stribizhev(之前的但不令人满意)的回答给了我一个通用解决方案的想法:
re.sub(r'(?<=\.)(\d+?)(0+)(?=[^\d]|$)',lambda m:m.group(1)+' '*len(m.group(2))
我建议使用字符串格式而不是正则表达式:
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
这是另一种方法:
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'', 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 |
作为将浮点数与表格数字数据的小数点分隔符对齐的解决方法,我试图找到一个正则表达式来用空格替换(全局后验)尾随零,规则如下:
- 小数位后无尾随零
- 如果小数点后的第一个数字为零,则保留它
还由于 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'',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 |
为什么这不是重复的问题
- Python正则表达式引擎与其他语言引擎略有不同,因此为其他语言提供的解决方案不会自动应用
- 尾随零将被替换,而不是被删除
- 这是关于多行字符串中多次出现的全局替换,而不仅仅是单个出现
您需要将 sub
更改如下:
print(re.sub(r'(?<=\.)([0-9]+?)(0+)(?=\D|$)',lambda m:m.group(1)+' '*len(m.group(2)), my_string))
这里是a demo of what (?<=\.)([0-9]+?)(0+)(?=\D|$)
regex matches.
正则表达式匹配:
(?<=\.)([0-9]+?)
- 1 位或更多位数字,但尽可能少 if 前面有文字.
(小数点分隔符)(0+)
- 1 个或多个零 ...(?=\D|$)
- 直到非数字\D
或字符串结尾$
.
你可以尝试使用它看看它是否有效?
([0-9]+(\.[0-9]+[1-9])?)(\.?0+$)
stribizhev(之前的但不令人满意)的回答给了我一个通用解决方案的想法:
re.sub(r'(?<=\.)(\d+?)(0+)(?=[^\d]|$)',lambda m:m.group(1)+' '*len(m.group(2))
我建议使用字符串格式而不是正则表达式:
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
这是另一种方法:
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'', 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 |