文本字符数统计 在线工具核心JS实现

这篇只讲功能层 JavaScript。这个工具的实现方式很直接:用户输入文本后,触发统一统计函数,一次计算出全部指标,再把结果绑定到界面。

在线工具网址:see-tool.com/text-charac...

工具截图:

1. 核心状态

先定义输入文本、统计结果、字符频率三个响应式状态:

javascript 复制代码
import { ref } from 'vue'

const inputText = ref('')

const stats = ref({
  lines: 0,
  chars: 0,
  chinese: 0,
  english: 0,
  words: 0,
  numbers: 0,
  symbols: 0,
  displayLength: 0,
  fileSize: 0,
  charsNoSpace: 0,
  sentences: 0,
  paragraphs: 0,
  avgWordLen: '0',
  readTime: 0
})

const charFrequency = ref([])

2. 统一统计入口函数

输入框 @input 直接调用 updateStatistics,所有统计都在这个函数里完成:

javascript 复制代码
const updateStatistics = () => {
  if (!process.client) return

  const text = inputText.value

  // 行数
  const lines = text ? text.split('\n') : []
  stats.value.lines = lines.length

  // 总字符数
  stats.value.chars = text.length

  // 中文字符数
  const chineseChars = text.match(/[\u4e00-\u9fff]/g) || []
  stats.value.chinese = chineseChars.length

  // 英文字符数
  const englishChars = text.match(/[a-zA-Z]/g) || []
  stats.value.english = englishChars.length

  // 单词数
  const words = text.trim() ? text.trim().split(/\s+/).filter(w => w.length > 0) : []
  stats.value.words = words.length

  // 符号数(排除字母、数字、下划线、空白、中日韩统一表意文字)
  const symbols = text.match(/[^\w\s\u4e00-\u9fff]/g) || []
  stats.value.symbols = symbols.length

  // 数字数
  const numbers = text.match(/[0-9]/g) || []
  stats.value.numbers = numbers.length

  // 显示长度:中文记 2,其它记 1
  let displayLength = 0
  for (const char of text) {
    if (/[\u4e00-\u9fff]/.test(char)) {
      displayLength += 2
    } else {
      displayLength += 1
    }
  }
  stats.value.displayLength = displayLength

  // UTF-8 字节大小
  stats.value.fileSize = new Blob([text]).size

  // 去空白字符数
  stats.value.charsNoSpace = text.replace(/\s/g, '').length

  // 句子数
  const sentences = text.split(/[.!?。!?]+/).filter(s => s.trim().length > 0)
  stats.value.sentences = sentences.length

  // 段落数(空行分隔)
  const paragraphs = text.split(/\n\s*\n/).filter(p => p.trim().length > 0)
  stats.value.paragraphs = paragraphs.length

  // 平均词长
  const avgWordLen = words.length > 0
    ? (words.reduce((sum, w) => sum + w.length, 0) / words.length).toFixed(1)
    : '0'
  stats.value.avgWordLen = avgWordLen

  // 阅读时长(200词/分钟)
  const readTime = Math.ceil(words.length / 200)
  stats.value.readTime = readTime

  updateCharFrequency(text)
}

这段代码把基础统计和派生统计放在同一流程,输入变化时只跑一次主函数,逻辑清晰。

3. 字符频率分析

字符频率的做法是遍历全文,过滤空白,统一小写,最后排序截断:

javascript 复制代码
const updateCharFrequency = (text) => {
  if (!text.trim()) {
    charFrequency.value = []
    return
  }

  const freq = {}
  for (const char of text) {
    if (/\S/.test(char)) {
      const lower = char.toLowerCase()
      freq[lower] = (freq[lower] || 0) + 1
    }
  }

  const sorted = Object.entries(freq)
    .sort((a, b) => b[1] - a[1])
    .slice(0, 20)
    .map(([char, count]) => ({ char, count }))

  charFrequency.value = sorted
}

这里 Aa 会被合并统计,更符合实际阅读习惯。

4. 复制统计结果

复制逻辑分两层:优先使用 Clipboard API,不可用时自动降级:

javascript 复制代码
const formatNumber = (num) => num.toLocaleString()

