JavaScript 中基于状态机的文本分词与带引号短语处理教程

JavaScript 中基于状态机的文本分词与带引号短语处理教程

本教程详细阐述了如何在JavaScript中实现一个健壮的文本分词器,尤其侧重于正确处理包含空格的带引号短语。通过引入有限状态机(FSM)的概念,我们将学习如何逐字符解析字符串,区分普通单词和引号内短语,并将其作为独立单元提取,从而克服传统split()方法在复杂场景下的局限性。

引言:传统分词的局限性

javascript中,我们经常需要将一段文本拆分成独立的词语或短语。最常见的方法是使用string.prototype.split(‘ ‘)。然而,当遇到包含空格的带引号短语时,这种简单的方法便会失效。例如,对于输入字符串”on time” “flight”,我们期望将”on time”作为一个整体的短语,而”flight”作为一个单独的词。如果使用split(‘ ‘),”on time”会被错误地拆分成”on和time”`,这显然不符合我们的预期。

传统的split()方法无法区分引号内外的空格,导致无法正确识别带引号的短语。为了解决这一问题,我们需要一种更智能的解析策略,即有限状态机(Finite-State Machine, FSM)。

核心策略:有限状态机 (FSM) 分词

有限状态机是一种抽象的计算模型,它在任何给定时间点都处于有限数量的状态之一。根据输入事件,FSM会从一个状态转换到另一个状态。在文本分词场景中,我们可以定义不同的“状态”来表示解析器当前正在处理的文本类型(例如,普通单词或引号内的短语),并根据遇到的字符进行状态转换。

状态定义

为了实现带引号短语的正确分词,我们至少需要两种核心状态:

word 状态:表示解析器当前正在处理一个普通的单词,该单词不被双引号包围。phrase 状态:表示解析器当前正在处理一个被双引号包围的短语。在此状态下,空格应被视为短语的一部分,而不是分隔符。

状态转换逻辑

解析器会逐字符遍历输入字符串,并根据当前字符和当前所处的状态来决定:

立即学习“Java免费学习笔记(深入)”;

是否将当前字符添加到正在构建的词语/短语中。是否完成当前词语/短语并将其添加到结果列表中。是否从一个状态切换到另一个状态。

JavaScript 实现与代码解析

下面是一个使用FSM原理实现的JavaScript函数,用于将字符串拆分为单词和带引号的短语:

function splitToWordsWithQuotes(str) {    let mode = null; // null: 初始状态/空白区域, 'word': 正在构建单词, 'phrase': 正在构建短语    const words = []; // 存储最终分词结果的数组    let word = '';    // 临时变量,用于构建当前正在处理的词语或短语    // 辅助函数:完成当前词语/短语的构建,并将其添加到结果数组中    const completeWord = () => {        if (word.length > 0) { // 只有当word不为空时才添加            words.push(word);        }        word = ''; // 重置临时变量    };    for (let i = 0; i < str.length; i++) {        const c = str[i]; // 当前字符        if (mode === null) { // 初始状态或处于空白区域            if (c === ' ') {                continue; // 跳过空格            }            if (c === '"') {                mode = 'phrase'; // 遇到双引号,进入短语模式            } else {                word += c; // 遇到非空格非引号字符,开始构建单词                mode = 'word'; // 进入单词模式            }            continue;        }        if (c === '"') { // 遇到双引号            completeWord(); // 完成当前词语或短语            // 切换模式:如果当前是单词模式,则下一个是短语模式(尽管这通常意味着一个新词的开始,且这里没有处理转义引号)            // 如果当前是短语模式,则表示短语结束,回到等待新词的模式(null)            mode = null; // 遇到结束引号后,回到初始/空白状态,等待下一个词或短语            continue;        }        if (c === ' ') { // 遇到空格            if (mode === 'phrase') {                word += ' '; // 如果在短语模式,空格是短语的一部分                continue;            }            // 如果在单词模式,空格表示单词结束            completeWord();            mode = null; // 回到初始/空白状态            continue;        }        // 其他字符:添加到当前词语或短语中        word += c;    }    // 循环结束后,如果word中还有内容,需要完成最后的词语/短语    completeWord();    return words;}

代码解析:

mode 变量:这是状态机的核心。

null:表示当前解析器处于初始状态或正在跳过空格,等待新的词语或短语开始。’word’:表示正在收集一个普通单词的字符。’phrase’:表示正在收集一个带引号短语的字符。

words 数组和 word 变量

words:存储所有已识别并完成的词语或短语。word:一个临时字符串,用于在当前状态下累积字符,直到一个词语或短语完成。

completeWord() 辅助函数:这个函数负责将当前word变量中的内容(如果非空)添加到words数组中,并清空word,为下一个词语或短语做准备。

字符遍历循环

初始/空白状态 (mode === null):遇到空格,直接跳过 (continue)。遇到双引号 (“),表示一个短语开始,将mode设置为’phrase’。遇到其他字符,表示一个普通单词开始,将字符添加到word,并将mode设置为’word’。遇到双引号 (c === ‘”‘):无论当前处于’word’还是’phrase’模式,遇到双引号都意味着当前词语/短语的结束。调用completeWord()。将mode重置为null,因为双引号通常是分隔符,之后需要重新判断下一个字符的类型。遇到空格 (c === ‘ ‘):如果在’phrase’模式,空格是短语的一部分,直接添加到word中。如果在’word’模式,空格表示当前单词结束。调用completeWord(),并将mode重置为null。其他字符:无论处于’word’还是’phrase’模式,其他字符(非空格、非双引号)都是当前词语或短语的一部分,直接添加到word中。

循环结束后的处理:循环结束后,word中可能还残留着最后一个词语或短语的字符,因此需要再次调用completeWord()来确保所有内容都被处理。

运行示例与结果分析

让我们使用一些示例输入来测试这个FSM分词器:

const myStr = '    "hello guys", some     words with "quotes inside"" some spaces inside " please keep quoted words as one "phrase / word" end-of-line ';const myWrongStr = '"hello guys", some words" with "quotes inside" please keep quoted words as one "phrase / word" ';console.log('--- 正常输入 ---');console.log('输入:', myStr);console.log('输出:', splitToWordsWithQuotes(myStr));// 预期输出: ["hello guys", "some", "words", "with", "quotes inside", "some", "spaces", "inside", "please keep quoted words as one", "phrase / word", "end-of-line"]console.log('n--- 包含未闭合引号的输入 ---');console.log('输入:', myWrongStr);console.log('输出:', splitToWordsWithQuotes(myWrongStr));// 预期输出: ["hello guys", "some", "words" with ", "quotes inside", "please keep quoted words as one", "phrase / word"]// 注意:未闭合的引号 "words" 被处理为 "words" with ",因为第二个引号被当作了短语的结束。

从示例中可以看出,splitToWordsWithQuotes 函数能够:

正确识别并提取带引号的短语,即使短语内部包含空格(如”hello guys”、”quotes inside”)。正确处理短语和单词之间的分隔(逗号、多个空格)。忽略字符串开头和结尾的空格。将普通单词正确地单独提取。

对于myWrongStr,它展示了当前FSM的一个特点:如果引号未正确闭合,它会继续将后续字符视为当前短语或单词的一部分,直到遇到下一个引号或分隔符。例如,some words” with 中的第二个引号被视为短语的结束,而不是一个未转义的引号字符。

注意事项与扩展

输出格式:当前FSM的输出是一个字符串数组,其中带引号的短语已经去除了原始的引号(例如,

以上就是JavaScript 中基于状态机的文本分词与带引号短语处理教程的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1518917.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年12月20日 11:58:59
下一篇 2025年12月20日 11:59:09

相关推荐

  • JS 音频可视化实现 – 使用 Web Audio API 分析频率数据的技巧

    答案是利用Web Audio API的AnalyserNode将音频频率数据实时解析,并通过Canvas绘制成可视化图形。核心流程包括:创建AudioContext,连接音频源与AnalyserNode,配置fftSize和smoothingTimeConstant参数,获取频率数据数组,结合req…

    好文分享 2025年12月20日
    000
  • React 组件卸载时如何正确终止异步循环与轮询操作

    本文探讨了 React 组件卸载后,内部异步 while 循环(如 API 轮询)仍持续运行的问题。核心原因在于 JavaScript 异步任务不会随组件卸载自动终止。我们将详细介绍如何利用 useEffect 的清理函数和 useRef 状态标识,确保在组件生命周期结束时,安全有效地中断这些持续性…

    2025年12月20日
    000
  • JS 浏览器扩展调试 – 使用 DevTools 调试背景页与内容脚本的技巧

    调试浏览器扩展需区分背景页与内容脚本:背景页通过chrome://extensions/打开独立DevTools调试;内容脚本在目标网页的DevTools中查找并调试;跨域通信可结合console.log与断点,利用debugger语句定位执行流;异步逻辑借助调用堆栈和事件监听断点(如Message…

    2025年12月20日
    000
  • React组件卸载时异步操作的优雅终止:useEffect与useRef实践

    本文探讨React组件卸载后,内部异步循环(如API轮询)仍持续运行的问题。核心在于React不会自动终止组件卸载时正在进行的异步任务。教程将详细介绍如何利用useEffect的清理函数和useRef来追踪组件的挂载状态,从而确保异步操作在组件卸载时能够被及时、优雅地终止,避免内存泄漏和不必要的资源…

    2025年12月20日
    000
  • React组件卸载后异步循环未停止:useEffect清理机制详解

    在React组件中,异步循环(如通过while循环进行的API轮询)即使在组件卸载后也可能继续执行,因为React不会自动终止这些后台任务。本文将深入探讨此问题的原因,并提供一个使用useEffect的清理函数结合useRef来安全管理和终止组件卸载时异步操作的专业解决方案,确保资源有效释放并避免潜…

    2025年12月20日
    000
  • React组件卸载时异步循环的正确终止方法

    React组件卸载后,useEffect中启动的异步循环(如API轮询)为何会继续运行的问题。我们将详细介绍React的副作用清理机制,并演示如何利用useEffect的返回函数和useRef来安全地管理组件的挂载状态,从而确保异步操作在组件卸载时能被正确终止,避免资源浪费和潜在的内存泄漏。 理解R…

    2025年12月20日
    000
  • 浏览器渲染原理与重绘回流优化

    浏览器通过解析HTML和CSS构建DOM与CSSOM树,合并为渲染树后进行布局(回流)和绘制(重绘)。优化核心是减少回流与重绘:避免频繁修改DOM,使用DocumentFragment或虚拟DOM批量更新;用transform替代top/left动画;避免复杂选择器和table布局;将JS放底部或加…

    2025年12月20日
    000
  • JavaScript中的内存泄漏通常由哪些原因引起?

    内存泄漏指不再需要的对象因被意外引用而无法被垃圾回收,常见于未清除的事件监听器、定时器、闭包和全局变量;可通过Chrome开发者工具分析堆快照与引用链,结合代码审查定位问题,并通过及时解绑事件、清除定时器、使用WeakMap及遵循框架生命周期等策略有效预防。 JavaScript中的内存泄漏,简单来…

    2025年12月20日
    000
  • 怎么利用JavaScript进行前端代码分割策略?

    代码分割通过将JavaScript拆分为按需加载的块,提升首屏加载速度与用户体验。其核心是动态导入(import())和构建工具支持,如Webpack、Vite等,实现路由或组件级别的懒加载。在React中使用React.lazy()与Suspense,Vue通过defineAsyncCompone…

    2025年12月20日
    000
  • JavaScript教程:循环动态创建单选按钮并赋予独立值

    本教程旨在解决在循环中动态创建HTML输入元素(特别是单选按钮)时,如何为每个元素分配不同值的常见问题。文章将详细阐述通过预定义值数组,结合循环迭代和正确的属性设置(如name属性用于分组单选按钮),实现高效且可维护的动态元素创建与赋值方法,确保每个输入元素拥有其独有的值。 动态创建输入元素的挑战 …

    2025年12月20日
    000
  • 在React应用中特定路由下渲染静态资源的策略

    本文介绍了一种在React应用中,无需重写或使用iFrame,即可将现有静态HTML、CSS和JavaScript内容集成到特定路由的方法。通过利用React项目的public目录,开发者可以轻松地将遗留静态资源作为独立页面提供服务,并从React组件中进行链接,有效避免了代码重复和维护负担。 在现…

    2025年12月20日
    000
  • 如何用WebAssembly Tail Call优化递归算法性能?

    WebAssembly的尾调用优化通过将尾递归调用转化为栈帧重用,避免栈溢出并提升性能。它要求递归调用位于函数末尾且无后续操作,编译器将其转换为return_call指令实现跳转而非压栈。该优化对深度递归场景至关重要,尤其在函数式语言编译到Wasm时。Rust、C/C++、AssemblyScrip…

    2025年12月20日
    000
  • JS 移动端测试自动化 – 使用 Appium 进行跨平台 UI 测试的方案

    Appium + JavaScript 实现跨平台移动端UI自动化测试,通过一套代码在iOS和Android上运行,提升测试效率与一致性。 JS 移动端测试自动化,特别是利用 Appium 进行跨平台 UI 测试,提供了一个相当成熟且高效的解决方案。它允许我们使用一套基于 JavaScript 的测…

    2025年12月20日
    000
  • JavaScript数据类型转换的隐式规则

    答案:JavaScript隐式类型转换发生在宽松相等比较、加法运算、布尔上下文、一元操作符和模板字面量等场景,核心是JS根据操作符和上下文自动转换类型,导致看似不合理的结果。例如==会触发类型强制,使"5"==5为true;+操作符遇字符串则转为拼接,1+"2&quot…

    2025年12月20日
    000
  • 如何通过JavaScript实现动态表单生成?

    动态表单生成需先定义表单结构数据,再通过JavaScript动态创建元素并渲染到页面,同时添加提交事件处理;样式可通过CSS或框架优化,验证可用HTML5或JS实现,复杂逻辑如级联选择需结合事件监听与AJAX,安全方面需防范XSS、CSRF和SQL注入。 动态表单生成,简单来说,就是用JavaScr…

    2025年12月20日
    000
  • 什么是JavaScript的模块作用域与闭包的结合,以及它们如何实现私有变量和模块模式?

    JavaScript通过模块作用域和闭包实现私有变量与受控访问:模块作用域隔离内部状态,防止全局污染;闭包则使外部可通过返回的函数接口安全操作私有变量。从IIFE到ES6模块,二者结合始终是封装、复用和状态管理的核心机制。 JavaScript的模块作用域与闭包结合,本质上是提供了一种强大的机制来封…

    2025年12月20日
    000
  • JavaScript数组去重:处理数字字符串混合类型的有效策略

    本教程探讨了在JavaScript数组中,当数字以数值和字符串形式混合存在时,如何高效且准确地进行去重操作。核心挑战在于标准去重方法无法区分数字1和字符串”1″。文章将通过统一数据类型的方法,结合parseInt和map,提供一个健壮的解决方案,确保所有等效数字都被正确识别并…

    2025年12月20日
    000
  • 怎么利用JavaScript进行移动端适配?

    JavaScript通过动态设置viewport、计算rem单位、控制媒体查询、检测设备类型、优化图片加载及处理触摸事件,实现移动端适配;结合性能优化手段如懒加载、文件压缩和CDN加速,提升移动端页面的兼容性与加载效率。 JavaScript在移动端适配中扮演着重要的角色,它能帮助我们动态调整页面元…

    2025年12月20日
    000
  • 从字符串格式数值数组中移除重复项

    本文旨在解决JavaScript中包含字符串格式数值的数组去重问题。通过将数组元素统一转换为数值类型,并利用filter方法结合indexOf进行筛选,最终得到一个不包含重复数值的数组。本文将提供详细的代码示例和解释,帮助开发者高效地处理此类数据。 在JavaScript中,处理包含字符串格式数值的…

    2025年12月20日
    000
  • JavaScript 循环创建单选框并赋予不同值

    本文介绍了如何使用 JavaScript 循环动态创建多个单选框(radio button),并为每个单选框赋予不同的值。通过示例代码,详细讲解了如何利用数组存储预设值,并在循环中依次赋值给新创建的单选框,以及如何将这些单选框添加到 HTML 页面中。 在 Web 开发中,经常需要在页面上动态生成表…

    2025年12月20日
    000

发表回复

登录后才能评论
关注微信