This method first extracts the prefix portion of
self. Then it adds one, propagating the carry
until it reaches the end of the odometer portion, or
until it hits a non-digit character, whichever comes
first. The class attribute
TRAILING_DIGITS is a compiled regular expression that matches trailing
digits at the end of a string, but it must match at
# - - - B a n d N o F i e l d . i n c r e m e n t P r e f i x - - - TRAILING_DIGITS = re.compile ( r'\d+' # Matches one or more digits r'$') # End-of-string anchor def incrementPrefix(self): '''Return self's prefix, incremented with rollover. '''
First we extract the part of the prefix that will not be affected by rollover, and the part that may be affected.
#-- 1 -- # [ fixed := self's fixed part # odoPrefix := odometer portion of self's prefix ] fixed = self.value [ : L_BAND_NO_FIXED ] odoPrefix = self.value [ L_BAND_NO_FIXED : L_BAND_NO_PREFIX ]
Next we separate
odoPrefix into a
“good part,” containing all trailing
digits, and a “junk part,” consisting of
all the characters to the left of the good part. If
the good part is empty, there's nothing we can do but
return the whole prefix as it stands. Note that we use
.search() method on the regular
expression, so it will find matches anywhere, not the
.match() method that matches only at the
start of the string.
#-- 2 -- # [ if odoPrefix has any trailing digits -> # goodPart := those digits # junkPart := characters from odoPrefix before # those digits # else -> # return self's prefix ] m = self.TRAILING_DIGITS.search(odoPrefix) if m is None: return self.prefix() else: goodPart = m.group() junkPart = odoPrefix[:-len(goodPart)]
At this point,
goodPart contains only
digits, so we can convert it to integer, add one, and
convert it back to a string (with left zeroes). There are
several subtle Python features here. Formatting with a
"%*d" format takes two values, a field size and a
field value, so for example
"%*d" % (3, 13)
would produce the value
" 13". Adding
0 just after the
left zero fill, so for example
"%0*d" % (5, 13)
would produce the value
#-- 3 -- # [ goodPart is a string containing only digits -> # goodLen := len(goodPart) # goodPlus := int(goodPart)+1, converted to an integer # with left zero fill to size L_BAND_NO_ODO_PREFIX ] goodLen = len(goodPart) goodPlus = ("%0*d" % (goodLen, int(goodPart) + 1))
There is one annoying pathological case. Suppose that the
odoPrefix part is
"999". If we
add one to that, we get
"1000". In that case,
we really should discard the carry.
#-- 4 -- if len(goodPlus) > goodLen: goodPlus = goodPlus[1:]
All that remains is to reassemble the three different parts of the prefix: the non-odometer part, any “junk” part, and the incremented odometer part.
#-- 5 -- return "%s%s%s" % (fixed, junkPart, goodPlus)
None of this would have been necessary if the Bird Banding Lab had stipulated that a string of bands is number 00–99 instead of 01–100. Obviously there were no programmers involved in this decision!