How to match the numeric value in a regular expression? - regex

Okay, this is quite an interesting challenge I have got myself into.
My RegEx takes as input lines like the following:
147.63.23.156/159
94.182.23.55/56
134.56.33.11/12
I need it to output a regular expression that matches the range represented. Let me explain.
For example, if the RegEx receives 147.63.23.156/159, then it needs to output a RegEx that matches the following:
147.63.23.156
147.63.23.157
147.63.23.158
147.63.23.159
How can I do this?
Currently I have:
(\d{1,3}\.\d{1,3}\.\d{1,3}\.)(\d{1,3})/(\d{1,3})
$1 contains the first xxx.xxx.xxx. part
$2 contains the lower range for the number
$3 contains the upper range for the number

Regexes are really not a great way to validate IP addresses, I want to make that clear right up front. It is far, far easier to parse the addresses and do some simple arithmetic to compare them. A couple of less thans and greater thans and you're there.
That said, it seemed like it would be a fun exercise to write a regex generator. I came up with a big mess of Python code to generate these regexes. Before I show the code, here's a sample of the regexes it produces for a couple of IP ranges:
1.2.3.4 to 1.2.3.4 1\.2\.3\.4
147.63.23.156 to 147.63.23.159 147\.63\.23\.15[6-9]
10.7.7.10 to 10.7.7.88 10\.7\.7\.([1-7]\d|8[0-8])
127.0.0.0 to 127.0.1.255 127\.0\.[0-1]\.(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))
I'll show the code in two parts. First, the part that generates regexes for simple integer ranges. Second, the part that handles full IP addresses.
Matching number ranges
The first step is to figure out how to generate a regex that matches an arbitrary integer range, say 12-28 or 0-255. Here's an example of the regexes my implementation comes up with:
156 to 159 15[6-9]
1 to 100 [1-9]|[1-9]\d|100
0 to 255 \d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5])
And now the code. There are numerous comments inline explaining the logic behind it. Overall it relies on a lot of recursion and special casing to try to keep the regexes lean and mean.
import sys, re
def range_regex(lower, upper):
lower, upper = str(lower), str(upper)
# Different lengths, for instance 1-100. Combine regex(1-9) and
# regex(10-100).
if len(lower) != len(upper):
return '%s|%s' % (
range_regex(lower, '9' * len(lower)),
range_regex(10 ** (len(lower)), upper)
)
ll, lr = lower[0], lower[1:]
ul, ur = upper[0], upper[1:]
# One digit numbers.
if lr == '':
if ll == '0' and ul == '9':
return '\\d'
else:
return '[%s-%s]' % (ll, ul)
# Same first digit, for instance 12-14. Concatenate "1" and regex(2-4).
elif ll == ul:
return ll + sub_range_regex(lr, ur)
# All zeros to all nines, for instance 100-399. Concatenate regex(1-3)
# and the appropriate number of \d's.
elif lr == '0' * len(lr) and ur == '9' * len(ur):
return range_regex(ll, ul) + '\\d' * len(lr)
# All zeros on left, for instance 200-649. Combine regex(200-599) and
# regex(600-649).
elif lr == '0' * len(lr):
return '%s|%s' % (
range_regex(lower, str(int(ul[0]) - 1) + '9' * len(ur)),
range_regex(ul + '0' * len(ur), upper)
)
# All nines on right, for instance 167-499. Combine regex(167-199) and
# regex(200-499).
elif ur == '9' * len(ur):
return '%s|%s' % (
range_regex(lower, ll + '9' * len(lr)),
range_regex(str(int(ll[0]) + 1) + '0' * len(lr), upper)
)
# First digits are one apart, for instance 12-24. Combine regex(12-19)
# and regex(20-24).
elif ord(ul[0]) - ord(ll[0]) == 1:
return '%s%s|%s%s' % (
ll, sub_range_regex(lr, '9' * len(lr)),
ul, sub_range_regex('0' * len(ur), ur)
)
# Far apart, uneven numbers, for instance 15-73. Combine regex(15-19),
# regex(20-69), and regex(70-73).
else:
return '%s|%s|%s' % (
range_regex(lower, ll + '9' * len(lr)),
range_regex(str(int(ll[0]) + 1) + '0' * len(lr),
str(int(ul[0]) - 1) + '9' * len(ur)),
range_regex(ul + '0' * len(ur), upper)
)
# Helper function which adds parentheses when needed to sub-regexes.
# Sub-regexes need parentheses if they have pipes that aren't already
# contained within parentheses. For example, "6|8" needs parentheses
# but "1(6|8)" doesn't.
def sub_range_regex(lower, upper):
orig_regex = range_regex(lower, upper)
old_regex = orig_regex
while True:
new_regex = re.sub(r'\([^()]*\)', '', old_regex)
if new_regex == old_regex:
break
else:
old_regex = new_regex
continue
if '|' in new_regex:
return '(' + orig_regex + ')'
else:
return orig_regex
Matching IP address ranges
With that capability in place, I then wrote a very similar-looking IP range function to work with full IP addresses. The code is very similar to the code above except that we're working in base 256 instead of base 10, and the code throws around lists instead of strings.
import sys, re, socket
def ip_range_regex(lower, upper):
lower = [ord(c) for c in socket.inet_aton(lower)]
upper = [ord(c) for c in socket.inet_aton(upper)]
return ip_array_regex(lower, upper)
def ip_array_regex(lower, upper):
# One octet left.
if len(lower) == 1:
return range_regex(lower[0], upper[0])
# Same first octet.
if lower[0] == upper[0]:
return '%s\.%s' % (lower[0], sub_regex(ip_array_regex(lower[1:], upper[1:])))
# Full subnet.
elif lower[1:] == [0] * len(lower[1:]) and upper[1:] == [255] * len(upper[1:]):
return '%s\.%s' % (
range_regex(lower[0], upper[0]),
sub_regex(ip_array_regex(lower[1:], upper[1:]))
)
# Partial lower subnet.
elif lower[1:] == [0] * len(lower[1:]):
return '%s|%s' % (
ip_array_regex(lower, [upper[0] - 1] + [255] * len(upper[1:])),
ip_array_regex([upper[0]] + [0] * len(upper[1:]), upper)
)
# Partial upper subnet.
elif upper[1:] == [255] * len(upper[1:]):
return '%s|%s' % (
ip_array_regex(lower, [lower[0]] + [255] * len(lower[1:])),
ip_array_regex([lower[0] + 1] + [0] * len(lower[1:]), upper)
)
# First octets just 1 apart.
elif upper[0] - lower[0] == 1:
return '%s|%s' % (
ip_array_regex(lower, [lower[0]] + [255] * len(lower[1:])),
ip_array_regex([upper[0]] + [0] * len(upper[1:]), upper)
)
# First octets more than 1 apart.
else:
return '%s|%s|%s' % (
ip_array_regex(lower, [lower[0]] + [255] * len(lower[1:])),
ip_array_regex([lower[0] + 1] + [0] * len(lower[1:]),
[upper[0] - 1] + [255] * len(upper[1:])),
ip_array_regex([upper[0]] + [0] * len(upper[1:]), upper)
)

