如何在富文本编辑器中统计字数
正在加载今日诗词....2023-03-09
调研对象
首先收集一下其他文档的字数统计规则:
wangEditor
- 数字 一个单位长度
- 中文 一个单位长度
- 中文符号 一个单位长度
- 英文单词 每个字母算一个单位长度
- 英文符号 一个单位长度
- 空格 一个单位长度
即只要键盘输入就算一个单位长度,不区分符号、单词、中英文是否连续等
tinyMCE
- 数字 连续数字算一个单位长度,以空格隔开算
- 中文 一个单位长度
- 中文符号 不统计
- 英文单词 每个单词算一个单位长度,以空格隔开算
- 英文符号 不统计
- 空格 不统计
即只统计文字(连续的英文或数字),不统计符号、空格等
ckeditor
- 统计字符数:同 wangEditor 的计算方式
- 统计字数:不计算符号,输入以空格区分计算字数
飞书文档
- 统计字符数:不算符号的所有输入
- 统计字数:中文(不包括符号)以个数计算,英文、数字等以空格计算
钉钉文档
统计字数:英文符号、空格不计算,中文和中文符号以个数计算,英文、数字等以空格计算
石墨文档
- 统计字符数:除空格所有输入
- 统计不计空格字符数:所有输入
- 统计字数:规则同钉钉文档
语雀文档
只统计字数:规则同钉钉文档
腾讯文档
- 统计字数:规则同钉钉文档
- 统计不计标点字数:除去标点之后的字数
- 统计字符数:规则同石墨文档
- 统计不计空格字符数:规则同石墨文档
企业微信文档
- 统计字数:规则同钉钉文档
- 统计不计标点字数:除去标点之后的字数
- 统计字符数:规则同石墨文档
- 统计不计空格字符数:规则同石墨文档
通过统计,可以看到基本都是按照钉钉文档的规则来,即英文符号、空格不计算,中文和中文符号以个数计算,英文、数字等以空格计算,那么我们也采用这种方式,接下来就是整理思路开始写代码了。
思路
中文计算方式:中文的计算方式通常是按照字符数进行统计,每个中文字符都计为一个字。 英文计算方式:英文的计算方式通常是按照单词数进行统计,一般会将单词中间的空格作为分隔符,以此来确定单词的数量。
- 获取富文本编辑器的文本,按照空格对文本进行分割;
- 分割完成后对于英文和其他,可以按照空格分隔单词进行计数,直接让数量加 1;
- 但对于中文,由于中文没有空格,因此需要对中文进行分词处理,例如使用中文分词工具将中文文本分成词语;
- 通过正则获取分词后中文的个数,对数量进行累加操作。
实现
tiptap 的插件中有character-count用于统计字符数和单词数,并允许设置文档最大长度。
将插件添加到项目中后,通过下面这行代码就可以拿到文档的单词数:
editor.storage.characterCount.words()
but, 通过这个插件拿到的单词数是根据空格分割的单词数量,即一串中文也显示的是一个单词数,这明显不符合我们的调研结果。修改源码中计算单词的方法words
为下面这个写法:
// 匹配中文 中文符号
const chineseSymbolPattern =
/[\u4E00-\u9FA5\u3000-\u303F\uFF00-\uFFEF\u2000-\u206F]/g
const delZeroWidth = (text: string) =>
text.replace(/[\u200b-\u200f\uFEFF\u202a-\u202e]/g, '')
const countWords = (text: string) => {
// 以空格分割
const words = text.split(/\s+/)
let count = 0
for (let i = 0; i < words.length; i++) {
if (delZeroWidth(words[i]) === '') continue
// 如果单词包含中文字符,则进行分词处理
if (chineseSymbolPattern.test(words[i])) {
const segments = segmentChinese(words[i])
segments.forEach((segment: string) => {
if (delZeroWidth(segment) !== '') {
// 中文个数
count += segment.match(chineseSymbolPattern)?.length || 0
// 分割中文 拿到其余的个数 如hello你you好
count += segment
.split(chineseSymbolPattern)
.filter((seg) => seg !== '')?.length
}
})
} else {
// 如果不是 则按照空格++
count++
}
}
return count
}
// 对中文文本进行分词处理
const segmentChinese = (text: string) =>
text.split(/([\u4E00-\u9FA5]+)/).filter((seg) => seg !== '')
/** 计算文档字数 */
const calcDocumentTextSize = (text: string) => {
const number = countWords(text)
return number
}
export default calcDocumentTextSize
最后测试了一下,与钉钉文档的字数统计大部分情况下相同,有时候会有几个到几十个字的差异,目前还没有发现原因,不过不影响,因为钉钉可能做了某些特殊的处理。
京ICP备2022027737号
Copyright © 2022 - present @wangxiang