Python makes letter-to-number conversion exceptionally clean thanks to built-in functions like ord() and chr(). Whether you need a quick one-liner for a script, a robust encoder class for a project, or batch processing for data analysis, Python has the tools. This guide covers every approach from basic to advanced, with production-ready code examples.
The Basics: ord() and chr()
Python's ord() function returns the Unicode code point (identical to ASCII for English letters) of a single character. Since uppercase A has code point 65 and alphabet position 1, the conversion formula is:
position = ord(letter.upper()) - 64
# Examples
ord('A') - 64 # → 1
ord('Z') - 64 # → 26
ord('m') - 64 # → 45 (wrong! lowercase m is 109)
ord('M') - 64 # → 13 (correct)
# Always use .upper() for consistency
ord('m'.upper()) - 64 # → 13The reverse operation uses chr():
letter = chr(position + 64)
chr(1 + 64) # → 'A'
chr(26 + 64) # → 'Z'
chr(13 + 64) # → 'M'
# For lowercase
chr(position + 96)
chr(1 + 96) # → 'a'Converting a Full String
List Comprehension (One-Liner)
text = "HELLO WORLD"
numbers = [ord(c.upper()) - 64 for c in text if c.isalpha()]
# → [8, 5, 12, 12, 15, 23, 15, 18, 12, 4]
# With separator
encoded = "-".join(str(n) for n in numbers)
# → "8-5-12-12-15-23-15-18-12-4"Preserving Word Boundaries
def encode_a1z26(text, sep="-", word_sep=" / "):
"""Encode text to A1Z26 with word boundaries."""
words = text.split()
encoded_words = []
for word in words:
nums = [str(ord(c.upper()) - 64)
for c in word if c.isalpha()]
encoded_words.append(sep.join(nums))
return word_sep.join(encoded_words)
encode_a1z26("Hello World")
# → "8-5-12-12-15 / 23-15-18-12-4"
encode_a1z26("Python", sep=",")
# → "16,25,20,8,15,14"Decoding Numbers Back to Letters
def decode_a1z26(encoded, sep="-", word_sep=" / "):
"""Decode A1Z26 number string back to text."""
words = encoded.split(word_sep)
decoded_words = []
for word in words:
letters = [chr(int(n.strip()) + 64)
for n in word.split(sep)
if n.strip().isdigit() and 1 <= int(n.strip()) <= 26]
decoded_words.append("".join(letters))
return " ".join(decoded_words)
decode_a1z26("8-5-12-12-15 / 23-15-18-12-4")
# → "HELLO WORLD"
decode_a1z26("16,25,20,8,15,14", sep=",")
# → "PYTHON"Using the string Module
An alternative approach uses the string module, which provides ascii_uppercase and ascii_lowercase constants:
import string
# Create a mapping dictionary
letter_to_num = {letter: idx + 1
for idx, letter in enumerate(string.ascii_uppercase)}
# {'A': 1, 'B': 2, 'C': 3, ..., 'Z': 26}
# Convert using the dictionary
text = "CIPHER"
numbers = [letter_to_num[c] for c in text.upper() if c in letter_to_num]
# → [3, 9, 16, 8, 5, 18]
# Reverse mapping
num_to_letter = {v: k for k, v in letter_to_num.items()}
decoded = "".join(num_to_letter[n] for n in numbers)
# → "CIPHER"Building a Complete A1Z26 Cipher Class
class A1Z26Cipher:
"""Complete A1Z26 encoder/decoder with configurable options."""
def __init__(self, sep="-", word_sep=" / ",
case="upper", zero_indexed=False):
self.sep = sep
self.word_sep = word_sep
self.case = case
self.offset = 0 if zero_indexed else 1
def encode_char(self, char):
"""Convert a single letter to its position number."""
if not char.isalpha():
return None
return ord(char.upper()) - 65 + self.offset
def decode_num(self, num):
"""Convert a position number to a letter."""
code = num + 65 - self.offset
if 65 <= code <= 90:
letter = chr(code)
return letter.lower() if self.case == "lower" else letter
return None
def encode(self, text):
"""Encode a full string to A1Z26."""
words = text.split()
encoded_words = []
for word in words:
nums = [str(self.encode_char(c))
for c in word if c.isalpha()]
encoded_words.append(self.sep.join(nums))
return self.word_sep.join(encoded_words)
def decode(self, encoded):
"""Decode an A1Z26 string back to text."""
words = encoded.split(self.word_sep)
decoded_words = []
for word in words:
parts = word.split(self.sep)
letters = []
for p in parts:
p = p.strip()
if p.isdigit():
letter = self.decode_num(int(p))
if letter:
letters.append(letter)
decoded_words.append("".join(letters))
return " ".join(decoded_words)
def word_value(self, text):
"""Calculate the sum of all letter positions."""
return sum(self.encode_char(c) for c in text
if c.isalpha())
# Usage
cipher = A1Z26Cipher()
cipher.encode("Hello World")
# → "8-5-12-12-15 / 23-15-18-12-4"
cipher.decode("8-5-12-12-15 / 23-15-18-12-4")
# → "HELLO WORLD"
cipher.word_value("PYTHON")
# → 98 (16+25+20+8+15+14)Batch Processing with Files
def process_file(input_path, output_path, mode="encode"):
"""Batch encode or decode a text file."""
cipher = A1Z26Cipher()
with open(input_path, "r") as f_in, \
open(output_path, "w") as f_out:
for line in f_in:
if mode == "encode":
f_out.write(cipher.encode(line.strip()) + "\n")
else:
f_out.write(cipher.decode(line.strip()) + "\n")
# Encode a file
process_file("message.txt", "encoded.txt", mode="encode")
# Decode a file
process_file("encoded.txt", "decoded.txt", mode="decode")Performance Considerations
For most use cases, the simple ord(c) - 64 approach is fastest because it performs a single integer subtraction per character. The dictionary lookup method has slightly more overhead due to hash table lookups. For processing millions of characters:
# Fastest: direct arithmetic
[ord(c) - 64 for c in text.upper() if c.isalpha()]
# Slightly slower: dictionary lookup
letter_map = {chr(i + 64): i for i in range(1, 27)}
[letter_map[c] for c in text.upper() if c in letter_map]
# For very large texts, use bytes for speed
text_bytes = text.upper().encode('ascii', errors='ignore')
[b - 64 for b in text_bytes if 65 <= b <= 90]No coding required: Convert text instantly with our free online A1Z26 Converter — runs entirely in your browser.
Frequently Asked Questions
What is the difference between ord() and the A1Z26 position?
ord() returns the Unicode/ASCII code point. For uppercase English letters, this ranges from 65 (A) to 90 (Z). The A1Z26 position is obtained by subtracting 64: ord('A') - 64 = 1. For lowercase letters (97-122), subtract 96 instead, or convert to uppercase first with .upper().
Does Python 3 handle Unicode differently for ord()?
In Python 3, strings are Unicode by default. ord() returns the Unicode code point, which is identical to ASCII for characters 0-127 (including all English letters). For A1Z26 purposes, there is no difference between Python 2 and Python 3 when working with English letters.
How do I handle accented characters like e-acute?
Accented characters (e.g., e with acute) have Unicode code points above 127 and do not have standard A1Z26 positions. You can normalize them using unicodedata.normalize('NFKD', text) to strip diacritics before converting, which turns e-acute into a plain "e."
Can I use numpy for vectorized conversion?
Yes. Convert the string to a numpy array of character codes, then subtract 64: import numpy as np; codes = np.frombuffer(text.upper().encode(), dtype=np.uint8); positions = codes[(codes >= 65) & (codes <= 90)] - 64. This is extremely fast for large texts.