LessonsAdvanced: Bigger programsLesson 40

Unicode and text encodings

Text in every language and emoji: characters, codepoints and bytes, converting text to and from UTF-8, and comparing accented text safely.

  • Advanced
  • Lesson 40 of 83
  • About 10 minutes
  • 7 examples

Text in any language

xTalk text is Unicode: it can hold every writing system in use, and emoji. Chunk expressions work on characters as a reader sees them, whatever the language:

ExampleRun it in the playground
put "héllo wörld" into tText
put the number of chars of tText
put toUpper(tText)
put "日本語のテキスト" into tJapanese
put the number of chars of tJapanese
put char 1 to 3 of tJapanese
Output
11
HÉLLO WÖRLD
8
日本語

Codepoints

Unicode gives every character a number, its codepoint. codepointToNum and numToCodepoint go between the two:

ExampleRun it in the playground
put codepointToNum("A") && codepointToNum("é") && codepointToNum("€")
put numToCodepoint(9731)
put numToCodepoint(128512)
Output
65 233 8364
☃
😀

Characters, codepoints and code units

What a reader sees as one character can be more than one codepoint: an e followed by a combining accent is one character, é, made of two codepoints. And inside the engine some codepoints take two code units. Usually you want chars; the others are there when you need them:

ExampleRun it in the playground
put "e" & numToCodepoint(769) into tAccented
put the number of chars of tAccented
put the number of codepoints of tAccented
put numToCodepoint(128512) into tSmile
put the number of chars of tSmile
put the number of codeunits of tSmile
Output
1
2
1
2

Comparing accented text

The same word can be stored in two ways: with é as one codepoint, or as e plus an accent. They look identical, and xTalk treats them as the same, as it ignores case: is, offset and array keys all see one word. But their bytes differ, so once the text leaves xTalk, written to a file, hashed or sent to a server, they are two different words. Set the formSensitive to true to compare the forms exactly, and use normalizeText to put text into one standard form, NFC:

ExampleRun it in the playground
put "caf" & "e" & numToCodepoint(769) into tDecomposed
put tDecomposed is "café"
put the number of bytes of textEncode(tDecomposed, "UTF-8") && the number of bytes of textEncode("café", "UTF-8")
set the formSensitive to true
put tDecomposed is "café"
put normalizeText(tDecomposed, "NFC") is "café"
Output
true
6 5
false
true

Text and bytes: encodings

Files and networks carry bytes, not characters. An encoding is the rule that turns one into the other, and today's standard is UTF-8, which uses one byte for plain English letters and two to four for the rest. textEncode turns text into bytes, and textDecode turns bytes back into text:

ExampleRun it in the playground
put "naïve café" into tText
put textEncode(tText, "UTF-8") into tBytes
put the number of chars of tText
put the number of bytes of tBytes
put textDecode(tBytes, "UTF-8") is tText
Output
10
12
true

Ten characters made twelve bytes: ï and é take two each. Other encodings make other bytes from the same text:

ExampleRun it in the playground
put "café" into tText
put the number of bytes of textEncode(tText, "UTF-8")
put the number of bytes of textEncode(tText, "ISO-8859-1")
put the number of bytes of textEncode(tText, "UTF-16")
Output
5
4
8

The rule to remember: decode as text comes in, encode as it goes out. Read a file's bytes and textDecode them; textEncode text before you write it to a file or send it. The next lessons, Reading and writing files and Files a piece at a time, do exactly that.

Try it yourself

Write a function that gives the codepoints of a piece of text, written the way Unicode charts write them: U+ and four or more hexadecimal digits. Try it on Añ€.

Show a solution
ExampleRun it in the playground
function codepointList pText
   put empty into tList
   repeat for each codepoint tPoint in pText
      put format("U+%04X", codepointToNum(tPoint)) & space after tList
   end repeat
   return word 1 to -1 of tList
end codepointList

put codepointList("Añ€")
Output
U+0041 U+00F1 U+20AC

Words in this lesson

Each one finds every lesson that uses it. The Dictionary has the whole story of each, with its syntax and more examples.