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)
Related
I want to output something that looks like this (with 20 items per line, and two variables are needed, one for indexing, i, and one for the book name, bookname, in this case "Psalm"):
\hyperlink{Psalm1}{1} & \hyperlink{Psalm2}{2} & \hyperlink{Psalm3}{3} ...
Using Python (simplification of my loop, but enough to show the key error) I attempted:
for indx in range(1, 150, 20):
line = r""" \\hline
\\hyperlink{{{bn}{i}}}{{{i}}} & \\hyperlink{{{bn}{i+1}}}{{{i+1}}} & \\hyperlink{{{bn}{i+2}}}{{{i+2}}} ...
""".format( i=indx, bn = bookname)
What's the best way to recode to avoid the i+1 key error ?
Here is example of string generation (\hyperlink{Psalm1}{1}) using different methods:
i = 1
# string concatenation
formatted = r"\hyperlink{Psalm" + str(i) + "}{" + str(i) + "}"
# old-style formatting
formatted = r"\hyperlink{Psalm%d}{%d}" % (i, i))
# str.format
formatted = r"\hyperlink{{Psalm{0}}}{{{0}}}".format(i)
# f-string
formatted = rf"\hyperlink{{Psalm{i}}}{{{i}}}"
For this particular case I find old-style formatting more "clean" as it doesn't require doubling curly brackets.
To print 20 strings in each line you can pass generator which will produce formatted strings into str.join().
Full code:
stop = 150
step = 20
for i in range(1, stop + 1, step):
print(
" & ".join(
r"\hyperlink{Psalm%d}{%d}" % (n, n)
for n in range(i, min(i + step, stop + 1))
)
)
Or you can also use "one-liner":
stop = 150
step = 20
print(
"\n".join( # use " &\n".join(...) if you need trailing '&'
" & ".join(
r"\hyperlink{Psalm%d}{%d}" % (n, n)
for n in range(i, min(i + step, stop + 1))
)
for i in range(1, stop + 1, step)
)
)
Try to use f string
for i in range(1, 150, 20):
print(f"\\hyperlinkPPsalm{i} & \\hyperlinkPsalm{i+1} & \\hyperlinkPsalm{i+2}")
I'm writing this code
for n in filtered_list:
for a in range(1,3):
duplicate = str(filenameRegex.group(1) + "(" + n + ")" + filenameRegex.group(2))
I've been wondering is there a more concise way to write this? I mean the "(" + n + ")" part. I was thinking about something like %s s = n, but I don't know and couldn't trial-and-error how to use it in this case.
In this case, you have to use %d instead of %s because n is an integer, not a string !
for n in filtered_list:
for a in range(1, 3):
duplicate = "%s(%d)%s" % (filenameRegex.group(1), n, filenameRegex.group(2))
This is old-school formatting, though. In Python 3 you can use f-strings:
for n in filtered_list:
for a in range(1, 3):
duplicate = f"{filenameRegex.group(1)}({n}){filenameRegex.group(2)}"
you can try like this:
duplicate = "%s(%s)%s"%(filenameRegex.group(1),n,filenameRegex.group(2))
or
duplicate = "{0}({1}){2}".format(filenameRegex.group(1),n,filenameRegex.group(2))
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
I want to be able to generate 12 character long chain, of hexadecimal, BUT with no more than 2 identical numbers duplicate in the chain: 00 and not 000
Because, I know how to generate ALL possibilites, including 00000000000 to FFFFFFFFFFF, but I know that I won't use all those values, and because the size of the file generated with ALL possibilities is many GB long, I want to reduce the size by avoiding the not useful generated chains.
So my goal is to have results like 00A300BF8911 and not like 000300BF8911
Could you please help me to do so?
Many thanks in advance!
if you picked the same one twice, remove it from the choices for a round:
import random
hex_digits = set('0123456789ABCDEF')
result = ""
pick_from = hex_digits
for digit in range(12):
cur_digit = random.sample(hex_digits, 1)[0]
result += cur_digit
if result[-1] == cur_digit:
pick_from = hex_digits - set(cur_digit)
else:
pick_from = hex_digits
print(result)
Since the title mentions generators. Here's the above as a generator:
import random
hex_digits = set('0123456789ABCDEF')
def hexGen():
while True:
result = ""
pick_from = hex_digits
for digit in range(12):
cur_digit = random.sample(hex_digits, 1)[0]
result += cur_digit
if result[-1] == cur_digit:
pick_from = hex_digits - set(cur_digit)
else:
pick_from = hex_digits
yield result
my_hex_gen = hexGen()
counter = 0
for result in my_hex_gen:
print(result)
counter += 1
if counter > 10:
break
Results:
1ECC6A83EB14
D0897DE15E81
9C3E9028B0DE
CE74A2674AF0
9ECBD32C003D
0DF2E5DAC0FB
31C48E691C96
F33AAC2C2052
CD4CEDADD54D
40A329FF6E25
5F5D71F823A4
You could also change the while true loop to only produce a certain number of these based on a number passed into the function.
I interpret this question as, "I want to construct a rainbow table by iterating through all strings that have the following qualities. The string has a length of 12, contains only the characters 0-9 and A-F, and it never has the same character appearing three times in a row."
def iter_all_strings_without_triplicates(size, last_two_digits = (None, None)):
a,b = last_two_digits
if size == 0:
yield ""
else:
for c in "0123456789ABCDEF":
if a == b == c:
continue
else:
for rest in iter_all_strings_without_triplicates(size-1, (b,c)):
yield c + rest
for s in iter_all_strings_without_triplicates(12):
print(s)
Result:
001001001001
001001001002
001001001003
001001001004
001001001005
001001001006
001001001007
001001001008
001001001009
00100100100A
00100100100B
00100100100C
00100100100D
00100100100E
00100100100F
001001001010
001001001011
...
Note that there will be several hundred terabytes' worth of values outputted, so you aren't saving much room compared to just saving every single string, triplicates or not.
import string, random
source = string.hexdigits[:16]
result = ''
while len(result) < 12 :
idx = random.randint(0,len(source))
if len(result) < 3 or result[-1] != result[-2] or result[-1] != source[idx] :
result += source[idx]
You could extract a random sequence from a list of twice each hexadecimal digits:
digits = list('1234567890ABCDEF') * 2
random.shuffle(digits)
hex_number = ''.join(digits[:12])
If you wanted to allow shorter sequences, you could randomize that too, and left fill the blanks with zeros.
import random
digits = list('1234567890ABCDEF') * 2
random.shuffle(digits)
num_digits = random.randrange(3, 13)
hex_number = ''.join(['0'] * (12-num_digits)) + ''.join(digits[:num_digits])
print(hex_number)
You could use a generator iterating a window over the strings your current implementation yields. Sth. like (hex_str[i:i + 3] for i in range(len(hex_str) - window_size + 1)) Using len and set you could count the number of different characters in the slice. Although in your example it might be easier to just compare all 3 characters.
You can create an array from 0 to 255, and use random.sample with your list to get your list
This question already has an answer here:
Three Rail Decrypt when rails are different lengths
(1 answer)
Closed 8 years ago.
Im having a ton of trouble trying to figure out how to get my decrypt function to work. It works in ideal cases where the msg is divisible by 3, but after that i am totally lost. I have a vague idea of what i have to do because i was able to get this to work with two rails, but there are more possibilities with three rails. Im quite lost :( Also all those print statements are just to try and help me figure out what is going on within my program.
import sys
def main():
plaintext="abcdefgh"
print(threeRailEncrypt(plaintext))
print(threeRailDecrypt(threeRailEncrypt(plaintext)))
def threeRailEncrypt(plaintext):
ciphertext=""
rail1=""
rail2=""
rail3=""
for i in range(len(plaintext)):
if i%3 == 0:
rail1=rail1+plaintext[i]
elif i%3 == 1:
rail2=rail2+plaintext[i]
else:
rail3=rail3+plaintext[i]
ciphertext=rail1+rail2+rail3
return(ciphertext)
def threeRailDecrypt(msg):
if len(msg)%3==0:
third=len(msg)//3
print(third)
rail1=msg[:third]
rail2=msg[third:third*2]
rail3=msg[third*2:]
print(rail1,rail2,rail3)
dm=""
for i in range(third):
dm=dm+rail1[i]
dm=dm+rail2[i]
dm=dm+rail3[i]
else:
third=(len(msg)//3)+1
print(third)
rail1=msg[:third]
rail2=msg[third:third*2]
rail3=msg[third*2:]
print(rail1,rail2,rail3)
dm=""
for i in range(third):
dm=dm+rail1[i]
print(dm)
dm=dm+rail2[i]
print(dm)
dm=dm+rail3[i]
print(dm)
if len(rail2)>len(rail3):
dm=dm+rail2[-1]
return(dm)
main()
Progress-
def threeRailDecrypt(cypher, rails = 3):
length = len (cypher)
for i in range(rails):
lens=(length//rails)
if length % rails > i:
lens=lens+1
print(lens)
I will add a second answer with a completely different approach and more explicit code:
def dec2 (cypher):
length = len (cypher)
if length < 4: return cypher
third = length // 3 + (1 if length % 3 else 0)
cypher = list (cypher)
if length % 3 == 1: cypher.insert (third * 2 - 1, '')
return ''.join (''.join (cypher [i::third] ) for i in range (third) )
Or just shock your teacher:
enc = lambda p:''.join(p[_::3]for _ in range(3))
dec = lambda c:c if len(c)<4 else(lambda *a:(lambda c,t:
''.join(''.join(c[i::t])for i in range(t)))((lambda c,t,
l:c[:t*2-1]+(['']if l%3==1 else[])+c[t*2-1:])(*a),a [2])
)(*(lambda c:(list(c),len(c),len(c)//3+(1 if len(c)%3
else 0)))(c))
New:
You just have to split up the strings again as you did in your encryption function and then iterate over them to put each character back at it's real place.
def decrypt(crypt):
rails = []
result = ""
rails.append(crypt[:(len(crypt)+2)/3])
rails.append(crypt[(len(crypt)+2)/3:-(len(crypt)-2)/3])
rails.append(crypt[-(len(crypt)/3):])
for x in range(len(crypt)):
result += rails[x%3][:1]
rails[x%3] = rails[x%3][1:]
return(result)
I think Hyperboreus second solution is cooler though!
OLD(Interesting thought connected to the comments, didn't work as expected for longer strings though):
You will just have to reverse what you do in the encryption, so in the encryption you scramble the text by splitting them up with modulo 3. The simplest way to get the plaintext back again is to simply run the encryption method the length of the string minus two times on the encrypted string and it will be back to plain text.
def threeRailDecrypt(crypt):
for i in range(len(crypt)-2):
crypt = threeRailEncrypt(crypt)
return(crypt)
Hyperboreus solution is the proper one, but this is a bit simpler to understand I reckon.
Example with the string test:
test - untouched
ttes - first iteration (encryption)
tste - second iteration
test - third iteration
This should work. You just need to figure out how long the different rails are:
from itertools import zip_longest as zip
def enc (plain, rails = 3):
return ''.join (plain [i::rails] for i in range (rails) )
def dec (cypher, rails = 3):
length = len (cypher)
lens = [length // rails + (1 if length % rails > i else 0) for i in range (rails) ]
paths = [cypher [sum (lens [:i] ):sum (lens [:i + 1] ) ] for i in range (rails) ]
return ''.join (''.join (x for x in x if x) for x in zip (*paths) )
plain = 'abcdefghijk'
for i in range (10):
a = plain [:i]
b = enc (a)
c = dec (b)
print (a, b, c)
Should work for arbitrary (>0) number of rails used.
EDIT: Without zip_longest:
def enc (plain, rails = 3):
return ''.join (plain [i::rails] for i in range (rails) )
def dec (cypher, rails = 3):
length = len (cypher)
lens = [length // rails + (1 if length % rails > i else 0) for i in range (rails) ]
paths = [cypher [sum (lens [:i]):sum (lens [:i + 1] ) ] for i in range (rails) ]
maxLen = len (paths [0] )
paths = [list (path) + ( [] if len (path) == maxLen else [''] ) for path in paths]
return ''.join (''.join (x) for x in zip (*paths) )
Explanation of the decryption:
length = len (cypher)
Store the length of the cypher text in a local variable for convenience.
lens = [length // rails + (1 if length % rails > i else 0) for i in range (rails) ]
Now we calculate the length of each rail. The length of each rail is the length of the cypher text divided by the number of rails (in your case 3), and maybe (the if part) plus 1 if the length is not exactly divisible.
paths = [cypher [sum (lens [:i] ):sum (lens [:i + 1] ) ] for i in range (rails) ]
Now slice the cypher text into the different rails, according to the rail lengths we have just calculated.
maxLen = len (paths [0] )
The maximum length of a rail is equal to the length of the first rail.
paths = [list (path) + ( [] if len (path) == maxLen else [''] ) for path in paths]
Now add an empty character to the end of each rail which is too short, so all rails have the same length and can be zipped.
return ''.join (''.join (x) for x in zip (*paths) )
Zip the rails, join each resulting tuple into a string, then join all those strings into one long string and return it.