登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  前端

JavaScript Intl.Segmenter 怎样按中文词边界切分文本

来源:17golang原创

时间:2026-10-23 11:45:55 102浏览 收藏

JavaScript 按中文词边界切分文本时,不要把 split(" ") 当成分词器:中文句子通常没有空格,简单切分只能得到整句。更合适的做法是创建 Intl.Segmenter("zh-CN", { granularity: "word" }),再遍历它返回的 Segments,读取每个片段的 segment、index 和 isWordLike。这样既能拿到用户可见的词片段,也能保留它们在原字符串中的位置。

官方参考:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Segmenter

为什么 split(" ") 处理不了中文词边界

假设页面要给中文搜索结果加高亮,输入是“前端文本处理”。如果按空格切分,结果仍然是一整个字符串,因为原文没有空格。按字符拆开又会丢失“前端”“文本”“处理”这样的词粒度,而且表情符号、组合字符也不应该简单按 UTF-16 下标猜测用户看到的字符。

const text = "前端文本处理";

// 这里只按空格切分,中文句子没有空格时不会得到词片段
const bySpace = text.split(" ");
console.log(bySpace);

// 选择中文区域和 word 粒度,让运行时按区域化规则提供分段结果
const segmenter = new Intl.Segmenter("zh-CN", { granularity: "word" });
const segments = segmenter.segment(text);
console.log(Array.from(segments));

Intl.Segmenter 的职责是区域化文本分段,可以按 grapheme、word 或 sentence 选择粒度。这里的 word 适合“按词遍历”的前端任务,但它不是搜索引擎词典,也不替你完成同义词、拼音或业务词库匹配。

原始中文字符串、中文 Intl.Segmenter 配置与 Segments 字段的静态关系
图1:中文词边界的静态结构说明图,查看输入、分段配置与结果字段之间的关系;不是截图或运行证据。

创建分词器并读取每个片段

分词器创建时把语言环境和粒度写清楚。遍历 segmenter.segment(text) 得到的对象时,可以直接解构出每个分段记录。segment 是原文片段,index 是它在原字符串中的 UTF-16 code unit 起始位置,input 是输入字符串,isWordLike 则用于提示该片段是否像一个词。

function collectChineseWords(text) {
  // 固定中文区域和词粒度,避免调用方误用默认的 grapheme 粒度
  const segmenter = new Intl.Segmenter("zh-CN", { granularity: "word" });
  const result = [];

  // 保留 segment 与 index,后续高亮可以回到原字符串切片
  for (const item of segmenter.segment(text)) {
    result.push({
      text: item.segment,
      start: item.index,
      wordLike: item.isWordLike === true,
    });
  }
  return result;
}

const parts = collectChineseWords("前端文本处理:先识别词边界。");
// 这里只挑出词样片段,标点和空白仍保留在原字符串中
const words = parts.filter((item) => item.wordLike);
console.log(words);

不要只过滤 isWordLike 后再拼接成新字符串,否则标点、空格和原始位置都会丢失。更稳妥的做法是把分段结果看作“原文上的索引视图”:需要展示全部文本时遍历全部记录,需要做词列表时再按 wordLike 筛选。

如果要确认运行时最终采用了什么语言环境和粒度,可以调用 resolvedOptions()。没有显式指定粒度时,默认值是 grapheme,所以做中文词处理时不要省略 granularity: "word"。

怎样把分段结果用于高亮和截断

高亮的关键不是把字符串重新按字符拆开,而是使用每个分段的起始 index 和 segment.length 计算原文范围。下面的函数只返回标记范围,渲染层可以把这些范围交给 DOM 创建逻辑;它不会把用户文本直接拼接进 HTML,从而避免把文本当成标签解释。

