前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
首页程序员面试题库Intl.Segmenter 字符串分词分句
JavaScript数据处理

中文没有空格怎么分词?emoji 的长度为什么不对?Intl.Segmenter 怎么用?

new Intl.Segmenter(locale, { granularity }) 按语言规则切分文本,granularity 可以是 grapheme(用户看到的一个字符)、word(单词)或 sentence(句子)。它能给没有空格的中文分词,也能把带肤色的 emoji 当成一个字符,这两件事 split 和 length 都做不到。

前端进阶之旅 · 一题精讲更新于 2026.09.29
JavaScript#数据处理
先看核心答案读代码示例
理解线索

按任务选切分粒度

  1. grapheme字素,也就是用户看到的一个字符,适合字数统计和截断
  2. word按语言词典找词边界,不依赖空格
  3. sentence按句号、问号等规则找句子边界

分词结果依赖浏览器内置的 ICU 词典,不同浏览器、不同版本可能切得不一样。它适合做字数统计、光标移动这类通用场景,不能代替搜索引擎的分词器或大模型的 tokenizer。

核心回答

先记住这个答案

Intl.Segmenter 是按语言规则切分文本的 API。构造时通过 granularity 选粒度:grapheme 切成用户看到的字符,word 切成单词,sentence 切成句子。调用 segment(text) 返回一个可迭代对象,每一项有 segment(这一段的文本)、index(在原字符串里的起始位置),word 模式下还有 isWordLike,用来区分词和标点空格。它解决了两个老问题:split(' ') 切不了没有空格的中文和日文,str.length 数的是 UTF-16 单元,一个带肤色的 emoji 会被算成 4。

  • grapheme 是用户眼里的一个字符,emoji 不会被拆开
  • word 能给中文、日文分词,isWordLike 过滤标点
  • index 是 UTF-16 偏移,不是第几个字符
  • 返回的是可迭代对象,要数组就用展开运算符

字数统计和安全截断:grapheme

评论框限制 100 个字,用 length 判断,用户输入几个 emoji 就被多算了。截断昵称时用 slice,还可能把一个 emoji 从中间切开,显示成乱码。

三种「长度」的区别JavaScript
const text = '点赞👍🏻' // 竖大拇指 + 肤色修饰符
const seg = new Intl.Segmenter('zh-CN', { granularity: 'grapheme' })
const graphemes = [...seg.segment(text)]

console.log(text.length) // UTF-16 单元数
console.log([...text].length) // 码点数
console.log(graphemes.length) // 用户看到的字符数

// 安全截断:按字素取前 3 个,不会切坏 emoji
console.log(graphemes.slice(0, 3).map((s) => s.segment).join('') === text)
查看输出与解释
6
4
3
true

👍🏻 是两个码点拼成的:竖大拇指和一个肤色修饰符,每个码点在 UTF-16 里又要两个单元,所以 length 是 4。展开运算符 [...text] 按码点拆,能解决「一个码点两个单元」的问题,但拆不了多码点组合,还是算成 2。只有 grapheme 粒度能算出用户眼里的 1 个字符。 如果用 text.slice(0, 3) 截取,会切在大拇指那个码点的中间,页面上显示成一个问号方块。按字素截断就没有这个问题。家庭 emoji、国旗、带声调符号的字母也是同样的道理,它们都由多个码点组成。

中文分词和分句:word 和 sentence

英文按空格就能大致分词,中文不行。word 粒度会用内置的词典找词的边界。

分词并过滤标点JavaScript
const words = new Intl.Segmenter('zh-CN', { granularity: 'word' })
const list = [...words.segment('我喜欢写JavaScript。')]
  .filter((s) => s.isWordLike) // 去掉句号这类非词片段
  .map((s) => `${s.segment}@${s.index}`)
console.log(list.join(' '))

const sentences = new Intl.Segmenter('zh-CN', { granularity: 'sentence' })
const parts = [...sentences.segment('今天下雨了。你带伞了吗?带了!')]
console.log(parts.map((s) => s.segment).join(' | '))
查看输出与解释
我@0 喜欢@1 写@3 JavaScript@4
今天下雨了。 | 你带伞了吗? | 带了!

「喜欢」被识别成一个词,从下标 1 开始。句号那一段的 isWordLike 是 false,被过滤掉了。用这个方法可以做中文的字数统计、双击选中一个词、按词高亮搜索关键字。 分句按句号、问号、感叹号切。但规则是通用的,英文里「Mr. Smith left.」会被切成「Mr.」和后面两段,因为它把缩写后的点也当成了句末。对准确性要求高的场景,比如机器翻译前的分句,还要额外处理。

index 不是第几个字符
index 是这一段在原字符串里的 UTF-16 偏移,可以直接拿去 slice。但它不等于「第几个可见字符」,前面有 emoji 的话,index 会比字素序号大。要显示「第 N 个字」时,得用 grapheme 粒度另外数。
回答前,多想一步

容易答错的地方

以为返回值是数组
segment() 返回的是一个可迭代的 Segments 对象,没有 map、filter,要先用 [...segments] 或 Array.from() 转成数组。它还有一个 containing(index) 方法,可以直接查某个位置属于哪一段,做「双击选词」很方便。
用 grapheme 的结果算大模型 token
字素是用户看到的字符,大模型的 token 是按它自己的分词表切的,两者没有对应关系。算 token 数、估算费用,必须用对应模型的 tokenizer。
试着用自己的话回答

面试官还会怎么问?

不用 Intl.Segmenter,怎么数 emoji?

[...str].length 能处理大部分单码点 emoji,但处理不了肤色、家庭、国旗这类多码点组合。以前的做法是引入 grapheme-splitter 这类库,现在直接用 Intl.Segmenter 就行,主流浏览器都支持,Firefox 是最后一个跟进的。

isWordLike 为什么只在 word 粒度有?

它的作用是区分词语和空格、标点。grapheme 和 sentence 粒度的任务不是判断「像不像词」,所以返回的片段里没有这个字段。

不同浏览器分词结果会不一样吗?

会。分词依赖 ICU 的词典和规则,浏览器升级时可能更新。关键业务不要依赖某个具体的切分结果,比如不要把分词结果当成数据库索引的唯一依据,测试时也不要写死某个词一定被切成几段。

从一道题,走向一组知识

把知识连起来

数据处理

用户的语言浏览器不支持时,Intl 会怎么回退?应用要怎么兜底?

分词效果取决于运行时有没有对应语言的数据,用 Intl.Segmenter.supportedLocalesOf() 可以先检查。

数据处理

JSON.stringify 碰到 BigInt 直接报错,怎么序列化才不丢精度?

都是 JavaScript 内置类型在边界上的坑,一个是字符串长度不等于字符数,一个是大整数进不了 JSON。

参考资料

  • JavaScript:数据处理参考资料

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 字数统计和安全截断:grapheme
  3. 中文分词和分句:word 和 sentence
  4. 容易答错的地方
  5. 面试官还会怎么问
  6. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