4

I have a regex issue which do not seems as common as I thought : I would like to extract all numeric values having px units, apply some calculation, and then re-inject the new value within my string. I don't want to include the px string (see exemple below), but I could use an alternative method which keep them, or change the unit type.

Exemple, multiplying values by 2.5 :

from "2px aperture 12px science 2.1px yummy cake"

I want "5 aperture 30 science 5.25 yummy cake"

I made a sketchy script, but I don't get quite the desired output :

import re
my_string = "2px aperture 12px science 2.1px yummy cake"
nb_list= re.findall(r"([0-9.]+)px", my_string)
splitted_string = re.findall('.*?px', my_string)
print(f"splitted_string = {splitted_string}")
print(f"nb_list = {nb_list}")
new_list = []
for i in range(0, len(nb_list)):
  new_n = str(float(nb_list[i])*2.5)
  new_string = re.sub(r"[0-9.]+px", new_n, splitted_string[i])
  new_list.append(new_string)
new_list = ''.join(new_list)
print(f"new_list = {new_list}")

Result :

new_list = 5.0 aperture 30.0 science 5.25

I understand why I get this result, but I don't know what to change to get the desired output.

2
  • Try to use new_n = re.sub(r"(?<=\d)(.0)(?=[^\d])",'',new_n) after new_n = str(float(nb_list[i])*2.5)
    – Llex
    Commented Jul 9, 2019 at 14:53
  • 2
    Try re.sub(r"(\d+(?:\.\d+)?)px", lambda x: str(float(x.group(1))*2.5), my_string). Maybe r"(\d+(?:\.\d+)?)px\b" will be more precise though as px will be matched as a whole word. See ideone.com/68NH7f Commented Jul 9, 2019 at 14:54

1 Answer 1

9

Just use re.sub with a callback:

r = re.sub(
    r'(\d+(\.\d+)?)px\b',
    lambda m: '{:g}'.format(float(m.group(1)) * 2.5),
    s)

It's easy to extend this to multiple units, for example:

units = {
    'px': 2.5,
    'em': 4,
}

r = re.sub(
    fr'(\d+(\.\d+)?)({"|".join(units)})\b',
    lambda m: '{:g}'.format(float(m.group(1)) * units[m.group(3)]),
    s)
2
  • TIL about {:g}. I always did .rstrip('0').rstrip('.') but this is shorter. Upvoted. Commented Jul 9, 2019 at 15:00
  • That is exactly what I was trying to do ! I did not think about lambda within re.sub, and did not know about f+r strings. I will still take some time to fully understand this, but this seems neat and concise, thanks a lot !
    – Arkeen
    Commented Jul 9, 2019 at 15:08

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.