SMOG 公式 JavaScript 实现:文本可读性量化与自动化检测
简介smog-formula 是一份基于 1969 年 SMOGGobbledygook 的简单度量公式实现文本阅读难易程度检测的 JavaScript 工具包面向需要评估英文文本可读性等级的前端开发者、教育技术研究者与内容编辑。它通过统计句子数与多音节词数量快速换算出对应的阅读年级水平适用于教材分级、文章难度评估等场景。资源包共 12 个文件以 json 配置、js 源码、yml 工作流配置为主另含 md 说明、license 授权文件及 editorconfig、prettierignore 等工程规范文件压缩包整体约 6KB体积轻量、结构规整。该包仅支持 ESM需 Node 12 环境并以 import 方式引入核心导出 smogFormula 方法传入句子数与多音节词数即可得到可读性分值。目前已有 368 人学习下载适合希望快速集成可读性检测能力、了解 SMOG 公式工程化实现的读者参考使用。1. 文本可读性还能算出来SMOG 公式到底在测什么你可能遇到过这种场景辛辛苦苦写完一份产品说明、一段帮助文档甚至一篇技术博客发出去之后收到的反馈却是“看不懂”“太绕了”。问题出在哪是句子太长还是词太生僻靠感觉判断往往不靠谱。SMOG 公式就是干这个的——它用一套固定的计算规则把一段文本的阅读难度换算成一个“年级水平”数值。SMOG 是 Simple Measure of Gobbledygook 的缩写1969 年由 G. Harry McLaughlin 提出专门用来估算读者需要接受多少年教育才能顺畅理解一段文字。它最初用在医疗健康材料领域后来被教育、出版、政府文档等场景广泛采用。这个资源是一个 JavaScript 实现的 SMOG 公式计算模块核心逻辑不复杂但把多音节词统计、句子切分、公式套用这几步串起来了。适合谁用写文档的、做内容审核的、搞教育产品的以及任何想给文本难度加一个量化指标的前端或 Node.js 开发者。2. SMOG 公式的计算原理与 JavaScript 实现拆解2.1 公式本身从多音节词到 SMOG 指数SMOG 公式的核心变量只有一个多音节词的数量。所谓多音节词在英文语境下通常指包含三个或以上音节的词。公式的标准形式是SMOG 1.0430 × √(多音节词数 × (30 / 句子数)) 3.1291这个公式的直觉是一段文本里多音节词越密集读者需要的教育年限就越高。乘以 30/句子数 是为了把结果归一化到“每 30 个句子”的基准上这样不同长度的文本之间可以横向比较。最后加上的 3.1291 是经验修正项让结果更贴近实际年级水平。注意SMOG 公式对句子数有最低要求——如果句子数少于 30计算结果会偏乐观通常建议文本至少包含 30 个句子再使用。这也是为什么很多实现会在句子数不足时给出警告或直接拒绝计算。2.2 多音节词怎么数音节切分的工程近似音节切分是 SMOG 实现里最容易翻车的地方。英语音节没有绝对精确的规则常见做法是用启发式规则近似。我一般会按这个顺序处理先把单词转小写去掉首尾非字母字符然后统计元音字母组a、e、i、o、u、y的连续段数量再减去末尾不发音的 e最后处理一些例外比如 “es”“ed” 结尾不额外计音节的情况。下面是一个可复用的音节计数函数function countSyllables(word) { // 转小写并去掉非字母字符 word word.toLowerCase().replace(/[^a-z]/g, ); if (word.length 3) return 1; // 短词直接算一个音节 // 去掉末尾不发音的 e word word.replace(/(?:[^laeiouy]es|ed|[^laeiouy]e)$/, ); word word.replace(/^y/, ); // 词首 y 不算元音 // 匹配连续元音组 const matches word.match(/[aeiouy]{1,2}/g); return matches ? matches.length : 1; }逻辑说明前三行做预处理把干扰字符清掉。word.length 3是一个经验阈值像 “the”“and” 这种短词直接返回 1避免规则误判。两个replace分别处理末尾 e 和词首 y 的情况。最后的正则[aeiouy]{1,2}把连续元音当作一个音节单位比如 “beau” 里的 “eau” 算一个。参数方面如果你处理的文本里有大量专有名词或缩写建议在调用前先做一次白名单过滤否则音节数会偏高。2.3 句子切分与完整计算流程句子切分比音节切分简单但也不能随便按句号 split。常见做法是用正则匹配句末标点同时排除小数点和缩写点。下面是一个完整的 SMOG 计算函数function calculateSMOG(text) { // 按句末标点切分排除小数点和常见缩写 const sentences text .replace(/([.?!])\s*(?[A-Z])/g, $1|) .split(|) .filter(s s.trim().length 0); const sentenceCount sentences.length; if (sentenceCount 30) { console.warn(句子数少于 30SMOG 结果可能不准确); } // 提取所有单词 const words text.toLowerCase().match(/[a-z]/g) || []; // 统计多音节词三个及以上音节 const polySyllables words.filter(w countSyllables(w) 3).length; // 套用 SMOG 公式 const smog 1.0430 * Math.sqrt(polySyllables * (30 / sentenceCount)) 3.1291; return { smogIndex: Math.round(smog * 10) / 10, sentenceCount, polySyllableCount: polySyllables, wordCount: words.length }; }逻辑说明句子切分用了一个替换技巧先把句末标点后面跟大写字母的位置插入分隔符再按分隔符切分这样能避开 “3.14” 这种小数点。sentenceCount 30的警告是必须的否则用户会拿到一个看起来合理但实际偏差很大的数值。多音节词统计调用了前面的countSyllables阈值设为 3。最后公式里的常数 1.0430 和 3.1291 是原公式的固定系数不要改。返回结果里带上句子数、多音节词数和总词数方便排查。2.4 结果解读SMOG 指数对应什么阅读水平算出来的 SMOG 指数是一个数值通常按四舍五入取整后对应年级水平。下面这张表是常见的对照关系SMOG 指数对应年级水平典型读物5-6小学高年级儿童故事、简单说明7-8初中青少年小说、科普短文9-10高中新闻评论、一般技术文档11-12高中毕业学术入门、专业博客13-15大学学术论文、法律文书16研究生及以上专业期刊、技术标准注意这个对照是英文语境下的经验值。如果你拿它去测中文文本多音节词的定义完全不适用结果没有参考意义。SMOG 公式的设计对象是英文这一点在选型时要先确认。3. 把 SMOG 接进项目从安装到批量检测的完整操作3.1 环境准备与模块引入这个资源是一个 JavaScript 模块可以在 Node.js 环境或浏览器端使用。如果你用 npm 管理依赖常见做法是直接安装到项目里如果只是临时测一段文本也可以把核心函数复制到本地文件里跑。我一般会先建一个独立目录避免污染现有项目mkdir smog-test cd smog-test npm init -y npm install smog-formula安装完成后在脚本里引入const smog require(smog-formula); // 或者 ES Module 方式 // import smog from smog-formula;参数说明npm init -y生成默认的 package.json省去交互。npm install smog-formula会把模块装进 node_modules。如果你的项目用的是 pnpm 或 yarn把 install 命令替换成对应工具的写法即可模块本身不依赖特定包管理器。3.2 单段文本检测最小可运行示例先拿一段英文文本跑通流程确认环境没问题const smog require(smog-formula); const sampleText The quick brown fox jumps over the lazy dog. This sentence is simple. However, the implementation of internationalization frameworks often requires significant consideration of multiple architectural perspectives. Developers must evaluate the trade-offs between performance and maintainability. ; const result smog({ text: sampleText, minSentences: 30 // 低于此值会触发警告 }); console.log(SMOG 指数:, result.smogIndex); console.log(句子数:, result.sentenceCount); console.log(多音节词数:, result.polySyllableCount);逻辑说明调用时传入文本和minSentences参数。minSentences默认是 30如果你明确知道文本较短且只想要一个粗略参考可以调低但结果的可信度会下降。输出里smogIndex是最终指数另外两个字段用于验证中间步骤是否合理。如果polySyllableCount明显偏高先检查文本里是不是混入了大量缩写或代码片段。3.3 批量检测遍历文件目录并输出报告实际项目里更常见的是批量检测一批文档。下面这个脚本会遍历指定目录下的所有.txt文件逐个计算 SMOG 指数并输出汇总const fs require(fs); const path require(path); const smog require(smog-formula); const targetDir ./docs; function analyzeDirectory(dir) { const files fs.readdirSync(dir).filter(f f.endsWith(.txt)); const report []; for (const file of files) { const content fs.readFileSync(path.join(dir, file), utf-8); const result smog({ text: content, minSentences: 30 }); report.push({ file, smogIndex: result.smogIndex, sentences: result.sentenceCount, polySyllables: result.polySyllableCount }); } // 按 SMOG 指数降序排列最难的排前面 report.sort((a, b) b.smogIndex - a.smogIndex); console.table(report); return report; } analyzeDirectory(targetDir);逻辑说明readdirSync同步读取目录适合文件数量不多的场景。每个文件独立调用 SMOG 计算结果推进report数组。最后按指数降序排列方便一眼看出哪些文档最难读。console.table在 Node.js 里能直接输出表格省去手动格式化。参数方面targetDir改成你的实际目录路径即可如果文件编码不是 UTF-8需要在readFileSync里指定对应编码。3.4 把结果写进 CI自动化可读性检查如果你想让可读性检查变成流程的一部分可以在 CI 脚本里加一个阈值判断。比如规定所有面向用户的文档 SMOG 指数不得超过 12超过就报错退出const MAX_SMOG 12; function checkReadability(report) { const violations report.filter(r r.smogIndex MAX_SMOG); if (violations.length 0) { console.error(以下文档可读性不达标); violations.forEach(v { console.error( ${v.file}: SMOG ${v.smogIndex} (上限 ${MAX_SMOG})); }); process.exit(1); } console.log(所有文档可读性检查通过); } // 接在 analyzeDirectory 之后调用 checkReadability(analyzeDirectory(targetDir));逻辑说明MAX_SMOG是阈值参数按你的目标读者群体调整。filter筛出超标的文档如果有就打印文件名和实际指数然后process.exit(1)让 CI 任务失败。这个模式可以直接嵌进 GitHub Actions、GitLab CI 或其他流水线里。注意阈值不要设得太死技术文档天然会比营销文案难按文档类型分开设阈值更合理。4. 避坑与排查SMOG 计算里最容易翻车的五个地方4.1 句子数不足 30 却直接采信结果现象一段只有五六句话的文本算出来 SMOG 指数是 8看起来挺合理但实际读者反馈还是难懂。原因SMOG 公式的归一化基准是 30 个句子句子数远低于 30 时多音节词数的微小波动会被放大结果不稳定。解决在代码里强制检查句子数低于 30 时要么拒绝输出要么在结果里附带明确的置信度警告。我一般会在返回对象里加一个reliable: sentenceCount 30字段让调用方自己决定怎么处理。4.2 音节计数把缩写和代码当成多音节词现象一段技术文档里包含 “API”“HTTP”“JSON” 这类缩写算出来的多音节词数异常高SMOG 指数虚高。原因音节计数函数按元音组来数缩写里没有标准元音结构容易被误判。解决在统计前先做一轮过滤把全大写且长度小于等于 5 的词、以及包含数字的词排除掉。常见做法是维护一个白名单或者用正则/\b[A-Z]{2,}\b/先标记出来再决定是否计入。4.3 句子切分把小数点和缩写点当成句末现象文本里有 “version 3.14” 或 “Dr. Smith” 这样的内容句子数被多算SMOG 指数偏低。原因简单的按句号切分无法区分句末点和非句末点。解决用更精细的正则要求句末标点后面跟空格和大写字母或者维护一个常见缩写列表Dr.、Mr.、etc.在切分前先做替换。上面 2.3 节里的切分逻辑已经处理了小数点后跟大写字母的情况但缩写列表需要按你的文本特点补充。4.4 拿 SMOG 去测中文或其他非英文文本现象把一段中文文本丢进去算出来一个数值但完全不知道对应什么水平。原因SMOG 公式的多音节词定义基于英文音节结构中文没有音节切分的概念公式的输入变量根本不成立。解决明确 SMOG 只适用于英文文本。如果你需要评估中文可读性得换用针对中文的指标比如基于字频、词频或句长的模型。这个资源本身不解决中文场景选型时要先确认这一点。4.5 忽略文本预处理导致结果波动现象同一篇文档两次运行结果不一样。原因文本里混入了 Markdown 标记、HTML 标签或多余空白影响了单词和句子的切分。解决在调用 SMOG 之前先做一轮清洗去掉 Markdown 语法符号、HTML 标签、连续空白和换行符。常见做法是先用正则把[^]和[#*_[]] 这类字符替换掉再传给计算函数。清洗步骤不要省否则结果没有可比性。5. 进阶技巧用 SMOG 做版本对比与写作反馈SMOG 不只是一个一次性检测工具它还能用来做版本对比。我习惯在修改文档前后各跑一次把两次的 SMOG 指数和多音节词数拉出来对比看看改动到底有没有降低阅读难度。下面这个函数接收两个版本的文本输出差异报告function compareVersions(oldText, newText) { const oldResult smog({ text: oldText, minSentences: 30 }); const newResult smog({ text: newText, minSentences: 30 }); const diff { smogChange: (newResult.smogIndex - oldResult.smogIndex).toFixed(1), polySyllableChange: newResult.polySyllableCount - oldResult.polySyllableCount, sentenceChange: newResult.sentenceCount - oldResult.sentenceCount }; console.log(SMOG 变化:, diff.smogChange 0 ? ${diff.smogChange} : diff.smogChange); console.log(多音节词变化:, diff.polySyllableChange); console.log(句子数变化:, diff.sentenceChange); return diff; }逻辑说明分别计算两个版本的指标然后做差。smogChange为正说明改版后更难读了为负说明可读性提升。polySyllableChange能帮你定位问题——如果 SMOG 上升主要来自多音节词增加那就去检查新引入了哪些长词。sentenceChange则反映句子拆分或合并的情况。参数方面两个文本的句子数都要尽量接近 30 以上否则差异值本身就不稳定。另一个实用技巧是把 SMOG 指数和具体段落绑定。不要只算整篇文档的总分而是按段落分别计算找出最难读的那几段重点修改。做法很简单按空行切分段落每段单独调用 SMOG然后按指数排序。这样你能快速定位到“拖后腿”的段落而不是对着一个总分干瞪眼。还有一个边界要注意SMOG 对文本长度敏感太短的段落算出来的指数波动很大。我一般会跳过少于 5 个句子的段落或者把它们合并后再算。如果你在做自动化检查建议在报告里标注每个段落的句子数让读者自己判断可信度。从那以后我每次写完面向用户的文档都会先跑一遍 SMOG把指数超过 12 的段落挑出来重写再对比修改前后的数值。这个习惯帮我省掉了不少“看不懂”的反馈。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

JMeter 3.3压测RabbitMQ:从AMQP插件到发布确认对账

JMeter 3.3压测RabbitMQ:从AMQP插件到发布确认对账

简介:面向需要验证RabbitMQ高并发性能的开发测试人员,这份资源给出了基于JMeter 3.3的完整配套方案。作者在实战中整合了AMQP相关依赖与脚本,解决了原生JMeter不支持消息队列压测的问题,适合正在搭建RabbitMQ压测环境或想快速上手…

2026/10/18 9:02:24 阅读更多 →
Navicat for MySQL 10.0.11 使用笔记:老版本兼容性与数据导入导出实战

Navicat for MySQL 10.0.11 使用笔记:老版本兼容性与数据导入导出实战

简介:在MySQL日常运维与开发场景中,Navicat for MySQL 10.0.11简体中文版是一款面向国内数据库管理员、开发人员及初学者的图形化管理工具,以直观中文界面和一站式操作,帮助用户摆脱命令行依赖,高效完成数据库连接、维…

2026/10/18 9:02:24 阅读更多 →
Oracle ODBC驱动安装配置全攻略:Instant Client 32位避坑指南

Oracle ODBC驱动安装配置全攻略:Instant Client 32位避坑指南

简介:oracle odbc 驱动压缩包(instantclient-odbc-nt-11.2.0.3.0.7z)是Windows环境下连接Oracle 11.2.0.3.0数据库的官方ODBC驱动组件,面向使用PowerDesigner、ERStudio等数据建模工具的工程师,以及需要手工配置ODBC数…

2026/10/18 9:01:24 阅读更多 →

最新新闻

计算机基础面试:URL全流程、三次握手与HTTP演进要点

计算机基础面试:URL全流程、三次握手与HTTP演进要点

简介:这是一份面向程序员求职与面试准备的计算机网络基础知识点PDF资料,尤其适合准备后端、前端岗位笔试面试的求职者,用于快速补齐高频考点。内容取自代码随想录知识星球精华(最强八股文)第五版计算机基础篇&#xff…

2026/10/18 9:56:37 阅读更多 →
手写BP神经网络逼近二元函数:从零实现前向反向传播

手写BP神经网络逼近二元函数:从零实现前向反向传播

简介:本资源是一份面向机器学习初学者与MATLAB实践者的BP神经网络教学实现,聚焦于不依赖任何工具箱的手动构建与训练过程,重点解决二元函数逼近这一典型非线性拟合问题。压缩包为1KB的ZIP文件,内含1个核心MATLAB源码文件&#xff…

2026/10/18 9:56:36 阅读更多 →
程序员职业倦怠自救指南:7个心态调整法,把能量省下来

程序员职业倦怠自救指南:7个心态调整法,把能量省下来

前阵子和一个做了七八年后端的朋友吃饭,他说了一句话让我印象很深:“我现在不是累,是麻木。写代码没有快感,下班不知道干什么,第二天又坐在工位前发呆。”我听完第一个反应不是安慰他,而是松了一口气——因…

2026/10/18 9:56:36 阅读更多 →
旧房改造,补的是几十年积累的“老化欠账”

旧房改造,补的是几十年积累的“老化欠账”

最近有几个老旧小区的朋友同时问我类似的问题:房子被列入旧房改造名单,到底是好事还是麻烦?要不要趁机把家里重新弄一遍?大部分人的第一反应都是——改造是不是就是刷刷外墙、修修路,做个表面文章,好让小区…

2026/10/18 9:56:36 阅读更多 →
Oracle跨版本SQL执行计划快照集:10g-19c调优实战指南

Oracle跨版本SQL执行计划快照集:10g-19c调优实战指南

简介:本资源是面向Oracle数据库管理员(DBA)与性能调优工程师的SQL Tuning Advisor(SQLT)实战工具集,聚焦10g至19c全版本兼容的SQL性能诊断与优化场景。压缩包内含205个高实用性文件,主体为160个…

2026/10/18 9:56:36 阅读更多 →
基于SpringBoot+Vue的课外活动管理系统设计与实现

基于SpringBoot+Vue的课外活动管理系统设计与实现

1. 项目整体思路与技术选型前后端分离已经是现在做课外活动管理系统的主流姿势,但真正从零开始把分支打全、逻辑理清楚,还是要费不少功夫。这套基于JavaSpringBootVue的课外活动管理系统,核心解决的是高校/中职院校里“活动发布—报名—签到—…

2026/10/18 9:55:36 阅读更多 →

日新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 0:55:02 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 14:23:36 阅读更多 →