ICS 582Lecture 02Part 05
Corpora, Unicode and UTF-8
What a corpus is and why it must be documented, how Unicode names every character with a code point, and how UTF-8 packs code points into bytes that any model can consume.
- Concepts
- 4
- Slides
- 29-37
- Reading
- 24 min
Why this part matters
Your research project processes Arabic, and Arabic is where text representation stops being a detail. One letter has four visual shapes, one sound has several hamza spellings, legacy keyboards emit presentation forms, and every letter costs two bytes. A tokenizer that does not know this splits words in places no linguist would recognize.
This part builds the foundation under every tokenizer count in the rest of the course. First it asks what a corpus actually is and what you owe a reader who will trust a model trained on it. Then it descends from characters to numbers (code points), from numbers to bytes (UTF-8), and finally explains why bytes are the floor that byte-level BPE in part 7 stands on. Every corpus you use or build for the project must carry a data statement to be publishable, and every byte count you will ever see in a tokenizer log traces back to the four-row mapping table taught here.
By the end you can
- Name the variation dimensions of a corpus and list the fields of a data statement or datasheet.
- Distinguish code point, glyph and encoding, and explain why saying Unicode is an encoding is wrong.
- Apply NFC, NFD and NFKC to precomposed and decomposed Latin and Arabic examples and say which tokenization bug each prevents.
- Encode any code point into UTF-8 bytes by hand using the four-row table, and decode bytes back.
- Explain why bytes remove the unknown-character problem and what they cost.
Two datasets both labelled "Arabic corpus" can be almost unrelated. One is Modern Standard Arabic newswire from 2005, written by trained journalists in a formal register. The other is Gulf-dialect tweets from 2024, full of Arabizi, emoji and code switching into English. A model trained on the first will stumble on the second, and a vocabulary learned from either will miss most of the other. Same language label, different domain, genre, time, demographics and variety.
A Corpus is a structured collection of texts, often annotated, drawn from sources such as books, news, web crawls, social media and speech transcripts. The word "structured" matters: a corpus is a deliberate sample with metadata, not a pile of files. Jurafsky and Martin describe the dimensions along which such samples vary as language and variety (including code switching), genre, the demographics of the writer or speaker, and time. The table below adds domain, which the slides list separately, and gives an Arabic example for each.
| Dimension | Question it answers | Arabic example |
|---|---|---|
| Language and variety | Which language, dialect or register, and is there code switching? | Modern Standard Arabic newswire versus Najdi tweets, or Arabizi in Latin letters |
| Genre | What kind of text is it? | Quranic commentary, a parliamentary transcript, a product review |
| Time | When was it written or spoken? | Pre-2011 news versus post-2020 social media, where vocabulary shifted |
| Demographics | Who wrote or spoke it, by age, gender, region, education? | Teenage gamers in Riyadh versus retired teachers in Cairo |
| Domain | What is it about? | Medical records, legal contracts, football commentary |
Models inherit their corpus
A language model is a compressed description of its training corpus, so it inherits the corpus's biases and blind spots. Bender and Friedman argue that systems perform best for the populations represented in their training data, so bias and exclusion arise when that data does not match the people the system is deployed for, and the fix begins with knowing what the data contains. Without that knowledge, a result on a benchmark cannot be interpreted: a 95% accuracy on 2005 newswire says nothing about 2024 tweets. To interpret any result you need the metadata below.
Metadata a corpus must carry before its results can be interpreted
- Collection procedure
- How texts were sampled or crawled, and what was filtered out
- Time period
- When the texts were produced, not only when they were collected
- Languages and varieties
- Which languages, dialects and registers, and how much code switching
- Speaker or author demographics
- Age, gender, region, education and any other known attributes
- Licensing and privacy
- Who may use the data, for what, and whether consent was obtained
Data statements and datasheets
Two papers turned that metadata list into a standard document. Bender and Friedman (2018) proposed the data statement for NLP datasets, a short structured write-up with lettered sections from curation rationale through language variety, speaker and annotator demographics, speech situation and text characteristics. Gebru and colleagues (2021) proposed the datasheet for any machine learning dataset, borrowing the idea from electronics: a component datasheet lists operating ranges and test conditions, so a dataset should too. The slides merge the two into six fields.
The six datasheet fields, with the matching sections in the two papers
- Motivation and intended use
- Why the corpus was built, by whom, funded how, and what tasks it is meant for (Gebru: motivation and uses; Bender and Friedman: A, curation rationale)
- Composition
- What the instances are, how many, which languages, varieties and demographics they cover (Gebru: composition; Bender and Friedman: B to F)
- Collection process
- How and when the texts were gathered, by crawl, purchase or recording, and who did it (Gebru: collection process; Bender and Friedman: I, provenance)
- Preprocessing and annotation
- Cleaning, filtering, tokenization and labelling steps, and the annotators' guidelines and demographics (Gebru: preprocessing, cleaning, labeling; Bender and Friedman: D)
- Ethical considerations
- Consent, privacy, sensitive content and the populations who could be harmed by a model trained on it (Gebru: uses and maintenance)
- Distribution constraints
- License, access terms, versioning and who maintains the release (Gebru: distribution and maintenance)
The payoff is transparency, reproducibility and responsible use. Transparency because a reader can judge whether your corpus matches their population. Reproducibility because someone else can rebuild or extend the sample. Responsible use because a datasheet states what the data must not be used for, which is the only place such a limit can live once the files are downloaded.
Recall
Name four fields of a datasheet, and state the one-sentence reason a corpus needs one.
Quick check
Which datasheet field explains why the corpus was created?
Type the word café on two computers. On a Mac the é arrives as one unit, U+00E9. On a system with a decomposed input method it arrives as two, the plain letter U+0065 followed by a combining acute accent U+0301. On screen the two strings are pixel for pixel identical. In a program they are unequal, they have different lengths, and a tokenizer trained on one will split the other into pieces it never saw during training. Arabic has the same trap: a legacy keyboard emits the lam-alef ligature U+FEFB as one character, while a modern one emits U+0644 then U+0627.
To reason about this you need three separate words. Unicode assigns every character a Code point, an integer between 0 and 0x10FFFF written with a U+ prefix in hexadecimal, such as U+0061 for the letter a. That gives 1,114,112 possible positions, and the standard covers Latin, Cyrillic, Arabic, Han, emoji and over 170 other scripts within them. A Glyph is the rendered shape a font draws for a code point. The Unicode core specification is explicit that an abstract character should not be confused with a glyph: the code point is the identity, the glyph is one of many possible pictures of it.
The mapping runs many to many in both directions. One glyph can stand for several code points: the é above is one shape and two encodings, and Latin A (U+0041), Cyrillic А (U+0410) and Greek Α (U+0391) look identical in most fonts. One code point can also have several glyphs: the Arabic letter beh U+0628 is drawn as isolated, initial, medial or final form depending on its neighbours, yet the text stores a single code point and the font chooses the shape.
ASCII: the first 128 code points
The oldest slice of Unicode is ASCII, a 7-bit code with 128 positions defined in RFC 20, which occupies code points U+0000 to U+007F unchanged. The slide shows a fragment of the table in hexadecimal and decimal. The version below adds the binary column, because the binary reveals a fact you will use every time you write a case-folding rule.
| Character | Hex | Decimal | Binary |
|---|---|---|---|
| < | 3C | 60 | 0011 1100 |
| = | 3D | 61 | 0011 1101 |
| > | 3E | 62 | 0011 1110 |
| ? | 3F | 63 | 0011 1111 |
| @ | 40 | 64 | 0100 0000 |
| A | 41 | 65 | 0100 0001 |
| B | 42 | 66 | 0100 0010 |
| C | 43 | 67 | 0100 0011 |
| \ | 5C | 92 | 0101 1100 |
| ] | 5D | 93 | 0101 1101 |
| ^ | 5E | 94 | 0101 1110 |
| _ | 5F | 95 | 0101 1111 |
| ` | 60 | 96 | 0110 0000 |
| a | 61 | 97 | 0110 0001 |
| b | 62 | 98 | 0110 0010 |
| c | 63 | 99 | 0110 0011 |
Compare A at 0100 0001 with a at 0110 0001. They differ in exactly one bit, bit 5, which is worth 0x20 or 32. Every uppercase letter sits 32 positions before its lowercase partner, so Case folding in ASCII is a single OR with 0x20, which is why it is so fast and why it breaks the moment text leaves ASCII. Arabic has no case, and Greek final sigma has no simple bit rule, so Unicode defines case folding as a table lookup instead.
Normalization: one canonical spelling for each character
Back to café. Unicode calls U+00E9 the precomposed form and U+0065 U+0301 the decomposed form, and declares them canonically equivalent: they must be treated as the same character. Arabic has the same pair. Alef with hamza above, U+0623, decomposes canonically to bare alef U+0627 plus the combining hamza above U+0654, as the Unicode code chart records. A different, weaker relation is compatibility equivalence: the lam-alef ligature U+FEFB is compatible with U+0644 U+0627, and the ligature fi (U+FB01) with the two letters f and i. Compatibility variants look different or carry formatting, so folding them loses information.
Unicode normalization converts text to one of four standard forms defined in Unicode Standard Annex 15. The D forms decompose, the C forms decompose and then recompose, and the K forms also apply the lossy compatibility mappings. The table gives the result for é, for the lam-alef ligature and for the fi ligature, with the UTF-8 byte count of each result so you can see that normalization changes lengths, not only identities.
| Form | Operation | é (either spelling) | ﻻ U+FEFB | fi U+FB01 |
|---|---|---|---|---|
| NFD | Canonical decomposition | U+0065 U+0301 (3 bytes) | U+FEFB (3 bytes) | U+FB01 (3 bytes) |
| NFC | Canonical decomposition, then canonical composition | U+00E9 (2 bytes) | U+FEFB (3 bytes) | U+FB01 (3 bytes) |
| NFKD | Compatibility decomposition | U+0065 U+0301 (3 bytes) | U+0644 U+0627 (4 bytes) | f i (2 bytes) |
| NFKC | Compatibility decomposition, then canonical composition | U+00E9 (2 bytes) | U+0644 U+0627 (4 bytes) | f i (2 bytes) |
Worked example
NFD and NFC on a Latin and an Arabic letter
Decompose é
NFD(U+00E9) looks up the canonical decomposition and returns U+0065 U+0301, base letter then combining mark. Two code points, three UTF-8 bytes.Recompose é
NFC(U+0065 U+0301) first decomposes (no change) and then composes the base with its mark, returning U+00E9. One code point, two bytes. Both input spellings end in the same place.Decompose alef with hamza
NFD(U+0623) returns U+0627 U+0654, and NFC folds that pair back to U+0623. Bare alef U+0627 stays bare under every form: it is a different letter, not a variant spelling.Fold the ligature
NFC(U+FEFB) is unchanged, three bytes. NFKC(U+FEFB) returns U+0644 U+0627, two letters and four bytes, because the ligature is only compatibility-equivalent.Result
NFC is the safe default for storage, because it is lossless and shortest. NFKC is the matching form, because it also folds ligatures, fullwidth digits and superscripts, at the price of erasing them.
Here is the tokenizer bug normalization prevents. Suppose a BPE vocabulary is learned on NFC text, so "café" is one frequent token. Now NFD input arrives. The tokenizer sees c a f e U+0301, finds no merge that includes a lone combining mark, and emits "cafe" plus a separate accent token that no training word ever contained. Equality tests, dictionary lookups and Tokenization all fail in the same way, and all three are fixed by normalizing once, at ingestion, before anything else runs. Note that alef normalization in Arabic NLP toolkits, which folds U+0623, U+0625 and U+0622 into bare alef, is an application rule on top of Unicode, not one of the four forms.
Recall
Define code point and glyph, and give one example of each direction of the many-to-many mapping.
Recall
Name two normalization forms and one tokenization bug that normalization prevents.
Quick check
Which statement correctly separates Unicode from UTF-8?
Quick check
NFC applied to the sequence U+0065 U+0301 produces what?
The Arabic word كتاب has four letters, and Python's len agrees: four. Encode it and count the bytes: eight. The English word "Hi" is two and two. The difference is the whole content of this concept. Every Code point above U+007F needs more than one byte, and the rule that decides how many, and which bits go where, is small enough to learn by heart.
UTF-8 is a variable-length encoding form that maps each Unicode scalar value (a code point that is not a surrogate) to a sequence of one to four bytes. It is defined in the Unicode core specification and, for the Internet, in RFC 3629. Code points below 0x80 take one byte, and most other characters take two, three or four. The slide's mapping table is the exact rule, reproduced here with the payload capacity of each row.
| Range | Code point bits | Payload bits | Byte 1 | Byte 2 | Byte 3 | Byte 4 |
|---|---|---|---|---|---|---|
| U+0000 to U+007F | 0xxxxxxx | 7 | 0xxxxxxx | |||
| U+0080 to U+07FF | 00000yyy yyxxxxxx | 11 | 110yyyyy | 10xxxxxx | ||
| U+0800 to U+FFFF | zzzzyyyy yyxxxxxx | 16 | 1110zzzz | 10yyyyyy | 10xxxxxx | |
| U+010000 to U+10FFFF | 000uuuuu zzzzyyyy yyxxxxxx | 21 | 11110uuu | 10uuzzzz | 10yyyyyy | 10xxxxxx |
Read the templates as two kinds of byte. The first byte of every sequence is the lead byte, and its run of leading ones announces the length: 0 means one byte, 110 two, 1110 three and 11110 four. Every later byte is a continuation byte and starts 10, leaving six payload bits. The payload capacities are 7, 11, 16 and 21 bits, and the last is no accident: the largest code point U+10FFFF is exactly a 21-bit number, so four bytes suffice for the whole codespace.
Worked example
Encode the Arabic letter beh, U+0628
Write the code point in binary
0x0628 is 0000 0110 0010 1000. Its value 1576 lies between 0x80 and 0x7FF, so it takes the two-byte row with 11 payload bits.Take the low 11 bits
Drop the five leading zeros: 11000 101000. The template is 110yyyyy 10xxxxxx, so yyyyy = 11000 and xxxxxx = 101000.Fill the templates
Byte 1 is 110 + 11000 = 11011000 = D8. Byte 2 is 10 + 101000 = 10101000 = A8.Result
U+0628 encodes as D8 A8, two bytes.
Worked example
Encode the grinning face emoji, U+1F600
Pick the row
0x1F600 is above 0xFFFF, so it takes the four-byte row with 21 payload bits: 0 0001 1111 0110 0000 0000.Split into u, z, y, x groups
uuuuu = 00001, zzzz = 1111, yyyyyy = 011000, xxxxxx = 000000. The five u bits straddle two bytes: the top three go into byte 1 and the low two into byte 2, exactly as the template 11110uuu 10uuzzzz shows.Fill the templates
Byte 1: 11110 + 000 = F0. Byte 2: 10 + 01 + 1111 = 10011111 = 9F. Byte 3: 10 + 011000 = 98. Byte 4: 10 + 000000 = 80.Result
U+1F600 encodes as F0 9F 98 80, four bytes.
Worked example
Encode the letter a, U+0061
Pick the row and copy
0x61 is below 0x80, so the template is 0xxxxxxx and the seven payload bits 1100001 sit under a leading zero: 01100001.Result
U+0061 encodes as 61, the same number as its ASCII code. Nothing changed.
| Character | Code point | Range | Bytes | UTF-8 (hex) |
|---|---|---|---|---|
| a | U+0061 | U+0000 to U+007F | 1 | 61 |
| é | U+00E9 | U+0080 to U+07FF | 2 | C3 A9 |
| ب | U+0628 | U+0080 to U+07FF | 2 | D8 A8 |
| € | U+20AC | U+0800 to U+FFFF | 3 | E2 82 AC |
| 中 | U+4E2D | U+0800 to U+FFFF | 3 | E4 B8 AD |
| ﻻ | U+FEFB | U+0800 to U+FFFF | 3 | EF BB BB |
| 😀 | U+1F600 | U+010000 to U+10FFFF | 4 | F0 9F 98 80 |
Now do it live. The simulator below encodes whatever you type, one character at a time, and shows the row, the payload bits, the templates and the resulting bytes. It preloads Hi كتاب 😀: nine code points, ten UTF-16 units and sixteen bytes. The € 中 preset exercises the three-byte row, the only one with a 1110 lead byte. Switch the normalization control to NFD with the café preset, or to NFKC with the ligature preset, and watch the byte count change before a single byte is encoded.
- HU+0048U+0000 to U+007F
1 byte, 7 payload bitsbits 100100048010010000xxxxxxx - iU+0069U+0000 to U+007F
1 byte, 7 payload bitsbits 110100169011010010xxxxxxx - ␠U+0020U+0000 to U+007F
1 byte, 7 payload bitsbits 010000020001000000xxxxxxx - كU+0643U+0080 to U+07FF
2 bytes, 11 payload bitsbits 11001000011D9 8311011001110yyyyy1000001110xxxxxx - تU+062AU+0080 to U+07FF
2 bytes, 11 payload bitsbits 11000101010D8 AA11011000110yyyyy1010101010xxxxxx - اU+0627U+0080 to U+07FF
2 bytes, 11 payload bitsbits 11000100111D8 A711011000110yyyyy1010011110xxxxxx - بU+0628U+0080 to U+07FF
2 bytes, 11 payload bitsbits 11000101000D8 A811011000110yyyyy1010100010xxxxxx - ␠U+0020U+0000 to U+007F
1 byte, 7 payload bitsbits 010000020001000000xxxxxxx - 😀U+1F600U+010000 to U+10FFFF
4 bytes, 21 payload bitsbits 000011111011000000000F0 9F 98 801111000011110uuu1001111110uuzzzz1001100010yyyyyy1000000010xxxxxx
Dim cells are the fixed prefix bits of each byte template. Accent cells are the payload bits copied from the code point, left to right. Switch to NFD or NFKC to watch a precomposed letter or a ligature change its byte count before encoding.
Decoding, and why the prefixes matter
The prefixes are what make UTF-8 safe to read from any position. A decoder that sees a byte starting 0 knows it is a complete ASCII character. A byte starting 110, 1110 or 11110 announces one, two or three continuation bytes to follow. A byte starting 10 can never begin a character, so a decoder dropped into the middle of a stream steps backwards at most three bytes to find a lead byte. RFC 3629 lists this as a design property (character boundaries are easily found from anywhere in an octet stream), Jurafsky and Martin call it self-synchronizing, and it is why a corrupted byte damages one character rather than the rest of the file.
Worked example
Decode D8 A8 back to a code point
Read the lead byte
D8 is 11011000. It starts 110, so expect exactly one continuation byte and keep the five bits after the prefix: 11000.Read the continuation byte
A8 is 10101000. It starts 10 as required, so keep the six bits after the prefix: 101000.Concatenate
11000 + 101000 = 11000101000 = 0x628.Result
U+0628, the letter beh. Had the second byte started with anything other than 10, the decoder would report an error at that exact position.
Three consequences follow from the table and are worth stating explicitly. First, backward compatibility with ASCII: every code point below 0x80 becomes the single byte with the same value, so an ASCII file is already a valid UTF-8 file and a UTF-8 file that happens to contain only ASCII is byte for byte an ASCII file. Second, bytes below 0x80 never appear inside a multibyte sequence, so a program that searches for a space or a newline byte will never find one in the middle of an Arabic letter. Third, some byte values are simply impossible: C0, C1 and F5 to FF never occur in valid UTF-8, because they would encode overlong sequences or code points past U+10FFFF (RFC 3629, section 1, with the syntax that rules them out in section 4).
Recall
How many UTF-8 bytes do U+0628, U+00E9, U+4E2D and U+1F600 need, and why?
Recall
Encode ñ, U+00F1, by hand.
Recall
Why is UTF-8 backward compatible with ASCII, and how does a decoder find where a character starts?
Quick check
How many bytes does UTF-8 use for the Arabic letter beh, U+0628?
Quick check
A decoder reads a byte whose top bits are 10. What does it conclude?
A tokenizer trained with a character vocabulary in 2020 meets the melting face emoji 🫠, added as U+1FAE0 in Unicode 14 the following year. It has never seen the character, so it emits the unknown token. The same happens with an Arabizi spelling like "ktaab" typed into a word-level vocabulary, or any typo, any new script, any rare Han character. Now imagine the base symbols were bytes instead. Every UTF-8 byte is one of exactly 256 values, all of them already in the vocabulary, so there is nothing left to be unknown.
That is Byte-level tokenization: treat the UTF-8 bytes of the text, not its characters, as the atoms. Radford and colleagues motivated it in the GPT-2 paper with the numbers. A Code point base vocabulary would start at over 130,000 symbols before a single merge (about 160,000 in Unicode 17), while a byte-level base needs only 256. Jurafsky and Martin draw the consequence: because there are only 256 possible byte values, there will be no unknown tokens, and the <UNK> symbol becomes unnecessary. Arbitrary scripts, emoji and typos all decompose into bytes the model has seen.
| Property | Character vocabulary | Byte vocabulary |
|---|---|---|
| Base symbols | About 160,000 assigned characters (over 130,000 when GPT-2 was written) | Exactly 256 byte values |
| Unknown input possible | Yes, any unseen character | No, every byte is in the base |
| Base units for كتاب | 4 characters | 8 bytes |
| Interpretability of a base unit | A letter you can read | Half of an Arabic letter |
| Structure | Given by the script | Recovered by learned merges |
The price is structure. Bytes do not know where a character ends. The word كتاب becomes eight opaque symbols, and the letter ب is D8 A8, two units that mean nothing on their own. A sentence of Arabic is roughly twice as many base units as Latin text of the same length, which the simulator's كتاب preset shows as 4 characters against 8 bytes. Left alone, a byte model would spend its capacity relearning the UTF-8 table. So bytes are never used alone: they are combined with learned merges that glue frequent byte sequences back into characters, then into subwords. Jurafsky and Martin note that BPE over bytes rediscovers the two- and three-byte UTF-8 sequences early, and that it can also learn invalid sequences straddling a character boundary, which implementations filter out. GPT-2 adds a rule that prevents merges across character categories, because naive byte merges were sub-optimal.
Recall
What does Python return for len of the string كتاب, and for len of its UTF-8 encoding? Which number does a byte-level tokenizer start from?
Quick check
How large is the base vocabulary of a byte-level tokenizer before any merges?
Recap
If you remember nothing else
- A corpus is a situated sample that varies by domain, genre, time, demographics and variety. Models inherit its limits, so document it with a data statement or datasheet.
- Datasheet fields: motivation and intended use, composition, collection process, preprocessing and annotation, ethical considerations, distribution constraints.
- A code point is an integer from U+0000 to U+10FFFF. A glyph is the drawn shape. The two map many to many.
- ASCII is the first 128 code points, and upper and lower case letters differ by exactly 0x20.
- NFC and NFD make é and e plus U+0301 compare and tokenize alike. Only NFKC and NFKD also fold the ligature ﻻ into ل plus ا.
- UTF-8 uses 1, 2, 3 or 4 bytes with lead patterns 0, 110, 1110, 11110 and continuation bytes 10xxxxxx. Payloads hold 7, 11, 16 and 21 bits.
- U+0628 is D8 A8, U+00E9 is C3 A9, U+4E2D is E4 B8 AD, U+1F600 is F0 9F 98 80, and a is 61.
- String length counts code points, not bytes: كتاب is 4 code points and 8 bytes.
- Bytes give a 256-symbol base with no unknown character. Learned merges restore the structure that bytes lose.
Sources
- Speech and Language Processing, chapter 2: Words and TokensBookJurafsky and Martin, 3rd edition draftSections 2.3 on code points, UTF-8 and its self-synchronizing property, 2.4.3 on byte-level BPE, 2.5 on corpora and datasheets(opens in a new tab)
- RFC 3629: UTF-8, a transformation format of ISO 10646RFCIETFByte templates (section 3); section 1 lists ASCII compatibility, character boundaries findable from any position, and the bytes C0, C1, F5 to FF never appearing(opens in a new tab)
- The Unicode Standard, Version 17.0, chapter 3: ConformanceDocsUnicode ConsortiumAbstract character versus glyph (D7), codespace and code point (D9, D10), UTF-8 encoding form (D92)(opens in a new tab)
- Unicode Standard Annex 15: Unicode Normalization FormsDocsUnicode ConsortiumTable 1 defines NFD, NFC, NFKD and NFKC(opens in a new tab)
- FAQ: UTF-8, UTF-16, UTF-32 and BOMDocsUnicode ConsortiumUnicode versus encoding forms, the 21-bit codespace, UTF-16 in Java and Windows(opens in a new tab)
- Arabic code chart, U+0600 to U+06FFDocsUnicode ConsortiumU+0623 decomposes canonically to U+0627 U+0654; U+0628 ARABIC LETTER BEH(opens in a new tab)
- Arabic Presentation Forms-B code chart, U+FE70 to U+FEFFDocsUnicode ConsortiumU+FEFB is compatibility-equivalent to U+0644 U+0627(opens in a new tab)
- RFC 20: ASCII format for network interchangeRFCIETF7-bit code, 0x5B is [, 0x5D is ], A is 0x41 and a is 0x61(opens in a new tab)
- Unicode HOWTODocsPython documentationlen counts code points; encode returns bytes; unicodedata.normalize(opens in a new tab)
- Data Statements for Natural Language Processing: Toward Mitigating System Bias and Enabling Better SciencePaperTransactions of the ACL, Bender and Friedman, 2018Schema sections A to I; systems work best for the populations represented in their training data(opens in a new tab)
- Datasheets for DatasetsPaperCommunications of the ACM, Gebru et al., December 2021Motivation, composition, collection, preprocessing, uses, distribution, maintenance; the electronics datasheet analogy(opens in a new tab)
- Language Models are Unsupervised Multitask LearnersPaperOpenAI, Radford et al., 2019Section 2.2: over 130,000 code point symbols versus a 256-symbol byte base; merges blocked across character categories(opens in a new tab)
- Usage statistics of UTF-8 for websitesArticleW3TechsUTF-8 used by 99.1% of websites with a known encoding, September 2026(opens in a new tab)