Character codes and conversion

ord and chr, ASCII, comparing strings, and a Caesar cipher.

F3.2Strings, lists and recordsGCSE15 min

Covers OCR J277 2.2.2 Data types · AQA 8525 3.2.8 String handling operations in a programming language · Edexcel 1CP2 6.3.3 String manipulation

Do this lesson in the simulator

A computer stores only numbers. Every character you type, from A to ? to a space, is stored as a number called its character code. This lesson shows the codes, how to convert between characters and codes, and a secret message that works because letters are numbers.

ord and chr

ord gives the code for a character. chr gives the character for a code:

# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()

print(ord("A"), ord("B"), ord("Z"))
print(ord("a"), ord("z"))
print(ord("0"), ord(" "))
print(chr(66), chr(111), chr(116))

Run this in the simulator

A is 65, B is 66, and the alphabet counts up from there, so Z is 90. Lower-case letters start at 97. The digit 0 is 48, and even a space has a code, 32. These are the codes of ASCII, the character set that the first 128 codes of Unicode still follow.

Character codes on a number line: space at 32, digits from 48, capital letters from 65, lower-case letters from 97space0 to 9A to Za to z324857659097122+32: A (65) to a (97)smaller codes sort first
The codes come in blocks, in order: the space, then the digits, then the capitals, then the lower-case letters. Each lower-case letter sits exactly 32 codes after its capital.

Letters are in order

Because the codes count up, you can do arithmetic on letters:

# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()

letter = "C"
print("the next letter is", chr(ord(letter) + 1))
print("C is letter number", ord(letter) - ord("A") + 1, "of the alphabet")
print("upper to lower:", chr(ord(letter) + 32))

Run this in the simulator

In ASCII, lower case is always 32 codes after upper case, so adding 32 does to a capital letter what lower() does. (lower() also knows the letters beyond ASCII, such as É.)

Comparing strings

Comparing strings with < and > compares their codes, one character at a time:

# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()

print("apple" < "banana")
print("Zebra" < "apple")
print("robot" == "Robot")

Run this in the simulator

"Zebra" < "apple" is True, because Z (90) comes before a (97). Capital letters sort before lower case. Convert both strings with lower() before comparing if case should not matter.

Two comparisons of strings, character by character, each decided at the first character"Zebra" < "apple"Z90e101b98r114a97a97p112p112l108e10190 is less than 97,so the answer is True"robot" == "Robot"r114o111b98o111t116R82o111b98o111t116114 is not 82,so the answer is Falsegrey characters are never looked at
Python compares the codes of the first characters, and only moves on to the next pair if they are the same. Here the first pair already differs, so the rest is never looked at.

Turning digits into numbers, and back

int() and str() from lesson F1.8 convert whole strings. Under the hood, a digit is a character code too:

# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()

reading = "42"
print(int(reading) + 1)                  # the whole string as a number
print(ord(reading[0]) - ord("0"))        # the first digit, worked out from its code
print(str(99) + " red balloons")
print(reading.isdigit(), "4two".isdigit())

Run this in the simulator

isdigit() asks whether every character is a digit, which lets a program check an answer before it calls int() and crashes.

A secret message

A Caesar cipher moves every letter a fixed number of places along the alphabet. With character codes it is a loop:

# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()

message = "ROBOT"
shift = 3
secret = ""
for letter in message:
    code = ord(letter) - ord("A")          # 0 to 25
    code = (code + shift) % 26             # move along, wrapping Z round to A
    secret = secret + chr(code + ord("A"))
print(secret)

Run this in the simulator

% 26 wraps the end of the alphabet round to the start, so X shifted by 3 is A. Change shift to -3 and put the secret back in message: it decodes.

The alphabet numbered 0 to 25, with R moved 3 places to U, and X moved 3 places, past Z, round to AA0B1C2D3E4F5G6H7I8J9K10L11M12N13O14P15Q16R17S18T19U20V21W22X23Y24Z25R: (17 + 3) % 26 = 20, which is UX: (23 + 3) % 26 = 0, so X becomes A
Numbering the letters from 0 makes the cipher a sum. R moves 3 places to U. X would move to 26, which is past Z, and % 26 wraps it round to 0, which is A.

Task: shift the letters

Ask Message? and print the message with every letter moved one place along the alphabet. The task answers HAL (the computer in a famous film), and the answer is the name of a famous computer company. The message will be capital letters only. Work it out with ord and chr.

# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()

message = input("Message? ")
secret = ""

Challenges

  1. Print the whole alphabet with one loop and chr, without typing any letters.
  2. Make the cipher keep spaces as they are, so that HELLO ROBOT works.
  3. Play a note for each letter of a name: tone(ord(letter) * 5, 0.2). Which name sounds best?

Past paper questions on this

Real exam questions that test this, each worked through with a program you can run.