这个工具看起来简单,实际开发时我先做了一件事:不急着写界面,先把“提取逻辑”做好。因为用户输入可能会非常杂,可能一段话里同时有中文、英文、数字、空格和符号,如果核心函数不稳,前端交互再漂亮也没用。
在线工具网址:see-tool.com/text-number…
工具截图:
我最后把功能拆成两块:
extractTextNumbers:只负责提取。countChars:只负责统计。
页面层只做参数传递和按钮动作,避免把业务细节塞进组件里。
先说提取函数是怎么定下来的
函数签名是这样:
export function extractTextNumbers(
text,
extractChinese = true,
extractEnglish = true,
extractNumbers = true,
extractByLine = false,
removeDuplicates = false,
sortChars = false
) {
if (!text || !text.trim()) return ''
}
这几个布尔值其实对应页面上的勾选项。这样有个好处:页面不需要做复杂映射,用户怎么选,函数就怎么跑。
我一开始想过用一条“大正则”把三种字符一次性提取完,但后来放弃了。原因很简单:可读性太差,后面再加规则会麻烦。所以改成逐字符判断,逻辑更长一点,但直观。
逐字符提取,比花哨写法更稳
核心循环没有技巧,就是老老实实遍历:
for (let i = 0; i < line.length; i++) {
const char = line[i]
if (extractChinese && /[\u4e00-\u9fff]/.test(char)) {
lineResult += char
} else if (extractEnglish && /[a-zA-Z]/.test(char)) {
lineResult += char
} else if (extractNumbers && /[0-9]/.test(char)) {
lineResult += char
}
}
这里我保留了 if / else if 结构,目的就是“一个字符只走一个分支”。这能减少边界行为,输出顺序也和原文一致,用户更容易理解结果。
按行提取和整体提取,走两条分支
extractByLine 打开时,流程是:先按换行切分,再逐行处理,最后把每行结果再拼回去。
const lines = text.split('\n')
const processedLines = []
// 每一行独立提取
return processedLines.join('\n')
这个模式很实用。比如用户在整理多行数据时,通常希望“第几行对第几行”,不希望全部揉成一串。
关闭按行模式时就简单了,直接整段提取,适合一次性清洗长文本。
去重和排序,顺序不能乱
提取完之后我把后处理固定成:先去重,再排序。
if (removeDuplicates) {
result = [...new Set(result)].join('')
}
if (sortChars) {
result = result.split('').sort().join('')
}
如果先排序再去重,结果也能对,但中间会多做无效工作,而且阅读代码时不如“先减量再整理”自然。
统计函数单独维护,页面就轻很多
统计逻辑我没有混进提取函数,而是单独写在 countChars。
- 普通模式:直接看长度。
- 按行模式:行数只算非空行,总字符数不含换行。
页面里通过 computed 串起来:输入一变,提取结果自动变;提取结果一变,统计自动变。用户看起来就是实时更新,代码上也比较干净。
按钮动作只做三件事
页面脚本里和结果相关的动作只有三个:复制、下载、清空。
- 复制:
navigator.clipboard.writeText(extractedText) - 下载:
Blob + URL.createObjectURL + a.click() - 清空:只改输入值,结果和统计由计算属性自动归零
到这里,这个工具的核心 JS 基本就闭环了:输入文本进来,规则提取,按需后处理,统计结果,再做复制或导出。没有绕太多层,但每个环节都能单独看懂、单独改动。