I have a string like this:
"foo 15 bar -2hello 4 asdf+2"
I'd like to get:
"foo 14 bar -3hello 3 asdf+1"
I would like to replace every number (sequence of digits as signed base-10 integers) with the result of a subtraction executed on each of them, one for each number.
I've written a ~50 LOC function that iterates on characters, separating signs, digits and other text, applying the function and recombining the parts. Although it has one issue my intent with the question is not to review it. Instead I'm trying to ask, what is the pythonic way to solve this, is there an easier way?
For reference, here is my function with the known issue, but my intention is not asking for a review but finding the most pythonic way instead.
edit to answer the wise comment of Janne Karila:
+2
should become +1
+1
should become 0
asdf - 4
becomes asdf - 3
-+-2
becomes -+-3
edit on popular demand here is my buggy code :)
DISCLAIMER: Please note I'm not interested in fixing this code. I'm asking if there is a better approach than something like mine.
def apply_to_digits(some_str,handler):
sign = "+"
started = 0
number = []
tmp = []
result = []
for idx,char in enumerate(some_str):
if started:
if not char.isdigit():
if number:
ss = sign + "".join(number)
rewritten = str(handler(int(ss)))
result.append(rewritten)
elif tmp:
result.append("".join(tmp))
number = []
tmp = []
sign = "+"
started = 0
# char will be dealt later
else:
number.append(char)
continue
if char in "-+":
sign = char
started = 1
if tmp:
result.append("".join(tmp))
tmp = []
tmp.append(char)
continue
elif char.isdigit():
started = 1
if tmp:
result.append("".join(tmp))
tmp = []
number.append(char)
else:
tmp.append(char)
if number:
ss = sign + "".join(number)
rewritten = str(handler(int(ss)))
result.append(rewritten)
if tmp:
result.append("".join(tmp)), tmp
return "".join(result)
#
DISCLAIMER: Please note I'm not interested in fixing this code. I'm asking if there is a better approach than something like mine.
You could try using regex, and using re.sub
:
>>> pattern = "(-?\d+)|(\+1)"
>>> def sub_one(match):
return str(int(match.group(0)) - 1)
>>> text = "foo 15 bar -2hello 4 asdf+2"
>>> re.sub(pattern, sub_one, text)
'foo 14 bar -3hello 3 asdf+1'
The regex (-?\d+)|(\+1)
will either capture an optional -
sign and one or more digits, OR the literal sequence +1
. That way, the regex will make sure that all of your requirements when converting digits work properly.
The regex (-?\d+)
by itself does the right thing most of the time, but the (\+1)
exists to make sure that the string +1
always converts to zero, without a sign. If you change your mind, and want +1
to convert to +0
, then you can just use only the first part of the regex: (-?d+)
.
You could probably compress this all into a one-liner if you wanted:
def replace_digits(text):
return re.sub("(-?\d+)|(\+1)", lambda m: str(int(m.group(0)) - 1), text)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With