92

Is there an way to range over characters? something like this.

for c in xrange( 'a', 'z' ):
    print c

I hope you guys can help.

glglgl
  • 85,390
  • 12
  • 140
  • 213
huan
  • 1,041
  • 1
  • 8
  • 6

16 Answers16

122

This is a great use for a custom generator:

Python 2:

def char_range(c1, c2):
    """Generates the characters from `c1` to `c2`, inclusive."""
    for c in xrange(ord(c1), ord(c2)+1):
        yield chr(c)

then:

for c in char_range('a', 'z'):
    print c

Python 3:

def char_range(c1, c2):
    """Generates the characters from `c1` to `c2`, inclusive."""
    for c in range(ord(c1), ord(c2)+1):
        yield chr(c)

then:

for c in char_range('a', 'z'):
    print(c)
stacker
  • 25
  • 5
Ned Batchelder
  • 345,440
  • 70
  • 544
  • 649
  • 17
    Beautiful! For anyone who's going to copy this, keep in mind that range(1,3) iterates values 1 and 2 (and not three), but char_range('a','c') will iterate 'a', 'b' and 'c'! – vicmortelmans Jan 16 '18 at 21:22
  • You could add the optional step arg too: `def char_range(c1, c2, step=1)` ... `ord(c1), ord(c2)+1, step` – wjandrea Nov 11 '18 at 19:01
  • @wjandrea, doesn't quite work for negative steps, eg `char_range('g','a',-1)` gives `['g', 'f', 'e', 'd', 'c']` – alancalvitti Feb 13 '19 at 15:58
  • 1
    @alan Nice catch! Looks like that's the fault of the +1 on `ord(c2)`. So replace `ord(c2)+1` with `ord(c2) + (1 if step > 0 else -1)`. Though for clarity's sake you might want to factor that out of the `range()` call. – wjandrea Feb 13 '19 at 17:13
  • 2
    The problem with doing that, is that to generate a-z, you will need to know which character comes after z. Not especially comfortable. Probably better to avoid the name «range» (use closedrange or inclusiverange instead ?) – Camion Apr 07 '19 at 07:57
  • @Razzle Shazi : This is not something defined by python but in his code. I just told, that since range generally excludes the last position, using the word range in the function name might be a misleading choice. – Camion May 16 '19 at 03:28
104
import string
for char in string.ascii_lowercase:
    print char

See string constants for the other possibilities, including uppercase, numbers, locale-dependent characters, all of which you can join together like string.ascii_uppercase + string.ascii_lowercase if you want all of the characters in multiple sets.

agf
  • 160,324
  • 40
  • 275
  • 231
26

You have to convert the characters to numbers and back again.

for c in xrange(ord('a'), ord('z')+1):
    print chr(c) # resp. print unicode(c)

For the sake of beauty and readability, you can wrap this in a generator:

def character_range(a, b, inclusive=False):
    back = chr
    if isinstance(a,unicode) or isinstance(b,unicode):
        back = unicode
    for c in xrange(ord(a), ord(b) + int(bool(inclusive)))
        yield back(c)

for c in character_range('a', 'z', inclusive=True):
    print(chr(c))

This generator can be called with inclusive=False (default) to imitate Python's usual bhehaviour to exclude the end element, or with inclusive=True (default) to include it. So with the default inclusive=False, 'a', 'z' would just span the range from a to y, excluding z.

If any of a, b are unicode, it returns the result in unicode, otherwise it uses chr.

It currently (probably) only works in Py2.

glglgl
  • 85,390
  • 12
  • 140
  • 213
12

There are other good answers here (personally I'd probably use string.lowercase), but for the sake of completeness, you could use map() and chr() on the lower case ascii values:

for c in map(chr, xrange(97, 123)):
   print c
GreenMatt
  • 17,688
  • 7
  • 50
  • 76
11

If you have a short fixed list of characters, just use Python's treatment of strings as lists.

for x in 'abcd':
    print x

or

[x for x in 'abcd']
Dannid
  • 1,311
  • 16
  • 17
9

I like an approach which looks like this:

base64chars = list(chars('AZ', 'az', '09', '++', '//'))

It certainly can be implemented with a lot of more comfort, but it is quick and easy and very readable.

Python 3

Generator version:

def chars(*args):
    for a in args:
        for i in range(ord(a[0]), ord(a[1])+1):
            yield chr(i)

Or, if you like list comprehensions:

def chars(*args):
    return [chr(i) for a in args for i in range(ord(a[0]), ord(a[1])+1)]

The first yields:

print(chars('ĀĈ'))
<generator object chars at 0x7efcb4e72308>
print(list(chars('ĀĈ')))
['Ā', 'ā', 'Ă', 'ă', 'Ą', 'ą', 'Ć', 'ć', 'Ĉ']

while the second yields:

print(chars('ĀĈ'))
['Ā', 'ā', 'Ă', 'ă', 'Ą', 'ą', 'Ć', 'ć', 'Ĉ']

It is really convenient:

base64chars = list(chars('AZ', 'az', '09', '++', '//'))
for a in base64chars:
   print(repr(a),end='')
print('')
for a in base64chars:
   print(repr(a),end=' ')

outputs

'A''B''C''D''E''F''G''H''I''J''K''L''M''N''O''P''Q''R''S''T''U''V''W''X''Y''Z''a''b''c''d''e''f''g''h''i''j''k''l''m''n''o''p''q''r''s''t''u''v''w''x''y''z''0''1''2''3''4''5''6''7''8''9''+''/'
'A' 'B' 'C' 'D' 'E' 'F' 'G' 'H' 'I' 'J' 'K' 'L' 'M' 'N' 'O' 'P' 'Q' 'R' 'S' 'T' 'U' 'V' 'W' 'X' 'Y' 'Z' 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 'u' 'v' 'w' 'x' 'y' 'z' '0' '1' '2' '3' '4' '5' '6' '7' '8' '9' '+' '/' 

Why the list()? Without base64chars might become a generator (depending on the implementation you chose) and thus can only be used in the very first loop.

Python 2

Similar can be archived with Python 2. But it is far more complex if you want to support Unicode, too. To encourage you to stop using Python 2 in favor of Python 3 I do not bother to provide a Python 2 solution here ;)

