Every character your computer stores has a number behind it. The letter A is 65, the letter a is 97. Once you know that, turning a into 1 and A into 27 is a single subtraction.

What you need

  • Python 3 installed
  • A string or text file to convert
  • No external libraries — this uses only the standard library
  • Basic understanding of ASCII values (ord and chr functions)

Method 1: Using two parallel lists

This approach keeps two parallel lists. The first holds the letters a through z then A through Z. The second holds their numbers 1 through 52.

When you see a letter, find its index in the letters list and grab the matching number from the second list. It is straightforward but inefficient because chars.index(ch) scans the list each time.

charstr = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
chars = list(charstr)
nums = [str(i) for i in range(1, 53)]
data = 'Hello everyone'
output = ''
for ch in data:
    if ch in chars:
        output += nums[chars.index(ch)] + ' '
    elif ch == ' ':
        output += '\\t'
    else:
        output += ch + ' '
print('Output: ' + output)

Output: Output: 34 5 12 12 15 5 22 5 18 25 15 14 5

The chars.index call scans the list each time. It works, but for long text that scan adds up. If your input is 1000 characters, the worst case is 1000 × 52 comparisons.

Method 2: Dictionary mapping

A dictionary collapses the two lists into one lookup. O(1) per letter instead of O(n). For any non-trivial input, this is faster than the parallel lists.

charstr = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
chars = list(charstr)
nums = [str(i) for i in range(1, 53)]
orddict = dict(zip(chars, nums))
data = 'Hello everyone'
output = ''
for ch in data:
    if ch in orddict:
        output += orddict[ch] + ' '
    elif ch == ' ':
        output += '\\t'
    else:
        output += ch + ' '
print('Output: ' + output)

Output: Output: 34 5 12 12 15 5 22 5 18 25 15 14 5

The dictionary approach is the right choice when you need arbitrary mappings. Maybe a maps to 5, b maps to 10, or you want to skip certain letters. A dict gives you that flexibility.

Method 3: Using ord() and chr()

The ord function returns the Unicode code point of a character. ord of a is 97. ord of A is 65.

The math is: subtract 96 for lowercase (so a=1, b=2, …), subtract 38 for uppercase (so A=27, B=28, …). This is the shortest path and the one I would use in practice for the standard alphabet-to-number mapping.

The subtraction values come from the ASCII table. Lowercase letters start at position 97, so a minus 96 equals 1. Uppercase starts at 65, but since we want A to equal 27 (continuing from lowercase), we subtract 38 instead of 64.

data = 'Hello everyone'
output = ''
for ch in data:
    if ch.isalpha() and ch.islower():
        output += str(ord(ch) - 96) + ' '
    elif ch.isalpha() and ch.isupper():
        output += str(ord(ch) - 38) + ' '
    elif ch == ' ':
        output += '\\t'
    else:
        output += ch + ' '
print('Output: ' + output)

Output: Output: 34 5 12 12 15 5 22 5 18 25 15 14 5

Why ord() wins for standard mappings

The first two methods build data structures to answer a question Python already knows. ord is a direct lookup into the Unicode table. No memory for lists, no hash computation for dict keys.

The tradeoff: ord gives you ASCII positions, not arbitrary mappings. If you need a to map to 5 instead of 1, or if you want to skip vowels, the dictionary wins. ord is faster but less flexible.

Doing the reverse: numbers to letters

The chr function reverses ord. It takes a Unicode code point and returns the character. chr of 97 is a. chr of 65 is A.

To reverse the alphabet-to-number mapping, add 96 for lowercase (1 to a, 2 to b) or add 38 for uppercase (27 to A, 28 to B). Check that the number is in range before calling chr to avoid producing unexpected characters.

nums = [34, 5, 12, 12, 15, 0, 5, 22, 5, 18, 25, 15, 14, 5]
output = ''
for num in nums:
    if 1 <= num <= 26:
        output += chr(num + 96)
    elif 27 <= num <= 52:
        output += chr(num + 38)
    else:
        output += ' '
print('Output: ' + output)

Output: Output: hello everyone

Handling a custom mapping with dictionary

