spreading range of IPs to a list - python
I have the following code, what I am trying to do is create a small function so a when an IP range (contains : in it) is inputted all range is appended to the list.
collected_ips = []
while True:
query = input("IP:\t")
if not query:
break
elif len(query.split('.'))==4:
temp = query.split('.')
#result1 = all([i.isdigit() for i in t]) #must be True
if query.find(":")==-1:
try:
result2 = all([0<=int(i)<=255 for i in temp])
if result2:
collected_ips.append(query)
except ValueError:
print("Please Fix Input")
elif len(x.split(":"))==2:
#function here
#append to collected_ips
else:
print("Please Fix Input")
else:
print("Please Fix Input")
example of input:
123.123.30.20:50
output:
['123.123.30.20,'123.123.30.21'...'123.123.30.50']
example of input:
123.123.20:50.30
output:
['123.123.20.30','123.123.21.30',...'123.123.50.30']
This is one approach using range to generate numbers between two ranges:
def spread_ip_range(ip):
splits = ip.split('.')
indx = next((i for i, x in enumerate(splits) if ':' in x), -1)
lst = []
if indx != -1:
_from, to = splits[indx].split(':')
ranges = range(max(0, int(_from)), min(255, int(to)) + 1))
for r in ranges:
s = '.'.join(splits[:indx]) + '.' + str(r)
if splits[indx+1:]:
s += '.' + '.'.join(splits[indx+1:])
lst.append(s)
return lst
Usage:
>>> spread_ip_range('123.123.20:50.30')
['123.123.20.30', '123.123.21.30', '123.123.22.30', ......, '123.123.49.30', '123.123.50.30']
-
>>> spread_ip_range('123.123.30.20:50')
['123.123.30.20', '123.123.30.21', '123.123.30.22', ......, '123.123.30.49', '123.123.30.50']
You could also do this more concisely using reduce (from functools):
from functools import reduce
def expandIp(ip):
nodes = [list(map(int,n.split(":"))) for n in ip.split(".")]
ranges = [range(max(n[0],0),min(n[-1]+1,256)) for n in nodes]
ips = reduce(lambda a,rng: [ip+[n] for ip in a for n in rng],ranges,[[]])
return [".".join(str(n) for n in ip) for ip in ips]
nodes converts the ip pattern into a list of range values [start] or [start,end]
ranges converts the nodes into actual ranges using the start number as the end when there is not a ':' specifier for the node (also caps the node to range 0...255)
ips combines each node range with all values of preceding nodes
The result is the concatenation of nodes in each combination with a "." as separator
note: this solution will work for multiple range specifiers in the ip parameter. e.g. expandIp("10.1.1:2.100:101") will produce: 10.1.1.100, 10.1.1.101, 10.1.2.100, 10.1.2.101. So if you intend to use it for subnets, you will be able to do expandIp("10.0.1:3.0:255")
By the way, you could validate the ip parameter with a single condition at the beginning of the function or before calling it (then you wouldn't need to use min/max to assign the ranges variable):
n0255 = { str(n) for n in range(256) }
if not all( i<4 and j<2 and r in n0255 for i,n in enumerate(ip.split(".")) for j,r in enumerate(n.split(":"))):
print("Invalid Input")
The final function would look like this:
from functools import reduce
n0255 = { str(n) for n in range(256) }
def expandIp(ip):
if not all( i<4 and j<2 and r in n0255 for i,n in enumerate(ip.split(".")) for j,r in enumerate(n.split(":"))):
return None
nodes = [list(map(int,n.split(":"))) for n in ip.split(".")]
ranges = [range(n[0],n[-1]+1) for n in nodes]
ips = reduce(lambda a,rng: [ip+[n] for ip in a for n in rng],ranges,[[]])
return [".".join(str(n) for n in ip) for ip in ips]
which would simplify your calling code down to :
collected_ips = []
while True:
query = input("IP:\t")
if not query:
break
ips = expandIp(query)
if not ips:
print("Invalid Input")
else:
collected_ips += ips
Related
Alternative for re.findall for python
I am using an arduino uno and a thermistor to measure the current temperature. I have been using re.findall to find the matching string in line 4, is there an alternative instead of using re.findall that has the same function? as I am not allowed to use re in my project. Thanks def my_function(port): # Set COM Port..... ser = serial.Serial(port, 9600, timeout=0, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, rtscts=0) my_function('COM3') # Set path to my Arduino device # portPath = my_function baud = 9600 sample_time = 1 # Takes temperature every 1 second sim_time = 1 # Graphs 5 data points # Initializing Lists # Data Collection data_log = [] line_data = [] # Establishing Serial Connection connection = serial.Serial("com3", baud) # Calculating the length of data to collect based on the # sample time and simulation time (set by user) max_length = sim_time / sample_time # Collecting the data from the serial port while True: line = connection.readline() line_data = re.findall('\d*\.\d*', str(line)) line_data = filter(None, line_data) line_data = [float(x) for x in line_data] line_data = [(x - 32) * 0.5556 for x in line_data] # list comprehension to convert line_data temp to celsius line_data = [round(elem, 2) for elem in line_data] # round temp to 2 dp if len(line_data) > 0: print("The current temperature is:" + str(line_data[0]) + " celsius") break
Since no sample output is given, Here is a code that extracts all valid numbers from the text: a = "Temperature = 54.3F 62.5, 79999 54.3°C 23.3C" a+=' ' temp = [] i = 0 while i < len(a): if (a[i].isdigit() or a[i] == '.'): if a[i] == '.' and '.' in temp[-1]: x = a.find(' ', i) i = x temp.pop() elif i == 0: temp[-1] += a[i] elif len(temp)>0 and a[i-1] == temp[-1][-1]: temp[-1] += a[i] else: temp.append(a[i]) i += 1 temp = list(map(float, temp)) # Float casting print(temp) Output: ['54.3', '62.5', '79999', '54.3', '23.3']
Looking at this answer here: https://stackoverflow.com/a/4289557/7802476 A slight modification can also give decimals: >>> txt = "h3110 23 cat 444.4 rabbit 11 2 dog" >>> [float(s) for s in txt.split() if '.' in s and s.replace('.','').isdigit()] >>> [444.4] Your regex \d*\.\d* will match numbers such as {.2, 2., 2.2,...} but will not match {2} since \. has to be in the number. The above will also do the same. EDIT: The solution won't handle numbers that are attached to a string {2.2°C} where as the regex does. To make it handle units as well, [float(s.replace(f'{unit}', '')) for s in txt.split() if '.' in s and s.replace('.','').replace(f'{unit}', '').isdigit()] Where unit can be '°C' or 'F' for temperature. However, your regex matches all floating point numbers attached to any string. That is, cat2.2rabbit would also return 2.2, not sure if this should be returned.
Count of sub-strings that contain character X at least once. E.g Input: str = “abcd”, X = ‘b’ Output: 6
This question was asked in an exam but my code (given below) passed just 2 cases out of 7 cases. Input Format : single line input seperated by comma Input: str = “abcd,b” Output: 6 “ab”, “abc”, “abcd”, “b”, “bc” and “bcd” are the required sub-strings. def slicing(s, k, n): loop_value = n - k + 1 res = [] for i in range(loop_value): res.append(s[i: i + k]) return res x, y = input().split(',') n = len(x) res1 = [] for i in range(1, n + 1): res1 += slicing(x, i, n) count = 0 for ele in res1: if y in ele: count += 1 print(count)
When the target string (ts) is found in the string S, you can compute the number of substrings containing that instance by multiplying the number of characters before the target by the number of characters after the target (plus one on each side). This will cover all substrings that contain this instance of the target string leaving only the "after" part to analyse further, which you can do recursively. def countsubs(S,ts): if ts not in S: return 0 # shorter or no match before,after = S.split(ts,1) # split on target result = (len(before)+1)*(len(after)+1) # count for this instance return result + countsubs(ts[1:]+after,ts) # recurse with right side print(countsubs("abcd","b")) # 6 This will work for single character and multi-character targets and will run much faster than checking all combinations of substrings one by one.
Here is a simple solution without recursion: def my_function(s): l, target = s.split(',') result = [] for i in range(len(l)): for j in range(i+1, len(l)+1): ss = l[i] + l[i+1:j] if target in ss: result.append(ss) return f'count = {len(result)}, substrings = {result}' print(my_function("abcd,b")) #count = 6, substrings = ['ab', 'abc', 'abcd', 'b', 'bc', 'bcd']
Here you go, this should help from itertools import combinations output = [] initial = input('Enter string and needed letter seperated by commas: ') #Asking for input list1 = initial.split(',') #splitting the input into two parts i.e the actual text and the letter we want common in output text = list1[0] final = [''.join(l) for i in range(len(text)) for l in combinations(text, i+1)] #this is the core part of our code, from this statement we get all the available combinations of the set of letters (all the way from 1 letter combinations to nth letter) for i in final: if 'b' in i: output.append(i) #only outputting the results which have the required letter/phrase in it
How can I group a sorted list into tuples of start and endpoints of consecutive elements of this list?
Suppose that my sorted list is as such: L = ["01-string","02-string","03-string","05-string","07-string","08-string"] As you can see this list has been sorted. I now want the start and end points of each block of continuous strings in this list, for example, the output for this should be: L_continuous = [("01-string", "03-string"),("05-string","05-string"),("07-string","08-string")] So, just to clarify, I need a list of tuples and in each of these tuples I need the start and endpoint of each consecutive block in my list. So, for example, elements 0, 1 and 2 in my list are consecutive because 01,02,03 are consecutive numbers - so the start and endpoints would be "01-string" and "03-string". The numbers 1-3 are consecutive so they form a block, whereas 5 does not have any consecutive numbers in the list so it forms a block by itself.
Not a one-liner, but something like this might work: L = ["01-string","02-string","03-string","05-string","07-string","08-string"] counter = None # lastNum = None firstString = "" lastString = "" L_continuous = list() for item in L: currentNum = int(item[0:2]) if counter is None: # startTuple firstString = item counter = currentNum lastString = item continue if counter + 1 == currentNum: # continuation of block lastString = item counter += 1 continue if currentNum > counter + 1: # end of block L_continuous.append((firstString,lastString)) firstString = item counter = currentNum lastString = item continue else: print ('error - not sorted or unique numbers') # add last block L_continuous.append((firstString,lastString)) print(L_continuous)
The first thing to do is extract an int from the string data, so that we can compare consecutive numbers: def extract_int(s): return int(s.split('-')[0]) Then a straightforward solution is to keep track of the last number seen, and emit a new block when it is not consecutive with the previous one. At the end of the loop, we need to emit a block of whatever is "left over". def group_by_blocks(strs): blocks = [] last_s = first_s = strs[0] last_i = extract_int(last_s) for s in strs[1:]: i = extract_int(s) if i != last_i + 1: blocks.append( (first_s, last_s) ) first_i, first_s = i, s last_i, last_s = i, s blocks.append( (first_s, last_s) ) return blocks Example: >>> group_by_blocks(L) [('01-string', '03-string'), ('05-string', '05-string'), ('07-string', '08-string')]
Python parse ip regex
I want to be able to parse something like "10.[3-25].0.X" into the actual list of ip addresses described by this rule, so for the above example rule the list would be [10.3.0.0, 10.3.0.1....10.25.0.255]. What's the best way to do it? So far the only thing I was able to come out with is the following awful-looking function: wc = ''.join(wc.split()).upper() wc = re.sub(r'(?<![\[-])(\d+)(?![\]-])', r'[\1-\1]', wc) wc = re.sub(r'X', r'[0-255]', wc).split('.') ips = [] for i in range(int(re.findall(r'(\d+)-(\d+)', wc[0])[0][0]), int(re.findall(r'(\d+)-(\d+)', wc[0])[0][1]) + 1): for j in range(int(re.findall(r'(\d+)-(\d+)', wc[1])[0][0]), int(re.findall(r'(\d+)-(\d+)', wc[1])[0][1]) + 1): for k in range(int(re.findall(r'(\d+)-(\d+)', wc[2])[0][0]), int(re.findall(r'(\d+)-(\d+)', wc[2])[0][1]) + 1): for p in range(int(re.findall(r'(\d+)-(\d+)', wc[3])[0][0]), int(re.findall(r'(\d+)-(\d+)', wc[3])[0][1]) + 1): ips.append(str(i) + '.' + str(j) + '.' + str(k) + '.' + str(p)) return ips Any improvement ideas would be greatly appreciated.
You could make this a lot simpler. First, instead of writing the exact same thing four times, use a loop or a listcomp: ranges = [range(int(re.findall(r'(\d+)-(\d+)', wc[i])[0][0]), int(re.findall(r'(\d+)-(\d+)', wc[i])[0][1]) + 1) for i in range(4)] You can also turn the nested loop into a flat loop over the cartesian product: for i, j, k, p in itertools.product(*ranges): And you can turn that long string-concatenation mess into a simple format or join call: ips.append('{}.{}.{}.{}'.format(i, j, k, p)) # OR ips.append('.'.join(map(str, (i, j, k, p)))) And that means you don't need to split out the 4 components in the first place: for components in itertools.product(*ranges): ips.append('{}.{}.{}.{}'.format(*components)) # OR ips.append('.'.join(map(str, components))) And now that the loop is so trivial, you can turn it into a listcomp: ips = ['{}.{}.{}.{}'.format(*components) for components in itertools.product(*ranges)]
Here's a possible example using itertools.product. The idea is to first evaluate the "template" (e.g. 1.5.123.2-5, 23.10-20.X.12, ...) octet by octet (each yielding a list of values) and then take the cartesian product of those lists. import itertools import re import sys def octet(s): """ Takes a string which represents a single octet template. Returns a list of values. Basic sanity checks. """ if s == 'X': return xrange(256) try: low, high = [int(val) for val in s.strip('[]').split('-')] if low > high or low < 0 or high > 255: raise RuntimeError('That is no valid range.') return xrange(low, high + 1) except ValueError as err: number = int(s) if not 0 <= number <= 255: raise ValueError('Only 0-255 allowed.') return [number] if __name__ == '__main__': try: template = sys.argv[1] octets = [octet(s) for s in template.split('.')] for parts in itertools.product(*octets): print('.'.join(map(str, parts))) except IndexError as err: print('Usage: %s IP-TEMPLATE' % (sys.argv[0])) sys.exit(1) (Small) Examples: $ python ipregex.py '1.5.123.[2-5]' 1.5.123.2 1.5.123.3 1.5.123.4 1.5.123.5 $ python ipregex.py '23.[19-20].[200-240].X' 23.19.200.0 23.19.200.1 23.19.200.2 ... 23.20.240.253 23.20.240.254 23.20.240.255
ip= re.search(r'(\d{1,3}.){3}\d{1,3}','192.168.1.100') print(ip.group()) o/p==>192.168.1.100 case:2 ips= re.findall(r'(\d{1,3}.){3}\d{1,3}','192.168.1.100') print(ips) o/p==> ['1.'] case:3 ips= re.findall(r'(?:\d{1,3}.){3}\d{1,3}','192.168.1.100') print(ips) o/p==>['192.168.1.100'] why the re for case1(search) didnt work for case2(findall)
list object not callable project euler 59
I am trying to do project euler problem 59, i am having an issue in that one of the necessary methods won't work as the program returns: xorNum = test(f,j) TypeError: 'list' object is not callable. Both f and j are integers and when I used the test method with two random integers, it worked perfectly. Does anyone have any ideas why it may not be working? def main(): cipherText = """79,59,12,2,79,35,8,28,20,2,3,68,8,9,68,45,0,12,9,67,68,4,7,5,23,27,1,21,79,85,78,79,85,71,38,10,71,27,12,2,79,6,2,8,13,9,1,13,9,8,68,19,7,1,71,56,11,21,11,68,6,3,22,2,14,0,30,79,1,31,6,23,19,10,0,73,79,44,2,79,19,6,28,68,16,6,16,15,79,35,8,11,72,71,14,10,3,79,12,2,79,19,6,28,68,32,0,0,73,79,86,71,39,1,71,24,5,20,79,13,9,79,16,15,10,68,5,10,3,14,1,10,14,1,3,71,24,13,19,7,68,32,0,0,73,79,87,71,39,1,71,12,22,2,14,16,2,11,68,2,25,1,21,22,16,15,6,10,0,79,16,15,10,22,2,79,13,20,65,68,41,0,16,15,6,10,0,79,1,31,6,23,19,28,68,19,7,5,19,79,12,2,79,0,14,11,10,64,27,68,10,14,15,2,65,68,83,79,40,14,9,1,71,6,16,20,10,8,1,79,19,6,28,68,14,1,68,15,6,9,75,79,5,9,11,68,19,7,13,20,79,8,14,9,1,71,8,13,17,10,23,71,3,13,0,7,16,71,27,11,71,10,18,2,29,29,8,1,1,73,79,81,71,59,12,2,79,8,14,8,12,19,79,23,15,6,10,2,28,68,19,7,22,8,26,3,15,79,16,15,10,68,3,14,22,12,1,1,20,28,72,71,14,10,3,79,16,15,10,68,3,14,22,12,1,1,20,28,68,4,14,10,71,1,1,17,10,22,71,10,28,19,6,10,0,26,13,20,7,68,14,27,74,71,89,68,32,0,0,71,28,1,9,27,68,45,0,12,9,79,16,15,10,68,37,14,20,19,6,23,19,79,83,71,27,11,71,27,1,11,3,68,2,25,1,21,22,11,9,10,68,6,13,11,18,27,68,19,7,1,71,3,13,0,7,16,71,28,11,71,27,12,6,27,68,2,25,1,21,22,11,9,10,68,10,6,3,15,27,68,5,10,8,14,10,18,2,79,6,2,12,5,18,28,1,71,0,2,71,7,13,20,79,16,2,28,16,14,2,11,9,22,74,71,87,68,45,0,12,9,79,12,14,2,23,2,3,2,71,24,5,20,79,10,8,27,68,19,7,1,71,3,13,0,7,16,92,79,12,2,79,19,6,28,68,8,1,8,30,79,5,71,24,13,19,1,1,20,28,68,19,0,68,19,7,1,71,3,13,0,7,16,73,79,93,71,59,12,2,79,11,9,10,68,16,7,11,71,6,23,71,27,12,2,79,16,21,26,1,71,3,13,0,7,16,75,79,19,15,0,68,0,6,18,2,28,68,11,6,3,15,27,68,19,0,68,2,25,1,21,22,11,9,10,72,71,24,5,20,79,3,8,6,10,0,79,16,8,79,7,8,2,1,71,6,10,19,0,68,19,7,1,71,24,11,21,3,0,73,79,85,87,79,38,18,27,68,6,3,16,15,0,17,0,7,68,19,7,1,71,24,11,21,3,0,71,24,5,20,79,9,6,11,1,71,27,12,21,0,17,0,7,68,15,6,9,75,79,16,15,10,68,16,0,22,11,11,68,3,6,0,9,72,16,71,29,1,4,0,3,9,6,30,2,79,12,14,2,68,16,7,1,9,79,12,2,79,7,6,2,1,73,79,85,86,79,33,17,10,10,71,6,10,71,7,13,20,79,11,16,1,68,11,14,10,3,79,5,9,11,68,6,2,11,9,8,68,15,6,23,71,0,19,9,79,20,2,0,20,11,10,72,71,7,1,71,24,5,20,79,10,8,27,68,6,12,7,2,31,16,2,11,74,71,94,86,71,45,17,19,79,16,8,79,5,11,3,68,16,7,11,71,13,1,11,6,1,17,10,0,71,7,13,10,79,5,9,11,68,6,12,7,2,31,16,2,11,68,15,6,9,75,79,12,2,79,3,6,25,1,71,27,12,2,79,22,14,8,12,19,79,16,8,79,6,2,12,11,10,10,68,4,7,13,11,11,22,2,1,68,8,9,68,32,0,0,73,79,85,84,79,48,15,10,29,71,14,22,2,79,22,2,13,11,21,1,69,71,59,12,14,28,68,14,28,68,9,0,16,71,14,68,23,7,29,20,6,7,6,3,68,5,6,22,19,7,68,21,10,23,18,3,16,14,1,3,71,9,22,8,2,68,15,26,9,6,1,68,23,14,23,20,6,11,9,79,11,21,79,20,11,14,10,75,79,16,15,6,23,71,29,1,5,6,22,19,7,68,4,0,9,2,28,68,1,29,11,10,79,35,8,11,74,86,91,68,52,0,68,19,7,1,71,56,11,21,11,68,5,10,7,6,2,1,71,7,17,10,14,10,71,14,10,3,79,8,14,25,1,3,79,12,2,29,1,71,0,10,71,10,5,21,27,12,71,14,9,8,1,3,71,26,23,73,79,44,2,79,19,6,28,68,1,26,8,11,79,11,1,79,17,9,9,5,14,3,13,9,8,68,11,0,18,2,79,5,9,11,68,1,14,13,19,7,2,18,3,10,2,28,23,73,79,37,9,11,68,16,10,68,15,14,18,2,79,23,2,10,10,71,7,13,20,79,3,11,0,22,30,67,68,19,7,1,71,8,8,8,29,29,71,0,2,71,27,12,2,79,11,9,3,29,71,60,11,9,79,11,1,79,16,15,10,68,33,14,16,15,10,22,73""" asciiDict = {} #create ascii table dictionary with number as key asciiDict2 = {} #reverse key value of above dictionary for char in range(256): keyVal = "%d: %c" % (char, char) slicer = keyVal.index(':') key = keyVal[0:slicer] val = keyVal[slicer+2:] asciiDict[int(key)] = val for key in asciiDict.keys(): newVal = asciiDict[key] asciiDict2[newVal] = key newlist = [int(n) for n in cipherText.split(',')] #convert cipher text into list of numbers listOfThreeChars = [] for n in range(len(newlist)): listOfThreeChars.append(newlist[n:n+3]) #create list of groups of three consecutive numbers in cipherText mostCommonDict = mostCommon(listOfThreeChars) mostFrequent3 = mostCommonDict[max(mostCommonDict.keys())] #most common three consecutive numbers, if the key is right these #numbers will become a common three letter word such as 'the' print testCipher(asciiDict,asciiDict2, 'jhd', mostFrequent3) def testCipher(asciiDict,asciiDict2, cipherKey, cipherExtract): cipherKeyAscii = [] test = [] output = [] for k in cipherKey: asciiNum = asciiDict2[k] cipherKeyAscii.append(asciiNum) print cipherKeyAscii for i in range(len(cipherKeyAscii)): f,j = cipherKeyAscii[i],cipherExtract[i] print type(f), type(j),f,j xorNum = test(f,j) #HERE IS WHERE THE PROBLEM IS test.append(xorNum) for final in test: letter = asciiDict[final] output.append(letter) return output def mostCommon(lst): #find most common three consecutive number combinations in text dic = {} for three in lst: key = three count = [] for n in lst: if n == key: count.append(1) dic[len(count)] = key return dic #return max(set(sum(lst, [])), key=sum(lst, []).count) def toBinary(decimalNumber): quotient = 1 remainder = 0 tmpNum = decimalNumber finalNumberList = [] n = "" #e.g. take 14... while quotient != 0: remainder = decimalNumber % 2 #14 % 2 = 0 quotient = decimalNumber / 2 #14 / 2 = 7 decimalNumber = quotient # 7 % 2 = 1 and so on... finalNumberList.insert( 0, remainder ) # Used because all numbers are in a list, i.e. convert to string for num in finalNumberList: n += str( num ) return n def XOR(number1, number2): number1List = [] number2List = [] XORoutput = [] for i in str(number1): #turn both binary numbers into lists number1List.append(int(i)) for i in str(number2): number2List.append(int(i)) if len(number1List)>len(number2List): #make sure they are same lengths diff = len(number1List) - len(number2List) for i in range(diff): number2List.insert(0,0) for i in range(len(number1List)): #XOR it if number1List[i] == number2List[i]: XORoutput.append(0) if number1List[i] != number2List[i]: XORoutput.append(1) num = int(''.join(map(str,XORoutput))) #turn XOR list into int return num def test(num1, num2): #convert input to binary and xor and return to integer print num1,num2 bin1 = toBinary(num1) #turn to binary bin2 = toBinary(num2) xor = XOR(bin1,bin2) #XOR output = int(str(xor),2) #return to number return output if __name__ == "__main__": #print main.__doc__ main()
You set test to a list; you cannot have both a function and a list use the same name: def main(): # other code test = [] # more code for i in range(len(cipherKeyAscii)): # more irrelevant code xorNum = test(f,j) test.append(xorNum) masking the function test(). You even use test as a list again on the very next line. Rename the list, or rename the function. Most of all, pick better, clearer names for your variables.
You defined test to be a list. You also defined it to be a function. De-conflict your names and you should be good to go.