const copyStats = async (t, MessagePlugin) => {
  if (!process.client) return

  const statsText = [
    t('textCharacterCount.statsResult'),
    '==================',
    `${t('textCharacterCount.lines')}: ${formatNumber(stats.value.lines)}`,
    `${t('textCharacterCount.chars')}: ${formatNumber(stats.value.chars)}`,
    `${t('textCharacterCount.chinese')}: ${formatNumber(stats.value.chinese)}`,
    `${t('textCharacterCount.english')}: ${formatNumber(stats.value.english)}`,
    `${t('textCharacterCount.words')}: ${formatNumber(stats.value.words)}`,
    `${t('textCharacterCount.numbers')}: ${formatNumber(stats.value.numbers)}`,
    `${t('textCharacterCount.symbols')}: ${formatNumber(stats.value.symbols)}`,
    `${t('textCharacterCount.displayLength')}: ${formatNumber(stats.value.displayLength)} (${t('textCharacterCount.displayLengthHint')})`,
    `${t('textCharacterCount.fileSize')}: ${formatNumber(stats.value.fileSize)} ${t('textCharacterCount.bytes')}`,
    `${t('textCharacterCount.charsNoSpace')}: ${formatNumber(stats.value.charsNoSpace)}`,
    `${t('textCharacterCount.sentences')}: ${formatNumber(stats.value.sentences)}`,
    `${t('textCharacterCount.paragraphs')}: ${formatNumber(stats.value.paragraphs)}`,
    `${t('textCharacterCount.avgWordLen')}: ${stats.value.avgWordLen}`,
    `${t('textCharacterCount.readTime')}: ${stats.value.readTime} ${t('textCharacterCount.minutes')}`
  ].join('\n')

  try {
    await navigator.clipboard.writeText(statsText)
    MessagePlugin.success(t('textCharacterCount.messages.copySuccess'))
  } catch {
    const textarea = document.createElement('textarea')
    textarea.value = statsText
    textarea.style.position = 'fixed'
    textarea.style.opacity = '0'
    document.body.appendChild(textarea)
    textarea.select()

    try {
      document.execCommand('copy')
      MessagePlugin.success(t('textCharacterCount.messages.copySuccess'))
    } catch {
      MessagePlugin.error(t('textCharacterCount.messages.copyFailed'))
    }

    document.body.removeChild(textarea)
  }
}

5. 示例文本与清空

两个动作都很轻:改值 + 重新统计。

javascript 复制代码
const loadSample = (t, MessagePlugin) => {
  const sampleText = `Hello World! 这是一个文本统计分析的示例。

这个工具可以统计文本中的字符数、单词数、行数、句子数和段落数。
同时还能分析字符频率,帮助您了解文本的构成特点。`

  inputText.value = sampleText
  updateStatistics()
  MessagePlugin.success(t('textCharacterCount.messages.sampleLoaded'))
}

const clearText = (t, MessagePlugin) => {
  inputText.value = ''
  updateStatistics()
  MessagePlugin.info(t('textCharacterCount.messages.textCleared'))
}

6. 触发方式

输入区直接绑定:

html 复制代码
<textarea v-model="inputText" @input="updateStatistics"></textarea>

这种绑定方式保证了"输入即统计",不需要额外点击计算按钮。

相关推荐
十二7402 小时前
前端缓存踩坑实录:从版本号管理到自动化构建
前端·javascript·nginx
over6972 小时前
从 URL 输入到页面展示:一次完整的 Web 导航之旅
前端·面试·架构
Giant1002 小时前
TypeScript 核心知识点(覆盖 90% 开发场景)
前端
暴走的小呆2 小时前
为什么react要从顶层更新
前端
Fisschl2 小时前
在 Vue 中使用 remark 渲染 markdown
vue.js
仰望星空的小猴子2 小时前
React18和React19新特性
前端
小码哥_常2 小时前
Android新航标:Navigation 3为何成为变革先锋?
前端
SuperEugene2 小时前
Vue状态管理扫盲篇:状态管理中的常见坑 | 循环依赖、状态污染与调试技巧
前端·vue.js·面试
骑着小黑马2 小时前
从 Electron 到 Tauri 2:我用 3.5MB 做了个音乐播放器
前端·vue.js·typescript