If you just need to build them one at at time, this website will do the trick.
If you need code, and don't mind python, this code does it for any arbitrary numeric range.

If it's for Apache... I haven't tried it, but it might work:
RewriteCond %{REMOTE_ADDR} !<147.63.23.156
RewriteCond %{REMOTE_ADDR} !>147.63.23.159
(Two consecutive RewriteConds are joined by a default logical AND)
Just have to be careful with ranges with differing number of digits (e.g. 95-105 should be broken into 95-99 and 100-105, since it is lexicographic ordering).

I absolutely agree with the commenters, a pure-regex solution would be the wrong tool for the job here. Just use the regular expression you already have to extract the prefix, minimum, and maximum values,
$prefix, $minimum, $maximum = match('(\d{1,3}\.\d{1,3}\.\d{1,3}\.)(\d{1,3})/(\d{1,3})', $line).groups()
then test your IP address against ${prefix}(\d+),
$lastgroup = match($prefix + '(\d+)', $addr).groups()[0]
and compare that last group to see if it falls within the proper range,
return int($minimum) <= int($lastgroup) <= int($maximum)
Code examples are pseudocode, of course - convert to your language of choice.

To my knowledge, this can't be done with straight up regex, but would also need some code behind it. For instance, in PHP you could use the following:
function make_range($ip){
$regex = '#(\d{1,3}\.\d{1,3}\.\d{1,3}\.)(\d{1,3})/(\d{1,3})#';
if ( preg_match($regex, $ip, $matches) ){
while($matches[1] <= $matches[2]){
print "{$matches[0]}.{$matches[1]}";
$matches[1]++;
}
} else {
exit('not a supported IP range');
}
}
For this to work with a RewriteCond, I think some black magic would be in order...
How is this going to be used with RewriteCond, anyways? Do you have several servers and want to just quickly make a .htaccess file easily? If so, then just add that function to a bigger script that takes some arguments and burps out a .htaccess file.

