Measure the ruler itself first
What this article measures is the pinyin table behind the Chinese-to-pinyin page: how large it is, how much of a whole text it hits, and how it treats a character it does not carry. The measuring stick is not an improvised paragraph but the simplified full-text TXT already published on this site: 28,903 bytes, 10,211 UTF-16 code units, 10,209 counted by code point. Units exceed points by 2 because the file holds exactly 2 astral-plane characters, one U+2C7FD on line 219 and one U+2AFA2 on line 788, each occupying 2 surrogate code units. Splitting on LF produces 819 segments, 818 LF markers and 148 empty segments. This book serves here purely as a character-counting ruler, with no passage quoted and no content discussed.
The corpus is the TXT attached to the resource page 笠翁对韵 (Liweng Duiyun). Continuing the inventory: 7185 Han characters, 2053 distinct heads after deduplication, U+3000 full-width spaces 580, ASCII spaces 4 and 3024 non-Han characters.
How big the table is, and the two counts: 25 occurrences, 21 heads
The pinyin word list lives in public/static/js/pinyin-dict.js, measured at 110,567 bytes on disk; PINYIN_MAP has 8,026 keys, POLYPHONES has 72 keys, and all 72 polyphone keys also sit inside those 8,026. The page under test is Chinese to Pinyin; per-character sound lookup is the getPinyin query against PINYIN_MAP.
Passing all 7185 Han characters through it one by one: 7160 hit and 25 fall inside the CJK_RE range yet carry no value in the table. The hit rate is 7160 / 7185 = 99.65 percent; by unique character, 2032 of 2053, which is 98.98 percent. The two counts stay apart: 25 tallies occurrences, 21 tallies unique heads. Four heads, 蕡、嘐、麌、毚, appear twice each and both uses of 嘐 share line 490, precisely the gap between 25 and 21.
| Class | Heads | Head forms | Where the test lands |
|---|---|---|---|
| A traditional spelling only | 2 | 對、韻 | both simplified counterparts are in the table, valued dui4 and yun4 |
| B one glyph for both scripts, head off-table | 16 | 釭艭湡葅炤纑蝃蕡榼踘嘐薙麌毚睆瑊 | querying the table returns empty on either side |
| C simplified counterpart also off-table | 3 | 鱄、鳷、羵 | the matching simplified form is itself astral, e.g. U+2B68B |
Figure 1: the hit count and the 21 off-table heads sorted into three classes.
Classification follows one test: whether a character's simplified counterpart, per this site's traditional-to-simplified mapping, is also in PINYIN_MAP. The A-class 對 and 韻 appear on line 815 as variant spellings of common characters met in collation and catalogue notes; their simplified side is in the table, so they do not count as unrecorded. The 16 B-class heads use one glyph across both scripts and return nothing on either side. The 3 C-class heads have simplified counterparts that are astral characters themselves, with code points such as U+2B68B, U+2B6DB and U+2B157, so they land off-table too. 2 + 16 + 3 = 21, and one test splits the accounts into three clean piles.
232 polyphone hits, one sound each
Polyphonic characters occur 232 times across this corpus and touch 42 distinct POLYPHONES keys. Yet the full pinyin, compare and initials views all take one default reading from PINYIN_MAP: for 行 the raw value is xing2, the three views give xíng, 行(xíng) and x, while the hang2 parked among the candidates never enters any output string.
Candidates only reach the py-poly panel below the result; that block sits at the tail of convert(), shared by all three views, independent of the mode. Over the whole corpus the panel holds a constant 42 rows, and the 6 rows whose candidates are joined by an ideographic comma are 参、茸、切、得、差、佛, meaning keys with more than 2 candidates besides the current reading. The source text itself carries the 、 punctuation mark 2 times, a separate account from those 6 joiners, never to be blended into one figure.
What the toggles and views actually output
The initials view takes py.charAt(0): syllables beginning with zh, ch and sh, 353, 279 and 450 occurrences, shrink to the bare letters z, c and s and lose every h entirely. Zero-initial characters starting on a vowel yield that vowel itself, 56 times in all; 727 begin with y and 244 with w.
The tone toggle never changes output length. With the full view, keep-non-Han on and space separation, tone-marked and unmarked runs both measure 32,884 code units, and with no separator both measure 25,724: every tone-marked character is one code unit, so the replacement is unit for unit. Replacing v with ü runs across the whole string instead of touching only the tone slot: the corpus contains 47 hit syllables carrying v. The unmarked view gives exactly 47 bare ü. The marked view combines ǚ 32 times, ǜ 12 times and ǘ 2 times into 46 precomposed marked characters; one more marks the e, and the tail replacement leaves a bare ü where the v was not the tone slot, so 46 + 1 = 47. The tone slot follows vowel priority with v as the fallback, which is why nv4 writes nǜ and lve4 writes lüè. Neutral-tone values ending in 0 fill 28 table entries, this corpus hits 2 of them, and even the marked view gives no symbol.
Figure 2: length accounts for the keep toggle and the separators, all in code units.
The two states of keep-non-Han differ by 3051 code units, and the ledger is 3024 non-Han characters, 25 off-table occurrences plus the one extra unit each of the 2 astral heads. Whether off-table and astral characters survive depends on this single switch, and switching it off also swallows the 818 LF markers. Each of the three non-empty separators adds 7160 characters to the whole string: a separator only follows a hit pinyin, never an off-table or non-Han character, so space, hyphen and underscore match exactly.
The four stat boxes and the residue of empty input
Feeding in the whole corpus keeps the four stat boxes constant across full, compare and initial: 7160 annotated, 25 unrecorded Han, 3024 other characters and 10211 total characters. The last box reads input.length, a code-unit count 2 above the 10209 code points for the same reason as those two astral heads: they are filed under other characters, not unrecorded Han, because CJK_RE only covers the basic plane.
On empty input convert returns early, so the output and the stat are cleared, but that step runs before the polyphone panel is rebuilt. Feed the corpus, then clear the input box, and the result area goes blank while a panel labelled 42 polyphones remains parked exactly where it was.
Common questions
Q: Are 25 occurrences and 21 heads the same thing?
No. 25 tallies occurrences and 21 tallies unique heads; the difference comes from 蕡、嘐、麌、毚 appearing twice each, so 25 occurrences are not 25 separate characters.
Q: What happens to off-table characters when the whole file is converted?
With keep-non-Han enabled, the 25 off-table occurrences are pasted into the output untouched and receive no pinyin brackets. Switch it off and the off-table Han vanish together with the non-Han characters, a two-state length gap of 3051 code units. The unrecorded-Han box in stat reports exactly those 25.
Q: Do polyphone candidates only show up in compare mode?
No. The py-poly panel sits in the shared tail of all three views and does not depend on the mode, but candidates take no part in choosing a reading: every view outputs the single default sound from PINYIN_MAP, so context still needs a human check. How the annotation itself is added belongs to the older article adding pinyin above Chinese characters, which this one does not repeat.
Q: What does the corpus itself say?
Not handled here. The book appears only as counted characters, a ruler for measuring the pinyin table, and its content is not interpreted.