ljust and %-8s pad by code points, not columns¶
Level: 101 · anyone printing a table to a terminal
One line: ljust(8) and format("%-8s") count code points, so a decomposed café comes out one column short while 緑茶 and 🍵 — two terminal columns per character — push the bar to the right; String has no display-width method, and the example borrows the one inside Reline, irb's line editor.
Verified output of padding_counts_characters_rb.rb — regenerated by tools/run_examples.py, never hand-typed.
ljust(8):
tea |3
café |4
café |4
緑茶 |5
🍵 |6
format("%-8s|%d"):
tea |3
café |4
café |4
緑茶 |5
🍵 |6
Three counts of the same five names:
tea length 3, grapheme_clusters 3, terminal columns 3
café length 4, grapheme_clusters 4, terminal columns 4
café length 5, grapheme_clusters 4, terminal columns 4
緑茶 length 2, grapheme_clusters 2, terminal columns 4
🍵 length 1, grapheme_clusters 1, terminal columns 2
Padding by columns instead:
tea |3
café |4
café |4
緑茶 |5
🍵 |6
Whether the blocks on this page line up depends on the fonts your browser picks for the Japanese and the emoji. A terminal is the reference: there, the first two tables are ragged and the last one is straight.
Reading the output¶
- The first two tables pad by code points.
ljustandformatagree with each other, and both are wrong for three of the five rows. - Three counts, side by side. The decomposed
caféis 5 code points and 4 grapheme clusters in 4 columns.緑茶is 2 code points and 2 grapheme clusters in 4 columns.🍵is 1 of each in 2 columns. Grapheme clusters fix the combining accent and nothing else; only the column count predicts alignment. - Padding by columns lines up. The last table computes
8 - widthspaces per name.
What to do¶
For text that ends up in a terminal table, pad by display width. Reline::Unicode.calculate_width, used above, is internal to Reline rather than a public API; the unicode-display_width gem is the usual choice in code you ship. (Not machine-checked here.)
If you are coming from another language¶
Python's format spec pads by code points too:
Verified output of padding_counts_characters_py.py — regenerated by tools/run_examples.py, never hand-typed.
The Python library's The format mini-language ↗ and the Rust library's The format mini-language ↗ take apart the width field in their own languages.