02_Characters — a character is a number by agreement¶
Level: 101 → 201 · for anyone starting from zero
A byte is a number. Text is characters. The only bridge between them is a table, and this chapter is the history of that table getting bigger: 128 entries, then 256 with everybody's own second half, then a million with one numbering for all of them.
| # | Lesson | The question it answers | Status |
|---|---|---|---|
| 1 | A character is a number | Who decided 65 means A, and why are the digits and the two alphabets placed where they are? |
written |
| 2 | Rotation is not encryption | Why is ROT13 thirteen, why is it not encryption, and what breaks when you rotate past ASCII? | written, 2026-09-07 |
| 3 | Control characters | What are the first 32, which three still matter every day, and where does a C string end? | written |
| 4 | The NUL byte | Why does one zero byte end a C string, hide a grep match, and make the only separator a filename cannot forge? | written, 2026-09-06 |
| 5 | Code pages | What did everybody do with the unclaimed 128, and why do the tables agree just enough to hide the bug? | written, 2026-09-05 |
| 6 | Unicode code points | What is U+00E9, and why is it a number rather than a byte? |
written, 2026-09-05 |
| 7 | Writing a code point | You have the number — how do you write it down, and why does every language spell it differently? | written, 2026-09-06 |
| 8 | The table has a version | Whose copy of Unicode is your program actually reading, and what may you write down? | written, 2026-09-06 |
| 9 | Noncharacters and the private use areas | Which code points are reserved forever, and why "reserved" is not "invalid"? | written, 2026-09-08 |
| 10 | Preparing a string | Are these two strings the same name, and who decides which differences count? | written, 2026-09-06 |
| 11 | PRECIS: stringprep, after stringprep | What replaced stringprep, why a username and a password get different profiles, and what unpinning the table cost? | written, 2026-09-08 |
| 12 | A code point is not a character | Why does len() still not count what a person calls a character? |
written, 2026-09-07 |
| 13 | Case is not a per-character operation | Why does .upper() change a string's length, depend on neighbours, and refuse to know your language? |
written, 2026-09-08 |
| 14 | Where a line may break | Japanese and Thai have no spaces — so where may a line break, and why is textwrap wrong for both? |
written, 2026-09-08 |
| 15 | Confusables and scripts | Two strings, one picture, different code points — why does no normalization form merge them? | written, 2026-09-06 |
| 16 | Logical and visual order | Why is the order you store not the order you see, and why is a screenshot not evidence? | stub |
| 17 | Unicode in identifiers | Why does file = 2 define file, and why does Rust refuse to do that? |
written, 2026-09-06 |
| 18 | "Supports Unicode" is a level, not a yes | Your regex engine says it handles Unicode — which of UTS #18's fifteen requirements does it actually meet? | written, 2026-09-08 |
| 19 | rune is an int32 |
Go and .NET both call one code point a rune — why will Go's hold -1, and why does its loop count a cut-off euro sign twice? |
written, 2026-09-10 |
The through-line¶
The number is settled before the bytes are. By the end of this chapter every character you can name has one agreed number, its code point, and nothing has been said yet about how that number is written into a file. Keeping code point and byte apart is most of the subject, and chapter 3 is where they finally meet.