How do I write responsive regular expression in Python?

148 Views Asked by At

I want to get up to the first digit other than 0 after the dot. If there is a number other than 0 after the dot, it should be taken as 2 digits. So it must be sensitive.How do I do this with regular expressions or Python math library? Here are examples

"0.00820289933" => "0.008"
"0.00000025252" => "0.0000002"
"0.858282815215" => "0.85"
"0.075787842545" => "0.07"
"1.100000010999" => "1.10"
"0.0"            => "0.0"
"0.000000000000" => "0.0" 
"0.000365266565" => "0.0003" 
"0.000036526656" => "0.00003" 
"0.000003652665" => "0.000003" 
"0.000000365266" => "0.0000003" 

I tried this

def format_adjusted_value(value):
    str_value = str(value)

    if '.' in str_value:
        match = re.match(r'(\d*\.\d*?[1-9])0*$', str_value)

        if match:
            formatted_value = match.group(1)
        else:
            match = re.match(r'(\d*\.\d*?[0-9])0*$', str_value)
            formatted_value = match.group(1) if match else str_value
    else:
        formatted_value = str_value

    return formatted_value
4

There are 4 best solutions below

18
mozway On BEST ANSWER

What about using a simple \d*\.(?:[^0]|0+)[^0]:

import re

def format_adjusted_value(value):
    m = re.search(r'\d*\.(?:[^0]|0+)[^0]', str(value))
    if m:
        return m.group(0)
    return value

NB. [^0] could be [1-9] if you have characters other than digits.

Output:

0.00820289933: 0.008
0.00000025252: 0.0000002
0.858282815215: 0.85
0.075787842545: 0.07

Regex demo

\d*\.       # match digits (optional) and a .
(?:[^0]|0+) # match a non zero or one or more zeros
[^0]        # match a non-zero

Alternative regex: \d*\.(?:0+[1-9]|\d{,2})

0.00820289933: 0.008
0.00000025252: 0.0000002
0.858282815215: 0.85
0.075787842545: 0.07
0.: 0.
0.0: 0.0
0.000000: 0.00

Regex demo

0
Tim Roberts On

Like most regex issues, just say it in words. Match "zero dot, followed by EITHER a bunch of 0s and a digit, OR 1-9 and a digit:

import re
data = [
"0.00820289933",
"0.00000025252",
"0.858282815215",
"0.075787842545"
]

digs = re.compile(r"0\.((0+\d)|([1-9]\d))")

for p in data:
    print(digs.match(p))

Output:

<re.Match object; span=(0, 5), match='0.008'>
<re.Match object; span=(0, 9), match='0.0000002'>
<re.Match object; span=(0, 4), match='0.85'>
<re.Match object; span=(0, 4), match='0.07'>
1
Mahboob Nur On

Try like this

import re

def format_adjusted_value(value):
    str_value = str(value)

    match = re.match(r'\d*\.\d*?[1-9]', str_value)
    formatted_value = match.group(0) if match else str_value

    return formatted_value

test_values = [
    "0.00820289933",
    "0.00000025252",
    "0.858282815215",
    "0.075787842545",
]

for value in test_values:
    print(f"{value} => {format_adjusted_value(value)}")

Output:

0.00820289933 => 0.008
0.00000025252 => 0.0000002
0.858282815215 => 0.8
0.075787842545 => 0.07
1
blhsing On

To format a decimal number with custom logics it's better to convert the input to a decimal.Decimal object for proper exponent-based adjustments. Using a regex for numeric operations is almost always going to end up with unresolved corner cases.

from decimal import Decimal, Context, ROUND_DOWN

def format_adjusted_value(value):
    d = Decimal(value)
    exp = d.adjusted()
    return f'{d.normalize(Context(rounding=ROUND_DOWN, prec=1 + (exp > -2))):.{max(1 + (d.as_tuple().exponent < 0), -exp)}f}'

so that:

print(format_adjusted_value(0.00820289933))
print(format_adjusted_value(0.00000025252))
print(format_adjusted_value(0.858282815215))
print(format_adjusted_value(0.075787842545))
print(format_adjusted_value(1.100000010999))
print(format_adjusted_value(0.0))
print(format_adjusted_value(0.000000000000))
print(format_adjusted_value(0.000365266565))
print(format_adjusted_value(0.000036526656))
print(format_adjusted_value(0.000003652665))
print(format_adjusted_value(0.000000365266))

outputs:

0.008
0.0000002
0.85
0.07
1.10
0.0
0.0
0.0003
0.00003
0.000003
0.0000003

Demo: https://ideone.com/GQYNAc