017. Unicode Strings
All text is Unicode by default in Python 3
017. Unicode Strings
RAM Manager β Displaying Process Names
Aryanβs RAM manager reads process names from /proc on Linux. Some processes are started by users whose login names contain non-ASCII characters β accented names, CJK characters, or status messages with emoji. When Aryan displays a summary like:
Top 5 processes by RAM:
chrome (π€ sΓ©b) 544 MB
python (π€ ζζ) 398 MBβ¦the character-count column is wrong. He measured len(name.encode("utf-8")) instead of len(name), so "sΓ©b" reported 4 bytes instead of 3 characters, and "ζζ" reported 6 bytes instead of 2 characters. The columns donβt line up.
The lesson: In Python 3, strings are Unicode.
len()counts characters, not bytes. To get byte counts, use.encode("utf-8"). To get character counts, uselen()directly.
π‘ Fun fact: Python 2 had two separate string types β str (raw bytes) and unicode (text). Python 3βs biggest breaking change in 2008 was merging them into a single str type that is always Unicode. This eliminated an entire category of UnicodeDecodeError bugs that plagued Python 2 codebases, especially in web applications handling international text.
β οΈ Watch out: len("π".encode("utf-8")) returns 4 (bytes) but len("π") returns 1 (character). Using byte length for column alignment or display width calculations will mis-align any output containing non-ASCII characters.
π€ Think about it: Even len() counting characters is not always equal to the display width β some Unicode characters (like many CJK characters) are βfull widthβ and occupy two terminal columns. What would you need beyond len() to perfectly align a table with mixed ASCII and CJK text?
Learning objectives
- Understand that Python 3 strings are Unicode by default
- Use len() to count characters (not bytes)
- Use ord() and chr() for character/code point conversion
Key concepts
- unicode
- str
- ord()
- chr()
- encoding
Try it
Concept detail
Python 3 strings are always Unicode (UTF-32 internally). len(βcafΓ©β) == 4, not 5. Every character has a code point (integer). ord(βAβ) == 65, ord(βπβ) == 128075. chr(65) == βAβ (reverse of ord). Unicode escapes: β\u00e9β == βΓ©β, β\U0001F44Bβ == βπβ. To get bytes, encode: βhelloβ.encode(βutf-8β). To get back: bβhelloβ.decode(βutf-8β). Python 2 had str (bytes) and unicode (text) β Python 3 merged them as str (always Unicode).
Solution
def greeting_length(text):
return len(text)
def contains_emoji(text):
for char in text:
if ord(char) > 65535:
return True
return FalseTests
def test_ascii_length():
assert greeting_length("Hello") == 5
def test_emoji_length():
# π is 1 character (but 4 bytes in UTF-8)
assert greeting_length("Hi π") == 4
def test_cjk_length():
# Each CJK character is 1 char
assert greeting_length("δ½ ε₯½") == 2
def test_no_emoji():
assert contains_emoji("Hello") == False
def test_has_emoji():
assert contains_emoji("Hi π") == True