使用方法
- 文本框里已经写好了示例串
Hello 世界 😀 é è 𠮷 Liǔquán Tools,打开即出结果。粘贴自己的内容后点「开始统计」,或者按 Ctrl+Enter(Mac 上 Cmd+Enter)触发。 - 「口径」二选一:按码位统计时一个 emoji 算 1 个字符;按码元统计时按 UTF-16 码元切分,非 BMP 字符会拆成两个代理项,页面用 U+D842 这样的码位标注显示它们。
- 「忽略大小写」把 A 与 a 合并计数,显示的仍是第一次出现的那个字形;「忽略空格」把空格、制表符、换行、全角空格一并跳过。
- 「只统计某个字符」填一个字符就只看它:填「柳」会得到它在文本里出现几次;留空则统计全部。填了两个及以上字符会提示只能填一个。
- 「最多统计几种字符」默认 50,范围 1 到 500,用来给长文本封顶;「占比小数位」默认 2,范围 0 到 4。
- 结果区上面一行是四个总数:码位数、码元数、两者之差、去重字符种数。下面一段是频次清单,每行给出字符、次数、占比与它占几个码元。
- 清单下面是一张频次表,默认显示 12 行,点「加载更多」展开全部,点「导出 CSV」存成带字节序标记的文件;「复制结果」复制整段清单。
- 「复制对照表」复制正文里那张关键数字表,制表符分隔,可以直接粘进表格软件。
- 文本框留空、文本超过 100000 个码元、只统计框填了多个字符、上限不是 1 到 500 的整数,都会给出指明原因的提示。
这个统计字符出现次数工具能解决什么问题
最常见的一处对不上是:同一个字符串,不同地方数出来的长度不一样。示例串在 JavaScript 里 .length 给的是 32,而按字符数是 30,差 2。差的这 2 来自 😀(U+1F600)和 𠮷(U+20BB7)——它们都在基本多文种平面之外,一个字符要占两个 UTF-16 码元。数错了长度,前端的输入框字数限制、后端的字段长度校验、短信分条计费都会跟着错。
第二处对不上是同一个字形有两条不同的码位序列。é 可以是预组合的 U+00E9(1 个码位),也可以是 e 加组合附加符 U+0301(2 个码位)。两段看起来一模一样的文本,如果不做 Unicode 正规化就统计,会得到两个不同的字符数。本页照原样统计,不做正规化,为的就是把这种差异暴露出来。
第三类需求是快速核查:一段文本里空格出现 7 次、字母 l 出现 3 次、o 出现 3 次,这类数字在排版检查、密码强度自检、验证码文本核对里用得上。中文场景同样适用——「柳泉工具柳泉在线」码元 8、码位 8,其中柳 2 次、泉 2 次。
需要提醒的是边界:本页只回答「某个字符出现了几次」。把它当关键词密度用是错的,中文没有空格分词,单字频次对检索排序没有意义,真要的是词频。
计算原理与变量说明
页面只做三件事:切分、计数、排序。切分决定「一个字符是什么」,计数用一张以字符为键的映射表累加,排序按次数降序、次数相同时按第一次出现的位置升序。
| 变量 | 含义 | 示例串取值 |
|---|---|---|
| 码位数 | 按 Unicode 码位切分的字符数 | 30 |
| 码元数 | 按 UTF-16 码元切分的单元数 | 32 |
| 差值 | 码元数减码位数,等于非 BMP 字符的个数 | 2 |
| 去重种数 | 不同字符的个数 | 20 |
| 占比 | 该字符次数 ÷ 参与统计的单元数 | 空格 23.33% |
.length 数的是 UTF-16 码元,不是字符。JavaScript 的字符串内部是 UTF-16 序列,.length 返回的是码元个数。所有非 BMP 字符——emoji、罕见汉字、古文字、部分音乐符号——都会让这个数字偏大,每出现一个就多算 1。要拿字符数,必须按码位迭代。
字素簇不等于码位。👨👩👧 这个家庭 emoji 由 5 个码位组成(三个人物各 1 个、中间两个零宽连接符 ZWJ),占 8 个码元,但人眼看到的是 1 个「字」,也就是 1 个字素簇。🇨🇳 国旗是 2 个区域指示符码位、4 个码元、1 个字素簇。按码位统计 emoji 会数出好几倍,这是「数字符」与「数字」的根本分歧。
两种口径下的占比分母不同。切到码元口径时,参与统计的单元数从 30 变成 32,同一个字符的占比会被摊薄;非 BMP 字符还会被拆成两个代理项,各自单独计数。页面把它显示为 U+D83D、U+DE00 这样的码位标注,而不是裸字符,因为单独一个代理项并不是合法文本。
忽略大小写用的是简单小写映射。它把 A 与 a 归一、把 É 与 é 归一,显示的仍是首次出现的字形。它不做土耳其语 dotless i 这类区域特化映射,也不做正规化,所以 é 的两种写法在这一页里仍然是两个不同的字符。
完整示例(数值仅作演示)
打开页面时的默认结果:口径为码位,码位数 30、码元数 32、相差 2,去重 20 种。频次最高的三个是空格 7 次(23.33%)、字母 l 3 次(10.00%)、字母 o 3 次(10.00%),其余字符各 1 次,其中 H、e、世、界、😀、é、è、𠮷、L 都在示例串里只出现一次。
切到码元口径后,总数变成 32,😀 与 𠮷 各自被拆成两个代理项:😀 变成 U+D83D 与 U+DE00,𠮷 变成 U+D842 与 U+DFB7,四个代理项各计 1 次。这时「字符」这一列里的 U+ 开头条目就是代理项,它们不是合法字符,只是 UTF-16 的存储单元。
只统计某个字符:在「只统计某个字符」框里填「世」,结果只留一行——世 1 次、3.33%、占 1 个码元。换成中文短串「柳泉工具柳泉在线」,码元 8、码位 8,柳 2 次、泉 2 次,其余各 1 次。
勾选「忽略空格」后空格那一行消失,总数从 30 降到 23,其余字符的占比相应上升;勾选「忽略大小写」后 l 与 L 合并为 4 次(串里没有大写 O,o 仍是 3 次)。两个开关可以同时开。
字符计数对照表
第一张表是示例串的关键数字,「复制对照表」复制的就是它。
| 口径 | 数值 | 说明 |
|---|---|---|
| UTF-16 码元数 | 32 | 字符串 .length 的返回值 |
| Unicode 码位数 | 30 | 按码位迭代的结果 |
| 两者相差 | 2 | 😀 与 𠮷 各多占 1 个码元 |
| 空格出现次数 | 7 | 占比 23.33% |
| l 出现次数 | 3 | 忽略大小写后与 L 合并为 4 |
| o 出现次数 | 3 | 占比 10.00% |
| H 出现次数 | 1 | 占比 3.33% |
| 😀 码位 / 码元 | 1 / 2 | U+1F600,非 BMP |
| 𠮷 码位 / 码元 | 1 / 2 | U+20BB7,非 BMP |
| 👨👩👧 码位 / 码元 / 字素簇 | 5 / 8 / 1 | 两个 ZWJ 连接三个人物 |
| 🇨🇳 码位 / 字素簇 | 2 / 1 | 两个区域指示符 |
| 柳(柳泉工具柳泉在线) | 2 | 全串码元 8、码位 8 |
| 泉(柳泉工具柳泉在线) | 2 | 全串码元 8、码位 8 |
第二张表说明同一个 é 的两条码位序列。两段文本渲染出来完全一样,字符数却不同,这是文本统计里最常见的对不上。
| 写法 | 码位序列 | 码位数 | 码元数 |
|---|---|---|---|
| 预组合 é(NFC) | U+00E9 | 1 | 1 |
| 分解 é(NFD) | U+0065 U+0301 | 2 | 2 |
| 家庭 emoji 👨👩👧 | U+1F468 U+200D U+1F469 U+200D U+1F467 | 5 | 8 |
| 国旗 🇨🇳 | U+1F1E8 U+1F1F3 | 2 | 4 |
读法:第一张看两种口径的差距从哪来,第二张看同一个字形的两种编码。两者都不是 bug,是 Unicode 的设计。
结果边界、舍入与适用说明
- 文本框留空时提示请粘贴内容,不会拿上一次的结果冒充本次输出;清空后点「开始统计」会自动把示例串填回去。
- 文本超过 100000 个码元时提示具体长度并请分段粘贴,避免整页卡住。
- 「只统计某个字符」填了两个及以上字符时提示它只能填一个;填的字符不在文本里时给出 0 次的结果并注明未出现,不算错误。
- 「最多统计几种字符」留空、写成小数或超出 1 到 500 时分别给出对应提示;「占比小数位」超出 0 到 4 同理。
- 占比四舍五入到指定小数位,各字符占比之和可能不等于 100%,这是舍入的正常结果,不是漏统计;「忽略空格」会把空格从分母里剔除,因此空格那一栏消失后其余占比会相应上升。
- 码元口径下非 BMP 字符被拆成两个代理项,显示为 U+ 开头的码位标注;单独一个代理项不是合法字符,不能粘回文本里用。
- 本页不做 Unicode 正规化,NFC 与 NFD 两种写法按不同字符计数;需要先统一编码的请先做正规化再粘贴进来。
- 字素簇(人眼看到的一个「字」)不在本页统计范围内,页面给出的 5 / 8 / 1 只是说明它与码位不是一回事。
- 本页只数字符,不分词;需要的词频请找对应的词频工具。
常见问题
为什么两个地方数出来的长度不一样?
因为一个按 UTF-16 码元数、一个按 Unicode 码位数。示例串是 32 与 30,差 2,来自 😀(U+1F600)与 𠮷(U+20BB7)这两个非 BMP 字符,它们各占两个码元。
é 明明是一个字,为什么可能数成 2?
它可以是预组合的 U+00E9,也可以是 e 加组合附加符 U+0301。两种写法渲染一样但码位数不同,本页不做正规化,所以如实数成两种。
家庭 emoji 为什么一个人算出 5 个字符?
👨👩👧 由三个人物码位加两个零宽连接符组成,共 5 个码位、8 个码元,但字素簇只有 1 个。按码位统计 emoji 会数出好几倍。
码元口径下那些 U+D83D 是什么?
是 UTF-16 的代理项。非 BMP 字符在 UTF-16 里存成高低两个代理项,单独拿出来并不是合法字符,页面用码位标注显示它们。
忽略大小写会不会把 é 和 É 合并?
会,简单小写映射把它们归一,显示首次出现的字形。但它不做区域特化映射,也不做正规化,é 的两种编码写法仍是两个字符。
占比加起来不到 100% 是漏了吗?
不是。占比四舍五入到指定小数位,各行相加会有微小出入;把小数位调到 4 可以看出差别。
能拿它算关键词密度吗?
不能。中文没有空格分词,单字频次对检索排序没有意义;本页只回答某个字符出现几次,词频请用专门的工具。
粘贴的文本会上传到服务器吗?
不会。文本只在本页的脚本里参与切分与计数,没有发起任何网络请求,也没有写入本地存储。