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

  1. Paste the text into Input Text, or load a file.
  2. Turn on the options below if you need them.
  3. 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 writeWhat 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 modeFilled 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.

ExpressionLines 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.

ExpressionWhat 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 seeHow it’s storedCount
ᄒᆞᆫ (Old Hangul)Initial + medial + final, three jamo1
ée + combining ´1
👨‍👩‍👧Three emoji joined by joiner characters1
𠀀 (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

CategoryWhat it counts
HangulModern syllables, jamo (compatibility jamo ㄱ ㅏ included), Old Hangul conjoining jamo, tone marks, Hanyang PUA Old Hangul
HanjaUnicode Hanja blocks (see “Hanja” below)
KanaHiragana, Katakana
English & LatinEnglish letters, other Roman-alphabet letters such as é ü ŏ, full-width A. A letter alone can’t tell English from French, so they are counted together
CyrillicCyrillic letters, as in Russian
Digits0–9, full-width 1 and other decimal digits
Punctuation & symbolsPunctuation such as . , 「」 · …, symbols such as ㉠ ※, emoji
SpacesSpaces, tabs, ideographic spaces
OtherAnything 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.

BlockRange
Hangul JamoU+1100–U+11FF
Hangul Jamo Extended-AU+A960–U+A97F
Hangul Jamo Extended-BU+D7B0–U+D7FF
Tone marksU+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.

PartRangeCount
Precomposed syllablesU+E0BC–U+EFFF, U+F100–U+F66EOne each
Combining jamoInitial U+F784–U+F800, medial U+F806–U+F864, final U+F86A–U+F8F7A 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.

BlockRange
CJK Unified IdeographsU+4E00–U+9FFF
Extension AU+3400–U+4DBF
Extensions B–JU+20000–U+3347F (gaps between blocks excluded)
Compatibility IdeographsU+F900–U+FAFF
Compatibility Ideographs SupplementU+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.

BlockRange
CJK Radicals SupplementU+2E80–U+2EFF
Kangxi RadicalsU+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

RuleExample
A sheet is 20 cells × 10 lines
Each paragraph starts on a new line; leftover cells at the end of a line are dropped30 one-character paragraphs → 30 lines → 3 sheets
No indent in the first cell of a paragraph200 × 가 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 cell2026 → 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, rounded14 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.

  1. Bracketed ranges are removed.
  2. Lines are picked by the marker.
  3. 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