Related

Regular expression for equations, variable number of inside parenthesis

I'm trying to write Regex for the case where I have series of equations, for example:
a = 2 / (1 + exp(-2*n)) - 1
a = 2 / (1 + e) - 1
a = 2 / (3*(1 + exp(-2*n))) - 1
In any case I need to capture content of the outer parenthesis, so 1 + exp(-2*n), 1+e and 3*(1 + exp(-2*n)) respectively.
I can write expression that will catch one of them, like:
\(([\w\W]*?\))\) will perfectly catch 1 + exp(-2*n)
\(([\w\W]*?)\) will catch 1+e
\(([\w\W]*?\))\)\) will catch 3*(1 + exp(-2*n))
But it seems silly to pass three lines of code for something such simple. How can I bundle it? Please take a note that I will be processing text (in loop) line-by-line anyway, so you don't have to bother for securing operator to not greedy take next line.
Edit:
Un-nested brackets are also allowed: a = 2 / (1 + exp(-2*n)) - (2-5)
The commented code below does not use regular expressions, but does parse char arrays in MATLAB and output the terms which contain top-level brackets.
So in your 3 question examples with a single set of nested brackets, it returns the outermost bracketed term.
In the example from your comment where there are two or more (possibly nested) terms within brackets at the "top level", it returns both terms.
The logic is as follows, see the comments for more details
Find the left (opening) and right (closing) brackets
Generate the "nest level" according to how many un-closed brackets there are at each point in the equation char
Find the indicies where the nesting level changes. We're interested in opening brackets where the nest level increases to 1 and closing brackets where it decreases from 1.
Extract the terms from these indices
e = { 'a = 2 / (1 + exp(-2*n)) - 1'
'a = 2 / (1 + e) - 1'
'a = 2 / (3*(1 + exp(-2*n))) - 1'
'a = 2 / (1 + exp(-2*n)) - (2-5)' };
str = cell(size(e)); % preallocate output
for ii = 1:numel(e)
str{ii} = parseBrackets_(e{ii});
end
function str = parseBrackets_( equation )
bracketL = ( equation == '(' ); % indicies of opening brackets
bracketR = ( equation == ')' ); % indicies of closing brackets
str = {}; % intialise empty output
if numel(bracketL) ~= numel(bracketR)
% Validate the input
warning( 'Could not match bracket pairs, count mismatch!' )
return
end
nL = cumsum( bracketL ); % cumulative open bracket count
nR = cumsum( bracketR ); % cumulative close bracket count
nestLevel = nL - nR; % nest level is number of open brackets not closed
nestLevelChanged = diff(nestLevel); % Get the change points in nest level
% get the points where the nest level changed to/from 1
level1L = find( nestLevel == 1 & [true,nestLevelChanged==1] ) + 1;
level1R = find( nestLevel == 1 & [nestLevelChanged==-1,true] );
% Compile cell array of terms within nest level 1 brackets
str = arrayfun( #(x) equation(level1L(x):level1R(x)), 1:numel(level1L), 'uni', 0 );
end
Outputs:
str =
{'1 + exp(-2*n)'}
{'1 + e'}
{'3*(1 + exp(-2*n))'}
{'1 + exp(-2*n)'} {'2-5'}

Find value of a string given a superstring regex

How can I match for a string that is a substring of a given input string, preferable with regex?
Given a value: A789Lfu891MatchMe2ENOTSTH, construct a regex that would match a string where the string is a substring of the given value.
Expected matches:
MatchMe
ENOTST
891
Expected Non Match
foo
A789L<fu891MatchMe2ENOTSTH_extra
extra_A789L<fu891MatchMe2ENOTSTH
extra_A789L<fu891MatchMe2ENOTSTH_extra
It seems easier for me to do the reverse: /\w*MatchMe\w*/, but I can't wrap my head around this problem.
Something like how SQL would do it:
SELECT * FROM my_table mt WHERE 'A789Lfu891MatchMe2ENOTSTH' LIKE '%' || mt.foo || '%';
Prefix suffixes
You could alternate prefix suffixes, like turn the superstring abcd into a pattern like ^(a|(a)?b|((a)?b)?c|(((a)?b)?c)?d)$. For your example, the pattern has 1253 characters (exactly 2000 fewer than #tobias_k's).
Python code to produce the regex, can then be tested with tobias_k's code (try it online):
from itertools import accumulate
t = "A789Lfu891MatchMe2ENOTSTH"
p = '^(' + '|'.join(accumulate(t, '({})?{}'.format)) + ')$'
Suffix prefixes
Suffix prefixes look nicer and match faster: ^(a(b(c(d)?)?)?|b(c(d)?)?|c(d)?|d)$. Sadly the generating code is less elegant.
Divide and conquer
For a shorter regex, we can use divide and conquer. For example for the superstring abcdefg, every substring falls into one of three cases:
Contains the middle character (the d). Pattern for that: ((a?b)?c)?d(e(fg?)?)?
Is left of that middle character. So recursively build a regex for the superstring abc: a|a?bc?|c.
Is right of that middle character. So recursively build a regex for the superstring efg: e|e?fg?|g.
And then make an alternation of those three cases:
a|a?bc?|c|((a?b)?c)?d(e(fg?)?)?|e|e?fg?|g
Regex length will be Θ(n log n) instead of our previous Θ(n2).
The result for your superstring example of 25 characters is this regex with 301 characters:
^(A|A?78?|8|((A?7)?8)?9(Lf?)?|Lf?|f|(((((A?7)?8)?9)?L)?f)?u(8(9(1(Ma?)?)?)?)?|89?|9|(8?9)?1(Ma?)?|Ma?|a|(((((((((((A?7)?8)?9)?L)?f)?u)?8)?9)?1)?M)?a)?t(c(h(M(e(2(E(N(O(T(S(TH?)?)?)?)?)?)?)?)?)?)?)?|c|c?hM?|M|((c?h)?M)?e(2E?)?|2E?|E|(((((c?h)?M)?e)?2)?E)?N(O(T(S(TH?)?)?)?)?|OT?|T|(O?T)?S(TH?)?|TH?|H)$
Benchmark
Speed benchmarks don't really make that much sense, as in reality we'd just do a regular substring check (in Python s in t), but let's do one anyway.
Results for matching all substrings of your superstring, using Python 3.9.6 on my PC:
1.09 ms just_all_substrings
25.18 ms prefix_suffixes
3.47 ms suffix_prefixes
3.46 ms divide_and_conquer
And on TIO and their "Python 3.8 (pre-release)" with quite different results:
0.30 ms just_all_substrings
46.90 ms prefix_suffixes
2.24 ms suffix_prefixes
2.95 ms divide_and_conquer
Regex lengths (also printed by the below benchmark code):
3253 characters - just_all_substrings
1253 characters - prefix_suffixes
1253 characters - suffix_prefixes
301 characters - divide_and_conquer
Benchmark code (Try it online!):
from timeit import repeat
import re
from itertools import accumulate
def just_all_substrings(t):
return "^(" + '|'.join(t[i:k] for i in range(0, len(t))
for k in range(i+1, len(t)+1)) + ")$"
def prefix_suffixes(t):
return '^(' + '|'.join(accumulate(t, '({})?{}'.format)) + ')$'
def suffix_prefixes(t):
return '^(' + '|'.join(list(accumulate(t[::-1], '{1}({0})?'.format))[::-1]) + ')$'
def divide_and_conquer(t):
def suffixes(t):
# Example: abc => ((a?b)?c)?
regex = f'{t[0]}?'
for c in t[1:]:
regex = f'({regex}{c})?'
return regex
def prefixes(t):
# Example: efg => (e(fg?)?)?
regex = f'{t[-1]}?'
for c in t[-2::-1]:
regex = f'({c}{regex})?'
return regex
def superegex(t):
n = len(t)
if n == 1:
return t
if n == 2:
return f'{t}?|{t[1]}'
mid = n // 2
contain = suffixes(t[:mid]) + t[mid] + prefixes(t[mid+1:])
left = superegex(t[:mid])
right = superegex(t[mid+1:])
return '|'.join([left, contain, right])
return '^(' + superegex(t) + ')$'
creators = just_all_substrings, prefix_suffixes, suffix_prefixes, divide_and_conquer,
t = "A789Lfu891MatchMe2ENOTSTH"
substrings = [t[start:stop]
for start in range(len(t))
for stop in range(start+1, len(t)+1)]
def test(p):
match = re.compile(p).match
return all(map(re.compile(p).match, substrings))
for creator in creators:
print(test(creator(t)), creator.__name__)
print()
print('Regex lengths:')
for creator in creators:
print('%5d characters -' % len(creator(t)), creator.__name__)
print()
for _ in range(3):
for creator in creators:
p = creator(t)
number = 10
time = min(repeat(lambda: test(p), number=number)) / number
print('%5.2f ms ' % (time * 1e3), creator.__name__)
print()
One way to "construct" such a regex would be to build a disjunction of all possible substrings of the original value. Example in Python:
import re
t = "A789Lfu891MatchMe2ENOTSTH"
p = "^(" + '|'.join(t[i:k] for i in range(0, len(t))
for k in range(i+1, len(t)+1)) + ")$"
good = ["MatchMe", "ENOTST", "891"]
bad = ["foo", "A789L<fu891MatchMe2ENOTSTH_extra",
"extra_A789L<fu891MatchMe2ENOTSTH",
"extra_A789L<fu891MatchMe2ENOTSTH_extra"]
assert all(re.match(p, s) is not None for s in good)
assert all(re.match(p, s) is None for s in bad)
For the value "abcd", this would e.g. be "^(a|ab|abc|abcd|b|bc|bcd|c|cd|d)$"; for the given example it's a bit longer, with 3253 characters...

regex: Match strings of numbers up to permutation of ciphers

I am trying to find a regex query, such that, for instance, the following strings match the same expression
"1116.67711..44."
"2224.43322..88."
"9993.35599..22."
"7779.91177..55."
I.e. formally "x1x1x1x2.x2x3x3x1x1..x4x4." where xi ≠ xj if i ≠ j, and where xi is some number from 1 to 9 inclusive.
Or (another example), the following strings match the same expression, but not the same expression as before:
"94..44.773399.4"
"25..55.886622.5"
"73..33.992277.3"
I.e. formally "x1x2..x2x2.x3x3x4x4x1x1.x2" where xi ≠ xj if i ≠ j, and where xi is some number from 1 to 9 inclusive.
That is two strings should be equal if they have the same form, but with the numbers internally permuted so that they are pairwise distinct.
The dots should mean a space in the sequence, this could be any value that is not a single digit number, and two "equal" strings, should have spaces the same places. If it helps, the strings all have the same length of 81 (above they all have a length of 15, as to not write too long strings).
That is, if I have some string as above, e.g. "3566.235.225..45" i want to have some reqular expression that i can apply to some database to find out if such a string already exists
Is it possible to do this?
The answer is fairly straightforward:
import re
pattern = re.compile(r'^(\d)\1{3}$')
print(pattern.match('1234'))
print(pattern.match('333'))
print(pattern.match('3333'))
print(pattern.match('33333'))
You capture what you need once, then tell the regex engine how often you need to repeat it. You can refer back to it as often as you like, for example for a pattern that would match 11.222.1 you'd use ^(\d)\1{1}\.(\d)\2{2}\.(\1){1}$.
Note that the {1} in there is superfluous, but it shows that the pattern can be very regular. So much so, that it's actually easy to write a function that solves the problem for you:
def make_pattern(grouping, separators='.'):
regex_chars = '.\\*+[](){}^$?!:'
groups = {}
i = 0
j = 0
last_group = 0
result = '^'
while i < len(grouping):
if grouping[i] in separators:
if grouping[i] in regex_chars:
result += '\\'
result += grouping[i]
i += 1
else:
while i < len(grouping) and grouping[i] == grouping[j]:
i += 1
if grouping[j] in groups:
group = groups[grouping[j]]
else:
last_group += 1
groups[grouping[j]] = last_group
group = last_group
result += '(.)'
j += 1
result += f'\\{group}{{{i-j}}}'
j = i
return re.compile(result+'$')
print(make_pattern('111.222.11').match('aaa.bbb.aa'))
So, you can give make_pattern a good example of the pattern and it will return the compiled regex for you. If you'd like other separators than '.', you can just pass those in as well:
my_pattern = make_pattern('11,222,11', separators=',')
print(my_pattern.match('aa,bbb,aa'))

Rearranging elements in Python

i am new to Python and i cant get this.I have a List and i want to take the input from there and write those in files .
p = ['Eth1/1', 'Eth1/5','Eth2/1', 'Eth2/4','Eth101/1/1', 'Eth101/1/2', 'Eth101/1/3','Eth102/1/1', 'Eth102/1/2', 'Eth102/1/3','Eth103/1/1', 'Eth103/1/2', 'Eth103/1/3','Eth103/1/4','Eth104/1/1', 'Eth104/1/2', 'Eth104/1/3','Eth104/1/4']
What i am trying :
with open("abc1.txt", "w+") as fw1, open("abc2.txt", "w+") as fw2:
for i in p:
if len(i.partition("/")[0]) == 4:
fw1.write('int ' + i + '\n mode\n')
else:
i = 0
while i < len(p):
start = p[i].split('/')
if (start[0] == 'Eth101'):
i += 3
key = start[0]
i += 1
while i < len(p) and p[i].split('/')[0] == key:
i += 1
end = p[i-1].split('/')
fw2.write('confi ' + start[0] + '/' + start[1] + '-' + end[1] + '\n mode\n')
What i am looking for :
abc1.txt should have
int Eth1/1
mode
int Eth1/5
mode
int Eth2/1
mode
int Eth 2/4
mode
abc2.txt should have :
int Eth101/1/1-3
mode
int Eth102/1/1-3
mode
int Eth103/1/1-4
mode
int Eth104/1/1-4
mode
So any Eth having 1 digit before " / " ( e:g Eth1/1 or Eth2/2
)should be in one file that is abc1.txt .
Any Eth having 3 digit before " / " ( e:g Eth101/1/1 or Eth 102/1/1
) should be in another file that is abc2.txt and .As these are in
ranges , need to write it like Eth101/1/1-3, Eth102/1/1-3 etc
Any Idea ?
I don't think you need a regex here, at all. All your items begin with 'Eth' followed by one or more digits. So you can check the length of the items before first / occurs and then write it to a file.
p = ['Eth1/1', 'Eth1/5','Eth2/1', 'Eth2/4','Eth101/1/1', 'Eth101/1/2', 'Eth101/1/3','Eth102/1/1', 'Eth102/1/2', 'Eth102/1/3','Eth103/1/1', 'Eth103/1/2', 'Eth103/1/3','Eth103/1/4','Eth104/1/1', 'Eth104/1/2', 'Eth104/1/3','Eth104/1/4']
with open("abc1.txt", "w+") as fw1, open("abc2.txt", "w+") as fw2:
for i in p:
if len(i.partition("/")[0]) == 4:
fw1.write('int ' + i + '\n mode\n')
else:
fw2.write('int ' + i + '\n mode\n')
I refactored your code a little to bring with-statement into play. This will handle correctly closing the file at the end. Also it is not necessary to iterate twice over the sequence, so it's all done in one iteration.
If the data is not as clean as provided, then you maybe want to use regexes. Independent of the regex itself, by writing if re.match(r'((Eth\d{1}\/\d{1,2})', "p" ) you proof if a match object can be created for given regex on the string "p", not the value of the variable p. This is because you used " around p.
So this should work for your example. If you really need a regex, this will turn your problem in finding a good regex to match your needs without any other issues.
As these are in ranges , need to write it like Eth101/1/1-3, Eth102/1/1-3 etc
This is something you can achieve by first computing the string and then write it in the file. But this is more like a separate question.
UPDATE
It's not that trivial to compute the right network ranges. Here I can present you one approach which doesn't change my code but adds some functionality. The trick here is to get groups of connected networks which aren't interrupted by their numbers. For that I've copied consecutive_groups. You can also do a pip install more-itertools of course to get that functionality. And also I transformed the list to a dict to prepare the magic and then retransformed dict to list again. There are definitely better ways of doing it, but this worked for your input data, at least.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from itertools import groupby
from operator import itemgetter
p = ['Eth1/1', 'Eth1/5', 'Eth2/1', 'Eth2/4', 'Eth101/1/1', 'Eth101/1/2',
'Eth101/1/3', 'Eth102/1/1', 'Eth102/1/2', 'Eth102/1/3', 'Eth103/1/1',
'Eth103/1/2', 'Eth103/1/3', 'Eth103/1/4', 'Eth104/1/1', 'Eth104/1/2',
'Eth104/1/3', 'Eth104/1/4']
def get_network_ranges(networks):
network_ranges = {}
result = []
for network in networks:
parts = network.rpartition("/")
network_ranges.setdefault(parts[0], []).append(int(parts[2]))
for network, ranges in network_ranges.items():
ranges.sort()
for group in consecutive_groups(ranges):
group = list(group)
if len(group) == 1:
result.append(network + "/" + str(group[0]))
else:
result.append(network + "/" + str(group[0]) + "-" +
str(group[-1]))
result.sort() # to get ordered results
return result
def consecutive_groups(iterable, ordering=lambda x: x):
"""taken from more-itertools (latest)"""
for k, g in groupby(
enumerate(iterable), key=lambda x: x[0] - ordering(x[1])
):
yield map(itemgetter(1), g)
# only one line added to do the magic
with open("abc1.txt", "w+") as fw1, open("abc2.txt", "w+") as fw2:
p = get_network_ranges(p)
for i in p:
if len(i.partition("/")[0]) == 4:
fw1.write('int ' + i + '\n mode\n')
else:
fw2.write('int ' + i + '\n mode\n')

Is there better way to change string for evaluation

Is there better way of changing one of numbers to float in division?
I am currently having 1 loop that scan my equation for '/' sign and second that take number after '/' and change it to float. How can I improve it?
#!/usr/bin/env python2.7
import Tkinter as tk
main = tk.Tk()
main.title('Calculator')
def insert_variable(i):
"""Inserts the user defined sign and puts it on the end the entry widget"""
END = len(calc_entry.get())
calc_entry.insert(END,i)
def calculate():
''' deletes all characters in the entry and inserts evaluation of the equation '''
equation = calc_entry.get()
try:
calc_entry.delete(0, len(equation)) # deletes all characters in the entry
for i in range(len(equation)-1):# loop for checking for special signs
if equation[i] == '/': #if there is '/' sign change one of numbers to float
for j in range(i+1,len(equation)): # loop for number of digits after '/' sign
if equation[j] == '.': # if there is dot go ones more
pass
else:
try:
int(equation[j])# if there is something other than digit go to exception
except ValueError:
equation = equation[:i+1] + str(float(equation[i+1:j])) + equation[j:]# change number after / to float and join all strings
break
if equation[i] == '^': # if there is ^ sign change it for '**'
equation = equation[:i] +'**'+ equation[i+1:]
print equation
calc_entry.insert(0, str(round(eval(equation), 3))) # evaluates (calculates) the equation after loop
except SyntaxError:
calc_entry.insert(0,'<ERROR>')
except ZeroDivisionError:
calc_entry.insert(0,'ERROR DIVISION BY 0')
calc_entry = tk.Entry(main) # creates an entry
calc_entry.grid(row =1, columnspan = 6)
bEquate = tk.Button(main, text="=", command = calculate)
bEquate.grid(row=5, column=3)
bDivision = tk.Button(main, text="/", command = lambda : insert_variable("/"))
bDivision.grid(row=3, column=5)
main.mainloop()
What if I have sqrt() function that gives sqrt sign to the last number? How can I implement it to the calculate() function?
sqrtChr = unichr(0x221A)
def insert_sqrt():
"""inserts sqrt sign"""
global sqrtChr
END = len(calc_entry.get())
equation = calc_entry.get() # takes the whole user's equation from the entry
for i in range(-1,-END-1,-1): # loop that goes from -1,-2,-3 to end-1
if i == -END: # if there are no exceptions like '.' or special sign, inserts sqrt character at beginning of string in the entry
calc_entry.insert(0,sqrtChr)
elif equation[i] == '.': # if there is a dot in equation go to next character in equation
pass
else:
try: # if there is a sign insert sqrt character after it and break loop
int(equation[i])
except ValueError:
calc_entry.insert((END+i+1),sqrtChr)
break
Use
from __future__ import division
and 1/2 will gives you 0.5 - the same eval("1/2")