8

I'm trying to create a loop to generate and print strings as follows:

  1. Alphanumeric characters only:
  2. 0-9 are before A-Z, which are before a-z,
  3. Length goes up to 4 characters.

So, it would print:

  1. all strings from 0-z
  2. then from 00-zz
  3. then from 000-zzz
  4. then from 0000-zzzz

then it stops.

3
  • It's too difficult to understand your question... would a07z be a legal string or not? Commented Aug 20, 2011 at 18:15
  • Yes. All alphanumeric strings length 1-4 are legal Commented Aug 20, 2011 at 18:16
  • So when you say 0-9 are before A-Z you mean that 0000 should come before A000? Commented Aug 20, 2011 at 18:18

5 Answers 5

23
from string import digits, ascii_uppercase, ascii_lowercase
from itertools import product

chars = digits + ascii_uppercase + ascii_lowercase

for n in range(1, 4 + 1):
    for comb in product(chars, repeat=n):
        print ''.join(comb)

This first makes a string of all the numbers, uppercase letters, and lowercase letters.

Then, for each length from 1-4, it prints every possible combination of those numbers and letters.

Keep in mind this is A LOT of combinations -- 62^4 + 62^3 + 62^2 + 62.

Sign up to request clarification or add additional context in comments.

4 Comments

That's a cool solution. Also a cool thing to do is to make it a generator instead of directly printing the strings.
I know it's a ton of combinations, I was mostly doing it just for testing purposes. I cut it down to 3 characters instead of 4
Yeah, he said he wanted a "loop that printed strings" so that's what I gave him.
FYI ascii_lowercase + ascii_uppercase == ascii_letters
1
from string import digits, ascii_uppercase, ascii_lowercase
from itertools import product
chars = digits + ascii_uppercase + ascii_lowercase

def give_me_next(lst):
        lst = lst[::-1]
        change_next = False
        change = True
        n = 0
        for x in lst:
                if change_next == True:
                        change_next = False
                        pos = chars.find(x)
                        try:
                                a =  chars[pos+1]
                                lst = list(lst)
                                lst[n] = a
                                lst = "".join(lst)
                                x = a
                        except:
                                lst = list(lst)
                                lst[n] = '0'
                                lst = "".join(lst)
                                change_next = True
                                x = '0'

                pos = chars.find(x)
                try:
                        a =  chars[pos+1]
                        if change == True:
                                lst = list(lst)
                                lst[n] = a
                                lst = "".join(lst)
                                change = False
                except:
                        lst = list(lst)
                        lst[n] = '0'
                        lst = "".join(lst)
                        change_next = True

                n = n + 1

        lst = lst[::-1]
        return lst

a=  give_me_next('zzzzz')
while True:
        a =  give_me_next(a)
        print a

Comments

0

I dislike the answer given before me using product since looking at its implementation in the python documentation it seem to span the entire thing into a list in memory before starting to yield the results.

This is very bad for your case since, as agf himself said, the number of permutation here is huge (well over a million). For this case the yield statement was created - so that huge lists could be dynamically generated rather than spanned in memory (I also disliked the wasteful range where xrange is perfectly applicable).

I'd go for a solution like this:

def generate(chars, length, prefix = None):
    if length < 1:
        return
    if not prefix:
        prefix = ''
    for char in chars:
        permutation = prefix + char
        if length == 1:
            yield permutation
        else:
            for sub_permutation in generate(chars, length - 1, prefix = permutation):
                yield sub_permutation

This way, all that spans in memory is a recursive stack "n" deep, where "n" is the length of your permutations (4 in this case) and only a single element is returned each time.

chars is the set of chars to choose from, length is 4 and the use is rather similar to products, except that it doesn't span the whole list in memory during run time.

2 Comments

It says in the product description -- "This function is equivalent to the following code, except that the actual implementation does not build up intermediate results in memory:" All of the tools in itertools work that way, it's the entire purpose of the module.
I think for very small ranges, range is actually less wasteful than xrange.
0

I coded this today. It does exactly what you want and more. It's extendable as well

def lastCase (lst):
    for i in range(0, len(lst)):
        if ( lst[i] != '_' ):
            return False
    return True


l = [''] * 4 #change size here if needed. I used 4
l[0] = '0'
index = 0

while ( not lastCase(l) ):

    if ( ord(l[index]) > ord('_') ):
        l[index] = '0'
        index += 1
        while( l[index] == '_' ):
            l[index] = '0'
            index += 1
        if (l[index] == ''):
            l[index] = '0'

    #print or process generated string
    print(''.join(l))

    l[index] = chr(ord(l[index]) +1)

    if ( ord(l[index]) > ord('9') and ord(l[index]) < ord('A') ):
        l[index] = 'A'
    elif ( ord(l[index]) > ord('Z') and ord(l[index]) < ord('_')  ): 
        l[index] = '_'

    index = 0

print (''.join(l))

Comments

0

This seems like the simplest solution to me:

from string import digits, ascii_uppercase, ascii_lowercase

chars = digits + ascii_uppercase + ascii_lowercase
all_str = [''.join([a]) for a in chars] \
        + [''.join([a,b]) for a in chars for b in chars] \
        + [''.join([a,b,c]) for a in chars for b in chars for c in chars] \
        + [''.join([a,b,c,d]) for a in chars for b in chars for c in chars for d in chars]

print(all_str)
print("Number of strings:", len(all_str))

Example for strings with maximum 2 characters.

Of course, there may be a way to generalize to any max number of characters per string, but since you have a specific need for strings up to 4 characters, it's fine.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.