cloud68
cloud68

Reputation: 23

String manipulation weirdness when incrementing trailing digit

I got this code:

myString = 'blabla123_01_version6688_01_01Long_stringWithNumbers'
versionSplit = re.findall(r'-?\d+|[a-zA-Z!@#$%^&*()_+.,<>{}]+|\W+?', myString)

for i in reversed(versionSplit):
    id = versionSplit.index(i)
    if i.isdigit():
        digit = '%0'+str(len(i))+'d'
        i = int(i) + 1
        i = digit % i
        versionSplit[id]=str(i)
        break

final = ''
myString = final.join(versionSplit)
print myString

Which suppose to increase ONLY the last digit from the string given. But if you run that code you will see that if there is the same digit in the string as the last one it will increase it one after the other if you keep running the script. Can anyone help me find out why?

Thank you in advance for any help

Upvotes: 2

Views: 262

Answers (4)

Preet Kukreti
Preet Kukreti

Reputation: 8607

Is there a reason why you aren't doing something like this instead:

prefix, version = re.match(r"(.*[^\d]+)([\d]+)$", myString).groups()
newstring = prefix + str(int(version)+1).rjust(len(version), '0')

Notes:

  • This will actually "carry over" the version numbers properly: ("09" -> "10") and ("99" -> "100")
  • This regex assumes at least one non-numeric character before the final version substring at the end. If this is not matched, it will throw an AttributeError. You could restructure it to throw a more suitable or specific exception (e.g. if re.match(...) returns None; see comments below for more info).

Adjust accordingly.

Upvotes: 8

heltonbiker
heltonbiker

Reputation: 27575

First, three notes:

  1. id is a reserved python word;
  2. For joining, a more pythonic idiom is ''.join(), using a literal empty string
  3. reversed() returns an iterator, not a list. That's why I use list(reversed()), in order to do rev.index(i) later.

Corrected code:

import re

myString = 'blabla123_01_version6688_01_01veryLong_stringWithNumbers01'
print myString
versionSplit = re.findall(r'-?\d+|[a-zA-Z!@#$%^&*()_+.,<>{}]+|\W+?', myString)

rev = list(reversed(versionSplit))  # create a reversed list to work with from now on

for i in rev:
    idd = rev.index(i)
    if i.isdigit():
        digit = '%0'+str(len(i))+'d'
        i = int(i) + 1
        i = digit % i
        rev[idd]=str(i)
        break

myString = ''.join(reversed(rev))  # reverse again only just before joining
print myString

Upvotes: 1

steveha
steveha

Reputation: 76695

myString = 'blabla123_01_version6688_01_01veryLong_stringWithNumbers01'
versionSplit = re.findall(r'-?\d+|[^\-\d]+', myString)

for i in xrange(len(versionSplit) - 1, -1, -1):
    s = versionSplit[i]
    if s.isdigit():
        n = int(s) + 1
        versionSplit[i] = "%0*d" % (len(s), n)
        break

myString = ''.join(versionSplit)
print myString

Notes:

  • It is silly to use the .index() method to try to find the string. Just use a decrementing index to try each part of versionSplit. This was where your problem was, as commented above by @David Robinson.

  • Don't use id as a variable name; you are covering up the built-in function id().

  • This code is using the * in a format template, which will accept an integer and set the width.

  • I simplified the pattern: either you are matching a digit (with optional leading minus sign) or else you are matching non-digits.

  • I tested this and it seems to work.

Upvotes: 1

bonsaiviking
bonsaiviking

Reputation: 5995

The issue is the use of the list.index() function on line 5. This returns the index of the first occurrence of a value in a list, from left to right, but the code is iterating over the reversed list (right to left). There are lots of ways to straighten this out, but here's one that makes the fewest changes to your existing code: Iterate over indices in reverse (avoids reversing the list).

for idx in range(len(versionSplit)-1, -1, -1):
    i = versionSplit[idx]
    if chunk.isdigit():
        digit = '%0'+str(len(i))+'d'
        i = int(i) + 1
        i = digit % i
        versionSplit[idx]=str(i)
        break

Upvotes: 2

Related Questions