Try to avoid Python 2 today for new projects. Also try to port old projects to Python 3 first before extending them - in the long run it will be worth the effort!

Proper handling of Unicode in Python 2 is extremely complex, and it is nearly impossible to add Unicode support to Python 2 projects if this support was not build in from the beginning.

Hints how to backport this to Python 2:

  • Use xrange instead of range
  • Create a 2nd function (unicodes?) for handling of Unicode:
    • Use unichr instead of chr to return unicode instead of str
    • Never forget to feed unicode strings as args to make ord and array subscript work properly
Tino
  • 8,551
  • 3
  • 52
  • 57
5
for character in map(   chr, xrange( ord('a'), ord('c')+1 )   ):
   print character

prints:

a
b
c
securecurve
  • 5,360
  • 4
  • 41
  • 75
5
# generating 'a to z' small_chars.
small_chars = [chr(item) for item in range(ord('a'), ord('z')+1)]
# generating 'A to Z' upper chars.
upper_chars = [chr(item).upper() for item in range(ord('a'), ord('z')+1)]
4

For Uppercase Letters:

for i in range(ord('A'), ord('Z')+1):
    print(chr(i))

For Lowercase letters:

for i in range(ord('a'), ord('z')+1):
    print(chr(i))
3

Inspired from the top post above, I came up with this :

map(chr,range(ord('a'),ord('z')+1))                     
zondo
  • 19,040
  • 7
  • 42
  • 82
Saravanan Setty
  • 188
  • 1
  • 17
1

Using @ned-batchelder's answer here, I'm amending it a bit for python3

def char_range(c1, c2):
    """Generates the characters from `c1` to `c2`, inclusive."""
    """Using range instead of xrange as xrange is deprecated in Python3""" 
    for c in range(ord(c1), ord(c2)+1):
        yield chr(c)

Then same thing as in Ned's answer:

for c in char_range('a', 'z'):
    print c

Thanks Ned!

Andy K
  • 4,745
  • 10
  • 46
  • 78
1

i had the same need and i used this :

chars = string.ascii_lowercase
range = list(chars)[chars.find('a'):chars.find('k')+1]

Hope this will Help Someone

  • This is a good idea and was elegant for my application, something like: `for c in string.ascii_uppercase` (note: the OP has a range end "bug"). – Curt Dec 31 '20 at 08:42
0

Use "for count in range" and chr&ord:

print [chr(ord('a')+i) for i in range(ord('z')-ord('a'))]
erjemin
  • 3
  • 2
0

Use list comprehension:

for c in [chr(x) for x in range(ord('a'), ord('z'))]:
    print c
Bugalugs Nash
  • 482
  • 1
  • 5
  • 20
0

Another option (operates like range - add 1 to stop if you want stop to be inclusive)

>>> import string
>>> def crange(arg, *args):
...     """character range, crange(stop) or crange(start, stop[, step])"""
...     if len(args):
...         start = string.ascii_letters.index(arg)
...         stop = string.ascii_letters.index(args[0])
...     else:
...         start = string.ascii_letters.index('a')
...         stop = string.ascii_letters.index(arg)
...     step = 1 if len(args) < 2 else args[1]
...     for index in range(start, stop, step):
...         yield string.ascii_letters[index]
...
>>> [_ for _ in crange('d')]
['a', 'b', 'c']
>>>
>>> [_ for _ in crange('d', 'g')]
['d', 'e', 'f']
>>>
>>> [_ for _ in crange('d', 'v', 3)]
['d', 'g', 'j', 'm', 'p', 's']
>>>
>>> [_ for _ in crange('A', 'G')]
['A', 'B', 'C', 'D', 'E', 'F']
Wyrmwood
  • 2,973
  • 25
  • 31
0

Depending on how complex the range of characters is, a regular expression may be convenient:

import re
import string

re.findall("[a-f]", string.printable)
# --> ['a', 'b', 'c', 'd', 'e', 'f']

re.findall("[n-qN-Q]", string.printable)
# --> ['n', 'o', 'p', 'q', 'N', 'O', 'P', 'Q']

This works around the pesky issue of accidentally including the punctuation characters in between numbers, uppercase and lowercase letters in the ASCII table.

gens
  • 912
  • 11
  • 22