|
|
@@ -301,3 +301,138 @@ export const speedRatioList = [
|
|
|
{ value: 1.5, label: '1.5' },
|
|
|
{ value: 2, label: '2.0' },
|
|
|
];
|
|
|
+
|
|
|
+export const TRAILING_PUNCT = new Set([
|
|
|
+ '。',
|
|
|
+ ',',
|
|
|
+ '、',
|
|
|
+ ';',
|
|
|
+ ':',
|
|
|
+ '?',
|
|
|
+ '!',
|
|
|
+ '”',
|
|
|
+ '’',
|
|
|
+ '"',
|
|
|
+ "'",
|
|
|
+ ')',
|
|
|
+ ')',
|
|
|
+ ']',
|
|
|
+ '】',
|
|
|
+ '}',
|
|
|
+ '』',
|
|
|
+ '》',
|
|
|
+ '〉',
|
|
|
+ '>',
|
|
|
+]);
|
|
|
+
|
|
|
+export const LEADING_PUNCT = new Set(['“', '‘', '"', "'", '(', '(', '[', '【', '{', '『', '《', '〈', '<']);
|
|
|
+
|
|
|
+export const PUNCT_REGEX = new RegExp(
|
|
|
+ `[${[...LEADING_PUNCT, ...TRAILING_PUNCT].map((c) => c.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('')}]`,
|
|
|
+ 'g',
|
|
|
+);
|
|
|
+/**
|
|
|
+ * 创建一个单词对象
|
|
|
+ * @param {*} word
|
|
|
+ * @param {*} type 1 为前置标点,2 为后置标点
|
|
|
+ */
|
|
|
+
|
|
|
+function cloneWord(word, type) {
|
|
|
+ if (!word || typeof word !== 'object') return word;
|
|
|
+ let result = {
|
|
|
+ ...word,
|
|
|
+ pinyin_list: Array.isArray(word.pinyin_list) ? [...word.pinyin_list] : [],
|
|
|
+ annota_list: Array.isArray(word.annota_list) ? [...word.annota_list] : [],
|
|
|
+ };
|
|
|
+ // 如果type==1,则在pinyin_list开头加一个全角空格,如果type==2,则在pinyin_list末尾加一个全角空格
|
|
|
+ if (type === 1) {
|
|
|
+ result.pinyin_list.unshift(' ');
|
|
|
+ result.pinyin = ` ${result.pinyin}`;
|
|
|
+ } else if (type === 2) {
|
|
|
+ result.pinyin_list.push(' ');
|
|
|
+ result.pinyin += ' ';
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+}
|
|
|
+
|
|
|
+function isTrailingPunct(text) {
|
|
|
+ return TRAILING_PUNCT.has(text);
|
|
|
+}
|
|
|
+
|
|
|
+function isLeadingPunct(text) {
|
|
|
+ return LEADING_PUNCT.has(text);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * ✅ 判断是否是任意标点符号
|
|
|
+ * 用于:前置标点后面是否“值得吸附”
|
|
|
+ */
|
|
|
+export function isPunctuation(text) {
|
|
|
+ if (typeof text !== 'string') return false;
|
|
|
+ return TRAILING_PUNCT.has(text) || LEADING_PUNCT.has(text);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 严格版标点吸附:
|
|
|
+ * - 后置标点:前一个有文本才吸附
|
|
|
+ * - 前置标点:后一个有文本才吸附
|
|
|
+ * - 连续标点:各自独立判断
|
|
|
+ */
|
|
|
+export function mergePunctuationToWords(wordList) {
|
|
|
+ if (!Array.isArray(wordList)) return wordList;
|
|
|
+ const result = [];
|
|
|
+ let lastWord = null;
|
|
|
+
|
|
|
+ for (let i = 0; i < wordList.length; i++) {
|
|
|
+ const word = wordList[i];
|
|
|
+ if (!word || typeof word.text !== 'string') {
|
|
|
+ result.push(word);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ const text = word.text;
|
|
|
+
|
|
|
+ // 后置标点:前为分词才吸附
|
|
|
+ if (isTrailingPunct(text)) {
|
|
|
+ if (lastWord) {
|
|
|
+ lastWord.text += `${text}`;
|
|
|
+ lastWord.pinyin_list.push(' ');
|
|
|
+ lastWord.pinyin += ' ';
|
|
|
+ } else {
|
|
|
+ result.push(cloneWord(word, 2));
|
|
|
+ }
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 前置标点:只看“下一个 word”
|
|
|
+ if (isLeadingPunct(text)) {
|
|
|
+ const nextWord = wordList[i + 1];
|
|
|
+
|
|
|
+ if (
|
|
|
+ nextWord &&
|
|
|
+ typeof nextWord.text === 'string' &&
|
|
|
+ !isPunctuation(nextWord.text) // 下一个不是标点
|
|
|
+ ) {
|
|
|
+ // 吸附到下一个分词前面
|
|
|
+ const clonedNext = cloneWord(nextWord, 1);
|
|
|
+ clonedNext.text = `${text}${clonedNext.text}`;
|
|
|
+ result.push(clonedNext);
|
|
|
+ lastWord = clonedNext;
|
|
|
+
|
|
|
+ i++; // 跳过下一个 word(已经被合并)
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 后面没词 or 还是标点 → 原样返回
|
|
|
+ result.push(cloneWord(word));
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 普通分词
|
|
|
+ const cloned = cloneWord(word);
|
|
|
+ result.push(cloned);
|
|
|
+ lastWord = cloned;
|
|
|
+ }
|
|
|
+ console.log('result', result);
|
|
|
+ return result;
|
|
|
+}
|