function wordRanges(text, keyword) {
  // 只匹配非空关键词;真正的 DOM 高亮应使用 textContent 或节点 API
  if (typeof keyword !== "string" || keyword.length === 0) return [];

  const segmenter = new Intl.Segmenter("zh-CN", { granularity: "word" });
  const ranges = [];
  for (const item of segmenter.segment(text)) {
    // 只把词样片段交给高亮,保留原字符串的 code unit 范围
    if (item.isWordLike && item.segment.includes(keyword)) {
      ranges.push({ start: item.index, end: item.index + item.segment.length });
    }
  }
  return ranges;
}

const ranges = wordRanges("前端文本处理", "文本");
// ranges 可交给视图层按原字符串范围生成文本节点和高亮节点
console.log(ranges);

这里的 end 用的是 JavaScript 字符串的 code unit 位置,与 slice(start, end) 的参数一致。它不是“第几个 Unicode 码点”的序号,也不是屏幕上的像素位置。做截断时,先收集完整片段,再累加每个 segment.length,不要把一个片段从中间截断。

function takeWordSegments(text, maxUnits) {
  // maxUnits 与 String.prototype.slice 使用相同的 UTF-16 code unit 口径
  if (!Number.isInteger(maxUnits) || maxUnits  maxUnits) break;
    selected.push(item.segment);
    used += item.segment.length;
  }
  return selected.join("");
}

console.log(takeWordSegments("前端文本处理", 6));
Intl.Segmenter 分段结果被高亮和长度截断消费的静态关系
图2:分段结果消费关系的静态结构说明图,查看索引如何服务高亮与完整片段截断;不是截图或运行证据。

不支持 Intl.Segmenter 时怎样降级

浏览器能力不能靠版本字符串猜测,应该直接做特性检测。当前 MDN 将 Intl.Segmenter 标记为广泛可用,但旧设备、嵌入式 WebView 或项目自定义运行时仍可能缺少它。降级时要先明确产品需求:如果只是展示文本,可以退回原文或按明确的 UI 规则截取;如果必须按中文词边界高亮,就应接入经过项目评估的分词方案,而不是把 split("") 宣称成等价实现。

function createChineseSegmenter() {
  // 直接检查构造函数,避免用 userAgent 推断能力
  if (typeof Intl?.Segmenter !== "function") return null;
  return new Intl.Segmenter("zh-CN", { granularity: "word" });
}

function segmentForDisplay(text) {
  const segmenter = createChineseSegmenter();
  if (!segmenter) {
    // 降级结果明确标记为整段文本,不冒充中文词分段
    return [{ segment: text, index: 0, isWordLike: false, fallback: true }];
  }
  return Array.from(segmenter.segment(text));
}

console.log(segmentForDisplay("前端文本处理"));

特性检测通过只说明接口存在,不说明某个 locale 的规则与你的业务词库完全一致。可以用 Intl.Segmenter.supportedLocalesOf(["zh-CN"]) 了解运行时是否支持指定区域,但即使返回空数组,也要保留清晰的降级策略。不要把降级结果写回“已完成中文分词”的缓存字段,避免后续组件误判。

接入时容易忽略的几个问题

为什么拿到的片段里有标点或空白?因为遍历结果代表原文的连续分段,不只返回词。需要词列表时按 isWordLike 过滤,需要还原完整文本时则保留全部片段。

index 能直接当成字符数组下标吗?不能。它适用于 JavaScript 字符串的 code unit 口径;如果你的业务使用码点、用户感知字符或字节偏移,必须在边界处做明确转换。

Intl.Segmenter 能替代搜索分词服务吗?不能直接这样认为。它提供区域化分段 API,适合前端遍历、截断和展示;同义词、拼音、行业词库、权重和召回策略仍属于搜索系统的业务能力。

最终可以把判断顺序固定下来:先确认任务是词粒度还是字符粒度,再创建正确 locale 和 granularity 的分段器,遍历时保留 segment 与 index,最后用特性检测决定降级。这样中文文本处理不会被“按空格切分”或“按字符截断”这两个看似简单的写法带偏。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>