Open the app — Character Counter
When someone asks how long your manuscript is, which number do you give?
A translation job is priced by the Hanja in the source, a writing fee by 200-character manuscript sheets, a submission form by characters including spaces.
Same text, three yardsticks.
A word processor’s counter, though, lumps Hangul, Hanja, Latin letters and punctuation together.
If the source and the translation share one file, the source length is hard to pull out.
And with Old Hangul, every jamo may be counted on its own, inflating the number.
Paste your text once and this tool shows all three at once.
- Hanja — only characters in the Unicode Hanja ranges
- Characters by script — Hangul (Old Hangul included), Hanja, Kana, English & Latin, Cyrillic, digits, punctuation, spaces
- 200-character manuscript sheets — counted the same way as the Hangul word processor’s document statistics
It can also pick just the source lines, or leave out bracketed parts such as notes.
How to Use
Basic steps
- Paste the text into Input Text, or load a file.
- Turn on the options below if you need them.
- Click [Count].
Reading the result
The result reads like a short paragraph.
1,400 characters in total (1,120 without spaces)
Hanja: 400 (40 of them radicals)
Hangul: 360
English & Latin: 120
Digits: 120
Punctuation & symbols: 80
Spaces: 280
8 sheets of 200-character manuscript paper (8 without spaces)
The total comes first, then the count for each script, and the manuscript sheets last.
The script counts add up to the total.
They’re right-aligned so the digits line up, which makes adding a few of them by hand easy.
- Scripts with no characters are left out. Listing empty ones would only bury the ones that matter.
- (of them radicals) appears only when the radical option is on.
- Sheets without spaces are counted by removing spaces and tabs from each paragraph and applying the same manuscript rules. The Hangul word processor has no such figure, so it’s explained in “200-Character Manuscript Sheets” below.
Which character goes where is laid out in “Characters by Script” below.
If the marker doesn’t match your text, no lines are picked and the total comes out 0.
If the numbers look oddly small, check the marker first.
As soon as you edit the text or an option, the previous result disappears.
A number that no longer matches the text would only confuse.
Counting only lines that start with a marker
Some manuscripts alternate source and translation line by line.
((OR))學而時習之不亦說乎
((TR))배우고 때때로 익히면 또한 기쁘지 아니한가.
((OR))有朋自遠方來不亦樂乎
((TR))벗이 먼 곳에서 찾아오면 또한 즐겁지 아니한가.
Count everything and the translation gets mixed in, so you can’t tell how long the source is.
With this option on, only lines starting with the given marker are counted.
That applies to everything — the Hanja count, the by-script counts and the manuscript sheets.
The default is ((OR)); change it to suit your text.
A few rules apply.
- The marker must come at the very start of the line. A line with leading spaces is not picked.
Being lenient about spaces would also catch lines where the same string happens to appear mid-text. - The marker itself is not counted. Even a marker containing Hanja, like
((甲)), is not part of the text. - Turning the option on with an empty box is an error. An empty string matches every line, which is the same as turning the option off — most likely a mistake.
Skipping text inside brackets
Sometimes notes or collation marks sit in the middle of the source.
學而時習之{註:時는 때때로이다}不亦說乎
A note is not part of the source.
With this option on, the brackets and everything inside them are removed before counting.
The default is {...}.
Write brackets as open...close.
The ... in the middle splits the two, so brackets longer than one character work too.
| What you write | What gets removed |
|---|---|
{...} | From { to } |
[[...]] | From [[ to ]] |
<!--...--> | HTML comments |
|...| | Between | and the next | |
Advanced mode (regular expressions)
Some conditions are hard to express in basic mode.
Say the ((OR)) markers are indented, or the source markers vary while only the translation markers are consistent.
Turn on [Advanced mode (regular expressions)] to write both options as regular expressions, and an [Invert] box for the line condition appears.
Basic-mode values carry over as regular expressions
The first time you turn on advanced mode, the values from basic mode are filled in as equivalent regular expressions.
| Basic mode | Filled into advanced mode |
|---|---|
((OR)) | ^\(\(OR\)\) |
{...} | \{[\s\S]*?\} |
Starting from an empty box, it’s hard to know how to write the expression.
See how the same condition looks as a regular expression and edit from there.
The two modes have separate boxes, so switching back and forth keeps what you wrote in each.
Line condition
Each line is tested against the expression, and only matching lines are counted.
Unlike basic mode, the whole matching line is counted.
An expression can match anywhere in the line, so removing the matched part could throw away characters you meant to count.
Pick lines with [一-鿿]{5,} (five or more Hanja in a row), for instance, and leaving out those five would be wrong.
| Expression | Lines picked |
|---|---|
^\s*\(\(OR\)\) | Lines starting with ((OR)), even after spaces |
^\(\((OR|原)\)\) | Lines starting with ((OR)) or ((原)) |
^\d+\. | Numbered lines such as 1. 2. |
Invert
With [Invert] on, lines that do not match are counted. It’s grep’s -v.^\(\(TR\)\) with Invert counts everything except the translation lines.^$ with Invert counts everything except empty lines. An empty paragraph takes a manuscript line too, so this is handy for counting sheets without the blank lines.
Removal
Everything matching the expression is removed before counting.
Unlike basic-mode brackets, it runs over the whole text, and ^ $ match the start and end of each line.
| Expression | What gets removed |
|---|---|
\{[^}]*\}|\[[^\]]*\] | {...} and [...] together |
^#.*$ | Contents of comment lines starting with # |
<[^>]+> | HTML tags |
Line breaks inside a removed part are kept, as in basic mode.
A regular expression can’t pair nested brackets, nor find unmatched ones.
If removing brackets is the goal, basic mode is safer.
Syntax
These are JavaScript regular expressions with the u flag on.
So you can write ranges of Extension B Hanja and beyond, like [\u{20000}-\u{2A6DF}], and a single . matches one such character.
The u flag is strict, though: even a needless escape like \- is an error.
If the syntax is wrong, the error message tells you where.
Characters by Script
What counts as one character
One visible character counts as one.
That sounds obvious, but inside a computer it often isn’t.
| What you see | How it’s stored | Count |
|---|---|---|
ᄒᆞᆫ (Old Hangul) | Initial + medial + final, three jamo | 1 |
é | e + combining ´ | 1 |
👨👩👧 | Three emoji joined by joiner characters | 1 |
𠀀 (Extension B Hanja) | Two 16-bit units (a surrogate pair) | 1 |
Counting storage units would turn one Old Hangul syllable into three characters and the family emoji into five.
So the text is split into visible characters with the browser’s built-in segmenter (Intl.Segmenter).
Line breaks are not counted.
Spaces and tabs count as spaces and are left out of “Without spaces”.
Categories
| Category | What it counts |
|---|---|
| Hangul | Modern syllables, jamo (compatibility jamo ㄱ ㅏ included), Old Hangul conjoining jamo, tone marks, Hanyang PUA Old Hangul |
| Hanja | Unicode Hanja blocks (see “Hanja” below) |
| Kana | Hiragana, Katakana |
| English & Latin | English letters, other Roman-alphabet letters such as é ü ŏ, full-width A. A letter alone can’t tell English from French, so they are counted together |
| Cyrillic | Cyrillic letters, as in Russian |
| Digits | 0–9, full-width 1 and other decimal digits |
| Punctuation & symbols | Punctuation such as . , 「」 · …, symbols such as ㉠ ※, emoji |
| Spaces | Spaces, tabs, ideographic spaces |
| Other | Anything else: Gugyeol, Arabic, Thai and so on |
Circled ㉠ and parenthesized ㈀ belong to the Hangul script in Unicode, but they are numbering symbols rather than text, so they count as punctuation & symbols.
Old Hangul
Old Hangul is stored differently from file to file.
Both ways count as Hangul here.
Unicode conjoining jamo
The current standard.
Initial, medial and final jamo are written in a row to build a syllable.
| Block | Range |
|---|---|
| Hangul Jamo | U+1100–U+11FF |
| Hangul Jamo Extended-A | U+A960–U+A97F |
| Hangul Jamo Extended-B | U+D7B0–U+D7FF |
| Tone marks | U+302E, U+302F |
A run of jamo and the tone mark after it make one syllable, one character.
Hanyang PUA
The scheme the Hangul word processor used before Hangul 2010.
It put each Old Hangul syllable into Unicode’s Private Use Area (PUA).
Files converted from older documents still carry plenty of it.
| Part | Range | Count |
|---|---|---|
| Precomposed syllables | U+E0BC–U+EFFF, U+F100–U+F66E | One each |
| Combining jamo | Initial U+F784–U+F800, medial U+F806–U+F864, final U+F86A–U+F8F7 | A run of initial + medial (+ final) is one |
Gugyeol (U+F67E–U+F77C) sits in the same area but isn’t Hangul, so it counts as Other.
The blank-character part (U+E000–U+E0BB) and the Wingdings part (U+F000–U+F0FF) are Other too.
What a PUA code means depends on the font; the same code can be an entirely different character elsewhere.
That’s why PUA characters outside the Hanyang Old Hangul parts count as Other.
To move Old Hangul to the standard form, Broken Hangul Fixer converts Hanyang PUA to conjoining jamo.
Hanja
Only characters in the Unicode Hanja blocks count.
| Block | Range |
|---|---|
| CJK Unified Ideographs | U+4E00–U+9FFF |
| Extension A | U+3400–U+4DBF |
| Extensions B–J | U+20000–U+3347F (gaps between blocks excluded) |
| Compatibility Ideographs | U+F900–U+FAFF |
| Compatibility Ideographs Supplement | U+2F800–U+2FA1F |
These look like Hanja but are not counted as Hanja.
〇(U+3007),々(U+3005) — they live in a symbols block, not a Hanja block. They count as Other.- Radicals such as
⺈⼈— symbols standing for radicals, not characters. They count as punctuation & symbols by default; an option moves them to Hanja (below). - Ideographic variation selectors (IVS) after a Hanja — just a mark choosing the glyph, so they form one character with the Hanja before them.
Count radicals as Hanja
With [Count radicals as Hanja] on, characters in the radical blocks count as Hanja too.
| Block | Range |
|---|---|
| CJK Radicals Supplement | U+2E80–U+2EFF |
| Kangxi Radicals | U+2F00–U+2FDF |
Convert an old document or extract text from a PDF, and the Kangxi radical ⼈ quite often turns up where the ordinary 人 should be.
They look identical, so the Hanja count of such a text comes out short.
Turn the option on and the missing characters are counted too.
Hanja in the result includes the radicals, and Radicals among them shows how many there were.
A non-zero radical count is also a sign that radical characters have crept into your text.
Extension B and beyond count as one character
A JavaScript string’s length is not a character count.
It’s the number of 16-bit units.
Most Hanja fit in one 16-bit unit, but Extension B and beyond lie above U+FFFF and take two (a surrogate pair).
'天'.length → 1
'𠀀'.length → 2
Count with length, or walk the string one unit at a time, and such a Hanja counts as two, or gets split in half and isn’t recognized at all.
Older texts use more extension-area Hanja, so the difference matters.
This tool reads character by character and counts every Hanja, whatever its area, as exactly one.
200-Character Manuscript Sheets
Matched to the Hangul word processor
Manuscript sheets are often estimated as “characters with spaces ÷ 200”.
But real manuscript paper starts a new line for each paragraph, so the more short paragraphs a text has, the more sheets it needs than the estimate.
For dialogue-heavy fiction or poetry the gap is large.
That’s why the count is matched to a common yardstick: the Hangul word processor’s document statistics (File → Document Info → Statistics).
Hancom’s help only says it “assumes the document is copied onto 200-character manuscript paper”; there’s no formula.
So we opened 40 test files, each isolating one rule, in the Hangul word processor, measured the sheets, and tuned the rules until every number matched.
The rules
| Rule | Example |
|---|---|
| A sheet is 20 cells × 10 lines | |
| Each paragraph starts on a new line; leftover cells at the end of a line are dropped | 30 one-character paragraphs → 30 lines → 3 sheets |
| No indent in the first cell of a paragraph | 200 × 가 in one paragraph → 10 lines → 1 sheet |
| An empty paragraph is one line | |
| Spaces, tabs, Hangul, Hanja, uppercase letters and most punctuation take one cell | |
| Lowercase letters, digits, periods and hyphens: two neighbors share a cell | 2026 → 2 cells, 가a가 → 3 cells |
| Commas take no cell | |
| A period at the start of a line moves to the end of the previous line (one cell per line at most) | Question marks, quotes, brackets and 。 do not |
| Sheets = lines ÷ 10, rounded | 14 lines → 1 sheet, 15 lines → 2 sheets |
Note the rounding.
A short text of 4 lines shows 0 sheets.
The Hangul word processor shows it that way too, so we followed it.
For a text shorter than a sheet, the character count is the better measure.
Sheets without spaces
Some places reckon fees or length without spaces, so a sheet count without spaces is shown as well.
Spaces and tabs are removed from each paragraph, and the result is laid out on manuscript paper with the same rules.
Paragraph breaks stay, so a text with many short paragraphs doesn’t lose many sheets this way.
The Hangul word processor has no such figure; when comparing with its numbers, use the count with spaces.
Where it differs from the by-script counts
Following the Hangul word processor means the sheets use a different yardstick in a few places.
- Old Hangul conjoining jamo take one cell each.
ᄒᆞᆫis 1 character but 3 cells. A tone mark takes a cell too. - A paragraph made only of Hanyang PUA or Gugyeol counts as empty. Mixed with other characters, each takes one cell.
- Extension B Hanja and beyond take one cell, the same as in the character counts.
What this tool doesn’t cover
The Hangul word processor leaves text in tables and text boxes, and pictures, out of the sheet count, while including headers, footers and footnotes.
This tool counts only the text you paste, so the numbers may differ depending on what came along when you copied from a Hangul document.
Punctuation we didn’t test (exclamation marks, colons, corner brackets and so on) and tabs take one cell.
Manuscript conventions also vary a little between publishers and contests, so follow your recipient’s rules if they have their own.
Good to Know
Order of processing
With both options on, things happen in this order.
- Bracketed ranges are removed.
- Lines are picked by the marker.
- The picked lines are counted: Hanja, characters by script, manuscript sheets. Each picked line is one manuscript paragraph.
Brackets go first because a bracketed range can span several lines.
Line breaks inside the brackets are kept.
That way the line layout stays as it was, and the line after a bracket doesn’t get glued onto the one before.
((OR))學而{a note that
spans two lines}時習之
((OR))不亦說乎
After removing the brackets, this becomes three lines: ((OR))學而 / 時習之 / ((OR))不亦說乎.時習之 doesn’t start with ((OR)), so it isn’t counted.
Nested and unmatched brackets
When the opening and closing marks differ, nested brackets are paired.
In {가{나}다} the whole outer bracket is removed.
When the marks are the same (like |...|), they open and close in the order they appear.
Unmatched brackets stop the count with an error.
- An unclosed bracket —
'{' never closed - A closing bracket with no opening one —
'}' has no opening bracket
If a closing bracket is missing, everything to the end of the text would silently vanish from the count.
The number would look plausible but be wrong.
Rather than quietly hand you a wrong number, we chose to tell you where the problem is.
Fixing them right in the error window
In a long manuscript, “line 3129” alone doesn’t get you there.
So when this happens, every unmatched bracket is gathered into a window.
- For each line, the text around the bracket is shown with the bracket highlighted, and below it a box for editing the whole line.
- What you edit there is applied to the input box as you go. When you’re done, click [Count Again].
- If any remain, the window comes back. If not, the result appears.
- With many errors, only the list inside the window scrolls; [Close] and [Count Again] stay in view at the bottom.
- Up to 200 are shown at a time. If there are more, the rest appear when you count again after fixing.
When another line needs the fix
With an unclosed bracket, the real slip may not be at that spot.
The closing bracket may have been dropped, or mistyped, somewhere after it.
Brackets can span lines, so the break may even be on the next line.
The window only lets you edit the lines with errors, so in that case note the surrounding text shown there, close the window, and make the fix in the input box.
Hanja normalization is applied too
Before counting, Hanja normalization is applied to both the text and the option strings.
It maps compatibility Hanja — same shape, different code — to their standard codes.
One character becomes one character, so the counts don’t change.
What it does prevent is a marker or bracket containing Hanja failing to match the text because the codes differ.
While the normalization data is loading, the [Count] button stays disabled for a moment.
Files and speed
The Upload button loads a text file.
Supported formats: .txt, .md, .html, .xml, .tsv, .csv
Everything happens in your browser; the text is never sent anywhere.
About 1.7 million characters are counted in under a second.
Counting runs in a separate workspace (a Web Worker), apart from the page.
Some regular expressions can make the count run on forever — ^(a+)+$ is the classic example.
So if counting doesn’t finish within 10 seconds, it stops and tells you.
The page never freezes in the meantime, and a “Counting…” indicator appears if it takes more than a moment.
Tools that go well with this one
- Broken Hangul Fixer — converts between Hanyang PUA Old Hangul and conjoining jamo, and restores split-jamo Hangul.
- Hanja Normalization — unifies multi-code Hanja into standard Hanja, cleaning up the document itself.
- Index Extractor — pulls out index terms wrapped in set markers and sorts them without duplicates.
- Tag Converter — shows tags in classical texts by color and size to help proofreading.
Open the app — Character Counter