Interpreting Number Ranges in Python

我们两清 提交于 2019-12-23 14:59:24

问题


In a Pylons webapp, I need to take a string such as "<3, 45, 46, 48-51, 77" and create a list of ints (which are actually IDs of objects) to search on.

Any suggestions on ways to do this? I'm new to Python, and I haven't found anything out there that helps with this kind of thing.

The list would be: [1, 2, 3, 45, 46, 48, 49, 50, 51, 77]


回答1:


Use parseIntSet from here

I also like the pyparsing implementation in the comments at the end.

The parseIntSet has been modified here to handle "<3"-type entries and to only spit out the invalid strings if there are any.

#! /usr/local/bin/python
import sys
import os

# return a set of selected values when a string in the form:
# 1-4,6
# would return:
# 1,2,3,4,6
# as expected...

def parseIntSet(nputstr=""):
    selection = set()
    invalid = set()
    # tokens are comma seperated values
    tokens = [x.strip() for x in nputstr.split(',')]
    for i in tokens:
        if len(i) > 0:
            if i[:1] == "<":
                i = "1-%s"%(i[1:])
        try:
            # typically tokens are plain old integers
            selection.add(int(i))
        except:
            # if not, then it might be a range
            try:
                token = [int(k.strip()) for k in i.split('-')]
                if len(token) > 1:
                    token.sort()
                    # we have items seperated by a dash
                    # try to build a valid range
                    first = token[0]
                    last = token[len(token)-1]
                    for x in range(first, last+1):
                        selection.add(x)
            except:
                # not an int and not a range...
                invalid.add(i)
    # Report invalid tokens before returning valid selection
    if len(invalid) > 0:
        print "Invalid set: " + str(invalid)
    return selection
# end parseIntSet

print 'Generate a list of selected items!'
nputstr = raw_input('Enter a list of items: ')

selection = parseIntSet(nputstr)
print 'Your selection is: '
print str(selection)

And here's the output from the sample run:

$ python qq.py
Generate a list of selected items!
Enter a list of items: <3, 45, 46, 48-51, 77
Your selection is:
set([1, 2, 3, 45, 46, 77, 48, 49, 50, 51])



回答2:


I've created a version of @vartec's solution which I feel is more readable:

def _parse_range(numbers: str):
    for x in numbers.split(','):
        x = x.strip()
        if x.isdigit():
            yield int(x)
        elif x[0] == '<':
            yield from range(0, int(x[1:]))
        elif '-' in x:
            xr = x.split('-')
            yield from range(int(xr[0].strip()), int(xr[1].strip())+1)
        else:
            raise ValueError(f"Unknown range specified: {x}")

In the process, the function became a generator :)




回答3:


rng = "<3, 45, 46, 48-51, 77"
ids = []
for x in map(str.strip,rng.split(',')):
    if x.isdigit():
        ids.append(int(x))
        continue
    if x[0] == '<':
        ids.extend(range(1,int(x[1:])+1))
        continue
    if '-' in x:
        xr = map(str.strip,x.split('-'))
        ids.extend(range(int(xr[0]),int(xr[1])+1))
        continue
    else:
        raise Exception, 'unknown range type: "%s"'%x



回答4:


First, you'll need to figure out what kind of syntax you'll accept. You current have three in your example:

  1. Single number: 45, 46

  2. Less than operator

  3. Dash ranging: 48-51

After that, it's just a matter of splitting the string into tokens, and checking the format of the token.




回答5:


>>> print range.__doc__
range([start,] stop[, step]) -> list of integers

Return a list containing an arithmetic progression of integers. range(i, j) returns [i, i+1, i+2, ..., j-1]; start (!) defaults to 0. When step is given, it specifies the increment (or decrement). For example, range(4) returns [0, 1, 2, 3]. The end point is omitted! These are exactly the valid indices for a list of 4 elements.

>>> range(33,44)
[33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43]
>>> range(1,3)
[1, 2]

I imagine you could iterate your list, and call range appropriately.

>>> def lessThan(n) :
...  return range(n+1)
...
>>> lessThan(4)
[0, 1, 2, 3, 4]
>>> def toFrom(n,m):
...  return range(n,m)
...
>>> toFrom(33,44)
[33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43]

Then split the string on commas, and for each bit, parse it enough to figure out what function to call, catenating the lists returned.

Anything more and I'd have written it for you.




回答6:


I also had to do something similar for an app lately.

If you don't need concrete numbers but just a way to see whether a given number is in the range, you might consider parsing it to a Python expression you can eval into a lambda. For example <3, 5-10, 12 could be func=(lambda x:x<3 or (5 <= x <= 10) or x==12)). Then you can just call the lambda, func(11) to see if 11 belongs in there.



来源:https://stackoverflow.com/questions/712460/interpreting-number-ranges-in-python

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!