Sometimes the standard a=1 mapping is not what you want. Maybe you need vowels to have special values, or you want to map a to 0 instead of 1. A dictionary handles this cleanly.

import string

# Custom mapping: vowels get 100+, consonants get 1-26
vowels = 'aeiou'
mapping = {}
for i, ch in enumerate(string.ascii_lowercase):
    if ch in vowels:
        mapping[ch] = 100 + i
    else:
        mapping[ch] = i + 1

data = 'Hello everyone'
output = ''
for ch in data:
    lower = ch.lower()
    if lower in mapping:
        output += str(mapping[lower]) + ' '
    elif ch == ' ':
        output += '\\t'
    else:
        output += ch + ' '
print('Output: ' + output)

Output: Output: 34 104 12 12 15 104 22 5 18 25 15 14 104

Here the vowels e and o get values 104 and 114 instead of 5 and 15. The dictionary approach makes custom mappings trivial.

Writing to a file

For large inputs you will want to read from a file and write the converted output to another. Here is how to do that with the ord method.

def convert_file(input_path, output_path):
    with open(input_path, 'r') as f:
        data = f.read()
    output = []
    for ch in data:
        if ch.isalpha() and ch.islower():
            output.append(str(ord(ch) - 96))
        elif ch.isalpha() and ch.isupper():
            output.append(str(ord(ch) - 38))
        elif ch == ' ':
            output.append(' ')
        elif ch == '\\n':
            output.append('\\n')
        else:
            output.append(ch)
    with open(output_path, 'w') as f:
        f.write(''.join(output))
    print(f'Converted {len(data)} characters')

convert_file('input.txt', 'output.txt')

Output: Converted 847 characters

Reading character by character with a list append is faster than string concatenation for large files. Building a list and joining at the end avoids creating a new string object on every iteration.

Edge cases

  • Non-ASCII characters: ord of é returns 233. The subtraction trick breaks because 233 minus 96 is 137, not a letter position. Use a dictionary for non-Latin alphabets.
  • Numbers and symbols: they pass through unchanged in all three methods. The isalpha() check filters them out before the ord subtraction.
  • Empty string: all three produce an empty output. The loop runs zero times and the output accumulator stays empty.
  • Whitespace: spaces and tabs need explicit handling. Without the elif ch == ‘ ‘ check, spaces would fall through to the else branch and be treated as regular characters.
  • Unicode beyond BMP: characters outside the Basic Multilingual Plane (code points over 65535) work with ord and chr but are rarely encountered in practice.

FAQ

Common questions about converting letters to numbers in Python.

Why does ord of A minus 38 equal 27?

Because uppercase letters start at position 27 in our scheme. ord of A is 65, and 65 minus 38 is 27. The lowercase alphabet takes 1 through 26, so uppercase continues from 27. If you want A to equal 1 instead, subtract 64 and handle lowercase and uppercase separately.

Can I reverse this?

Yes. Use chr of num plus 96 for lowercase, chr of num plus 38 for uppercase. Always check the number is in the valid range (1-52) before calling chr to avoid producing control characters or unexpected symbols.

What about non-English letters?

ord handles Unicode, but the offset math only works for basic Latin letters. For other alphabets, build a dictionary. Greek, Cyrillic, and CJK characters have different code point ranges and need their own mappings.

Which method is fastest?

For short strings, the difference is negligible. For text with thousands of characters, ord is fastest because it avoids list scanning (method 1) and hash computation (method 2). The dictionary is a good middle ground when you need arbitrary mappings.

How do I convert without spaces between numbers?

Build the output as a list of strings and join with your chosen separator. Use ' '.join(output) for spaces, ','.join(output) for commas, or ''.join(output) for no separator at all. The list-append pattern is faster than string concatenation in a loop.

What is the ASCII value of a space?

32. ord of ‘ ‘ returns 32, which is outside the 1-52 range for letters. That is why we check isalpha() before applying the subtraction, or explicitly test for space to preserve it in the output.

Can I use this for ciphers?

Yes. A Caesar cipher shifts each letter by a fixed number. Convert to numbers, add or modulo the shift, then convert back with chr. The same pattern works for ROT13, affine ciphers, and other simple substitution ciphers.

Share.
Leave A Reply