2015-09-02 4 views
1

В качестве обходного пути для выравнивания поплавков к десятичному разделителю для табличных числовых данных я попытался найти регулярное выражение для замены (глобально апостериорных) конечных нулей с пробелами со следующими правилами:Python regex глобально заменить конечные нули пробелами

  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. Речь идет о глобальной замене многих случаев в многострочной строке , А не только один occurrency
+0

что ожидаемый выход? – vks

+0

@vks Это в вопросе. – mmj

+0

@mmj какое правило превращается 12 в 12.0, но сохраняет 12345, как есть? – yurib

ответ

3

(предыдущий, но неудовлетворительно) ответ stribizhev дал мне идею, чтобы получить к общему решению:

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

Вы должны изменить 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]+?) - 1 или более цифр, но как можно если предшествовавшей с буквальным . (десятичного разделителя)
  • (0+) - 1 или более нулей ...
  • (?=\D|$) - до незначной цифры \D или конец строки $.
+1

Хорошо, но идея правильная. Вы можете - и я вижу, что вы сделали - используйте lookaround для проверки конца строки (если вы используете флаг 're.M'). Я обновил свой вариант замены последнего регулярного выражения. –

+0

Доброта, я смутился с первоначальными требованиями. Обновлено. –

+0

Я проверил ссылки, никаких сюрпризов, я думаю. –

0

Можете ли вы попробовать использовать это и посмотреть, будет ли это работать? ([0-9]+(\.[0-9]+[1-9])?)(\.?0+$)

+0

Если вы укажете заменяемую строку, я буду рад попробовать ее. – mmj

-1

Я предложил бы использовать формат строки вместо регулярных выражений:

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 
+0

почему нисходящий? – yurib

+0

Выглядит неплохо, его нельзя отбрасывать. На мой взгляд, это связано с несколькими небольшими проблемами: во-первых, определения форматирования слишком много смешаны с кодом (это может быть улучшено, я считаю), во-вторых, не в последнюю очередь, если я хочу, чтобы столбец был отформатирован с десятичной точкой, я должен принять заботиться о том, чтобы передавать поплавки, поскольку, если я передаю целое число, он будет отформатирован без разделителя десятичных чисел. – mmj

+0

@mmj проблема с int/float - это именно то, о чем попросил OP. и, конечно, его можно реорганизовать, чтобы быть более гибким, это всего лишь POC. – yurib

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'\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 | 
+0

Спасибо, результат подобен ожидаемому, но он не соответствует требованиям глобальной замены, поэтому решение не будет приемлемо для принятия. – mmj

Смежные вопросы