fix: 修复nlp停用词调用顺序问题导致的报错

This commit is contained in:
digua
2026-03-15 15:13:54 +08:00
committed by digua
parent ef7ac49959
commit 3ab730baae
3 changed files with 228 additions and 31 deletions
+3 -2
View File
@@ -166,8 +166,9 @@ export function collectPosTagStats(
const tagged = jieba.tag(cleaned)
for (const item of tagged) {
// 检查词是否有效(长度和停用词过滤)
if (!isValidWord(item.word, minWordLength, 'zh-CN', enableStopwords)) {
// 这里固定按中文停用词规则校验,但参数顺序必须与 isValidWord 保持一致,
// 否则会把最小词长误传成 locale,最终触发 locale.startsWith 的运行时错误。
if (!isValidWord(item.word, 'zh-CN', minWordLength, enableStopwords)) {
continue
}
posStats.set(item.tag, (posStats.get(item.tag) || 0) + 1)
+223 -28
View File
@@ -1,3 +1,5 @@
import type { SupportedLocale } from './types'
/**
* 停用词表
* 用于过滤无意义的高频词
@@ -549,38 +551,208 @@ export const ENGLISH_STOPWORDS = new Set([
/** 日语停用词 */
export const JAPANESE_STOPWORDS = new Set([
// 助詞
'の', 'に', 'は', 'を', 'た', 'が', 'で', 'て', 'と', 'し', 'れ', 'さ',
'ある', 'いる', 'も', 'する', 'から', 'な', 'こと', 'として', 'い', 'や',
'れる', 'など', 'なっ', 'ない', 'この', 'ため', 'その', 'あっ', 'よう',
'また', 'もの', 'という', 'あり', 'まで', 'られ', 'なる', 'へ', 'か',
'だ', 'これ', 'によって', 'により', 'おり', 'より', 'による', 'ず', 'なり',
'られる', 'において', 'ば', 'なかっ', 'なく', 'しかし', 'について', 'せ',
'だっ', 'その後', 'できる', 'それ', 'う', 'ので', 'なお', 'のみ', 'でき',
'き', 'つ', 'における', 'および', 'いう', 'さらに', 'でも', 'ら', 'たり',
'その他', 'に関する', 'たち', 'ます', 'ん', 'なら', 'に対して',
'の',
'',
'',
'',
'',
'',
'',
'',
'',
'し',
'れ',
'さ',
'ある',
'いる',
'も',
'する',
'から',
'な',
'こと',
'として',
'い',
'や',
'れる',
'など',
'なっ',
'ない',
'この',
'ため',
'その',
'あっ',
'よう',
'また',
'もの',
'という',
'あり',
'まで',
'られ',
'なる',
'へ',
'か',
'だ',
'これ',
'によって',
'により',
'おり',
'より',
'による',
'ず',
'なり',
'られる',
'において',
'ば',
'なかっ',
'なく',
'しかし',
'について',
'せ',
'だっ',
'その後',
'できる',
'それ',
'う',
'ので',
'なお',
'のみ',
'でき',
'き',
'つ',
'における',
'および',
'いう',
'さらに',
'でも',
'ら',
'たり',
'その他',
'に関する',
'たち',
'ます',
'ん',
'なら',
'に対して',
// 代名詞
'私', '僕', '俺', '自分', 'あなた', '彼', '彼女', 'それ', 'これ', 'あれ',
'ここ', 'そこ', 'あそこ', 'どこ', 'みんな', '皆',
'私',
'',
'俺',
'自分',
'あなた',
'彼',
'彼女',
'それ',
'これ',
'あれ',
'ここ',
'そこ',
'あそこ',
'どこ',
'みんな',
'皆',
// 接続詞
'そして', 'しかし', 'でも', 'だから', 'それで', 'だけど', 'けど',
'ところで', 'さて', 'つまり', 'すなわち', 'ただし', 'もし', 'また',
'そして',
'しかし',
'でも',
'だから',
'それで',
'だけど',
'けど',
'ところで',
'さて',
'つまり',
'すなわち',
'ただし',
'もし',
'また',
// 副詞
'とても', 'すごく', 'もう', 'まだ', 'よく', 'ちょっと', 'ちょと', 'もっと',
'やっぱり', 'やはり', 'たぶん', 'きっと', 'ぜんぜん', 'ほんとに', 'ほんと',
'かなり', 'だいたい', 'ほとんど', 'まあ', 'なんか', 'なんとなく',
'とても',
'すごく',
'もう',
'まだ',
'よく',
'ちょっと',
'ちょと',
'もっと',
'やっぱり',
'やはり',
'たぶん',
'きっと',
'ぜんぜん',
'ほんとに',
'ほんと',
'かなり',
'だいたい',
'ほとんど',
'まあ',
'なんか',
'なんとなく',
// 感動詞・フィラー
'あ', 'ああ', 'えー', 'うん', 'えっ', 'おお', 'へー', 'ふーん', 'はい',
'いいえ', 'うーん', 'まあ', 'ねえ', 'ほら', 'あのね', 'えっと', 'その',
'あ',
'ああ',
'えー',
'うん',
'えっ',
'おお',
'へー',
'ふーん',
'はい',
'いいえ',
'うーん',
'まあ',
'ねえ',
'ほら',
'あのね',
'えっと',
'その',
// 動詞(高頻度)
'いる', 'ある', 'する', 'なる', 'できる', 'いく', 'くる', 'みる', 'おもう',
'いう', 'やる', 'くれる', 'もらう', 'あげる', 'しまう', 'おく',
'いる',
'ある',
'する',
'なる',
'できる',
'いく',
'くる',
'みる',
'おもう',
'いう',
'やる',
'くれる',
'もらう',
'あげる',
'しまう',
'おく',
// 形容詞(高頻度)
'いい', 'ない', 'よい', 'すごい', 'おおきい', 'ちいさい',
'いい',
'ない',
'よい',
'すごい',
'おおきい',
'ちいさい',
// 助動詞
'です', 'ます', 'でした', 'ました', 'ません', 'だった', 'でしょう',
'です',
'ます',
'でした',
'ました',
'ません',
'だった',
'でしょう',
// チャットでの高頻語
'www', 'ww', 'lol', 'ok', 'おけ', 'りょ', 'おつ', 'わら', '笑',
'それな', 'たしかに', 'マジ', 'まじ', 'ガチ', 'がち',
'www',
'ww',
'lol',
'ok',
'おけ',
'りょ',
'おつ',
'わら',
'笑',
'それな',
'たしかに',
'マジ',
'まじ',
'ガチ',
'がち',
])
/**
@@ -589,10 +761,12 @@ export const JAPANESE_STOPWORDS = new Set([
* @returns 停用词集合
*/
export function getStopwords(locale: string): Set<string> {
if (locale.startsWith('zh')) {
const normalizedLocale = normalizeStopwordLocale(locale)
if (normalizedLocale.startsWith('zh')) {
return CHINESE_STOPWORDS
}
if (locale === 'ja-JP') {
if (normalizedLocale === 'ja-JP') {
return JAPANESE_STOPWORDS
}
return ENGLISH_STOPWORDS
@@ -605,7 +779,28 @@ export function getStopwords(locale: string): Set<string> {
* @returns 是否为停用词
*/
export function isStopword(word: string, locale: string): boolean {
const stopwords = getStopwords(locale)
const normalizedWord = locale === 'en-US' ? word.toLowerCase() : word
const normalizedLocale = normalizeStopwordLocale(locale)
const stopwords = getStopwords(normalizedLocale)
const normalizedWord = normalizedLocale === 'en-US' ? word.toLowerCase() : word
return stopwords.has(normalizedWord)
}
/**
* 规范化停用词处理使用的 locale。
* 这里额外兜底一次,避免上游误传数字或其他异常值时直接导致运行时崩溃。
*/
function normalizeStopwordLocale(locale: string): SupportedLocale {
if (typeof locale !== 'string') {
return 'en-US'
}
if (locale.startsWith('zh')) {
return 'zh-CN'
}
if (locale === 'ja-JP') {
return 'ja-JP'
}
return 'en-US'
}
+2 -1
View File
@@ -79,7 +79,8 @@ export function getWordFrequency(params: WordFrequencyParams): WordFrequencyResu
// 收集词性统计(用于显示每个词性有多少词,仅中文有效)
let posTagStats: PosTagStat[] | undefined
if ((locale as string).startsWith('zh')) {
// 词性统计只对中文生效,这里先做类型兜底,避免异常 locale 直接触发 startsWith 报错。
if (typeof locale === 'string' && locale.startsWith('zh')) {
const posStatsMap = collectPosTagStats(texts, minWordLength ?? 2, enableStopwords)
posTagStats = [...posStatsMap.entries()].map(([tag, count]) => ({ tag, count }))
}