Peter Waller wrote:
Okay, I got fed up with there not being any (obvious) good examples of
how to do bash-like brace expansion in Python, so I wrote it myself.
Here it is for all to enjoy!

If anyone has any better solutions or any other examples of how to do
this, I'd be glad to hear from them.

That was a little while ago and while I found Peter's BraceExpand was a good start it wasn't quite what I wanted. It's not quite the same as bash: for example, BraceExpand("{1,3..5}") gives "1 3 4 5" but bash gives "1 3..5" (use "{1,{3..5}}") and bash turns all of "{a..z}", "{z..a}, "{10..-10}" into sequences.

My version is a little shorter, mostly because it's heavily recursive so it may fail spectacularly on some pathological cases :-)

jch
import re

def expand_braces (string):
    pos = string.find('{')
    # simple, single element string
    if pos < 0:
            return [string]

    # find the left, middle comma-separated string and the right
    left = string[:pos]
    middle = []
    count = 1
    for i in range(pos+1,len(string)):
        if count == 1 and string[i] == ',':
            middle.append(string[pos+1:i])
            pos = i
        elif count == 1 and string[i] == '}':
            middle.append(string[pos+1:i])
            pos = i
            break
        elif string[i] == '{':
            count += 1
        elif string[i] == '}':
            count -= 1
    right = string[pos+1:]
    
    # just "{x..y}" is a special case
    if len(middle) == 1:
        r = re.match("^(-?\d+)\.\.(-?\d+)$", middle[0])
        if not r:
            r = re.match("^(.)\.\.(.)$", middle[0])
    else:
        r = None
    if r:
        middle = []
        start = r.group(1)
        end = r.group(2)
        if len(start) != 1 or len(end) != 1:
            mapper = str
            start = int(start)
            end = int(end)
        else:
            mapper = chr
            start = ord(start)
            end = ord(end)
        if start <= end:
            middle = map(mapper, range(start, end+1))
        else:
            middle = map(mapper, range(start, end-1, -1))

    # join all the bits together
    result = []
    right = expand_braces(right)
    for m in middle:
        for m1 in expand_braces(m):
            for r in right:
                result.append("".join((left, m1, r)))
    return result

        
if __name__ == "__main__":
    from pprint import pprint
    def e(s, r):
        res = " ".join(expand_braces(s))
        if res != r:
            print "*** FAILED: '%s'" % s
            print "  -EXPECTED '%s'" % r
            print "  ------GOT '%s'" % res
    e('hello', 'hello')
    e('{hello,world}', 'hello world')
    e('x{a,b}', 'xa xb')
    e('x{a,b,c}y', 'xay xby xcy')
    e('A{1,2,3}B-C{4,5,6}D', 'A1B-C4D A1B-C5D A1B-C6D A2B-C4D A2B-C5D A2B-C6D 
A3B-C4D A3B-C5D A3B-C6D')
    e('a{b,<{c,d}>}e', 'abe a<c>e a<d>e')
    e('{1..10x}', '1..10x')
    e('{x1..10}', 'x1..10')
    e('{1..10}', '1 2 3 4 5 6 7 8 9 10')
    e('a{1..10}b', 'a1b a2b a3b a4b a5b a6b a7b a8b a9b a10b')
    e('{a,b}1..10', 'a1..10 b1..10')
    e('{a,9..13,b}', 'a 9..13 b')
    e('<{a,{9..13},b}>', '<a> <9> <10> <11> <12> <13> <b>')
    e('electron_{n,{pt,eta,phi}[{1,2}]}', 'electron_n electron_pt[1] 
electron_pt[2] electron_eta[1] electron_eta[2] electron_phi[1] electron_phi[2]')
    e('Myfile{1,3..10}.root', 'Myfile1.root Myfile3..10.root')
    e('Myfile{1,{3..10}}.root', 'Myfile1.root Myfile3.root Myfile4.root 
Myfile5.root Myfile6.root Myfile7.root Myfile8.root Myfile9.root Myfile10.root')
    e('{pre,,post}amble', 'preamble amble postamble')
    e('amble{a,b,}}', 'amblea} ambleb} amble}')
    e('{1..10}', '1 2 3 4 5 6 7 8 9 10')
    e('{a..j}', 'a b c d e f g h i j')
    e('{10..1}', '10 9 8 7 6 5 4 3 2 1')
    e('{j..a}', 'j i h g f e d c b a')
    e('{10..-10}', '10 9 8 7 6 5 4 3 2 1 0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10')


    

-- 
http://mail.python.org/mailman/listinfo/python-list

Reply via email to