00 — Start here¶
Level: 101 · read this first
This library is for someone who writes Ruby and has been surprised by text: an Encoding::CompatibilityError from a line that worked yesterday, a script that is fine in a terminal and broken under cron, a CSV whose row["id"] is nil, a validation regex that let a newline through.
What it assumes¶
Ruby syntax, and the words byte, code point and UTF-8. The Encodings library ↗ builds those up from nothing if they are new. Everything else a page needs is on that page.
The five to read first¶
- A string is bytes plus a label — the model everything else follows from.
- What
File.readreturns depends on the locale — why it works on your machine and not in the container. ^and$match at every line — the one with a security bug in it.- The BOM is kept unless you ask — the one you will hit this month.
- String literals are chilled, not frozen — what Ruby 3.4 and 4.0 changed about literals, and what they did not.
How to read a page¶
Each has the same shape: a one-line claim, the idea in prose, an output block from a program in that page's examples/ folder, a reading of that output, and what to do about it. Most end with the same question put to Perl and Python, with their output too, and links to the sibling libraries. The output blocks are generated, never typed — if a page shows a value, Ruby 4.0 printed it.
Ruby's text scorecard¶
A summary of the whole library, so you know what you are walking into.
Genuinely good
- Normalization, grapheme clusters, full case mapping and case folding are
Stringmethods —unicode_normalize,grapheme_clusters,upcase,downcase(:fold)— with no gem. See chapter 03. - Case mapping has no locale to go wrong: Turkish rules are an option you pass,
:turkic. See Case mapping is full Unicode. - Joining strings in incompatible encodings raises instead of producing mojibake — Perl prints
caféin the same situation. See Mixing encodings raises. unicode_normalizerefuses a string that is not in a Unicode encoding rather than guessing. See Normalization is built in.chompremoves a Windows\r\n; Perl's does not. Seechompknows about CRLF.- Since Ruby 3.2 the regex engine caches its backtracking, so the classic ReDoS pattern runs in linear time, and there is a timeout for the patterns the cache cannot help. See Backtracking has a cache.
pack's letters are Perl's, so a Perl record layout ports letter for letter. Seepackspeaks Perl.
Genuinely surprising, and sometimes bad
^and$are line anchors, always. See^and$match at every line.\w,\dand\sare ASCII-only on UTF-8 text, while\bis not. See\wis ASCII.- A byte that is invalid in its string's encoding gets in silently, and only some methods notice. See Invalid bytes get in, and fail later.
File.readlabels text with an encoding taken from the locale — US-ASCII underLC_ALL=C. See the locale lesson.- A UTF-8 byte order mark is kept as a character. See The BOM is kept.
String.newandread(n)return BINARY. SeeString.newis binary."42abc".to_iis 42 and"abc".to_iis 0, with no error. Seeto_inever fails.split(" ")is not a split on a space. Seesplithas an awk mode.- A string literal is still mutable in Ruby 4.0, with a warning only under
-W:deprecated. See String literals are chilled. ljustand%-8spad by code points, not by terminal columns. See Padding counts code points.downcasedoes not apply the Greek final-sigma rule; Python'slowerdoes. See Case mapping is full Unicode.\his a hex digit in Ruby and horizontal whitespace in Perl. See\his a hex digit.
The pattern: Ruby is right about Unicode content — what a character's case is, how it composes, where a grapheme ends — and Perl-shaped about syntax: anchors, split, \h, to_i. Most of the bugs live in the Perl-shaped half, and in encoding labels picked up from the environment instead of stated in the code.