import * as csv from "fast-csv"; import * as fs from "fs"; export class CSVUtils { filePath: string; constructor(filePath: string) { this.filePath = filePath; } async parseCsv() { if (fs.existsSync(this.filePath)) { let stream = csv.parseFile(this.filePath, { delimiter: ",", }); let rows: string[][] = await stream.toArray(); stream.destroy(); return rows; } else { console.error(`文件不存在: ${this.filePath}`); return [] } } getShortKey(content: string) { // let max = 16 // if (content.length < max) { // return content // } else { // return content.substring(0, max) // } return content; } tryGetEntry(content: string, map01: Map, map02: Map): [boolean, string, string[] | undefined] { let key = content.replaceAll('\r\n','\n'); if (map02.has(key)) { let row2 = map02.get(key)!; return [row2 != null, key, row2]; } let shortKey = this.getShortKey(key); if (map01.has(shortKey)) { let row1 = map01.get(shortKey)!; if (row1 != null && (row1[0] == shortKey || row1[1] == key)) { return [true, shortKey, row1]; } else { return [false, shortKey, undefined]; } } return [false, shortKey, undefined]; } merge(rows0: string[][], rows1: string[], langs: string[]) { let indexsxse = rows1.findIndex(x=>x.includes("完成每日的显圣任务,获得经验值提升显圣等级")) // 短key let map01: Map = new Map(); // 全文key let map02: Map = new Map(); if (rows0 != null) { for (let index = 1; index < rows0.length; index++) { const row = rows0[index]; let key = row[0]; map01.set(key, row); } for (let index = 1; index < rows0.length; index++) { const row = rows0[index]; let key = row[1]; if (key != null && key != "") { map02.set(key, row); } } } let rows2: string[][] = []; if (rows0 != null && rows0.length > 0) { // rows2.push(rows0[0]); // 合并 langs 中没有的语言列 let headers = [...rows0[0]]; for (const lang of langs) { if (!headers.includes(lang)) { headers.push(lang); } } rows2.push(headers); } else { rows2.push(["key", ...langs]); } for (let index = 0; index < rows1.length; index++) { const content = rows1[index]; let [exist, key, row] = this.tryGetEntry(content, map01, map02); if (exist) { if (row != null) { rows2.push(row); } else { if (key != content) { rows2.push([key, content]); } else { rows2.push([content, '']); } } } else { if (key != content) { rows2.push([key, content]); } else { rows2.push([content, '']); } } } return rows2; } static async writeCsv(filePath: string, rows: string[][]) { let str = await csv.writeToString(rows); fs.writeFileSync(filePath, str, "utf-8"); } static async updateToFile(filePath: string, literals: string[], langs: string[]) { let csvUtils = new CSVUtils(filePath); let rows0 = await csvUtils.parseCsv(); // 优化:使用Set去重,减少重复处理 const uniqueLiterals = [...new Set(literals)]; let rows2 = csvUtils.merge(rows0, uniqueLiterals, langs); await CSVUtils.writeCsv(filePath, rows2); console.log(`已经更新多语言表: ${filePath} , 共有 ${rows2.length - 1} 条目`); } /** * 表格第一列为 key + 语言列表, 按照 langs 精简表格列, 只保留指定key列和指定语言列 * @param filePath csv文件路径 * @param outFilePath 输出csv文件路径 * @param langs 指定要保留的语言列表 */ static async slimCsvWithLangs(filePaths: string[], outFilePath: string, langs: string[]) { let rows2: string[][] = []; let keySpace: Set = new Set(); let conflictKeySpace: Set = new Set(); let existAnyInput = false; for (const filePath of filePaths) { if (fs.existsSync(filePath) == false) { console.warn(`文件不存在: ${filePath}`); continue; } existAnyInput = true; let csvUtils = new CSVUtils(filePath); let rows0 = await csvUtils.parseCsv(); let header = rows0[0]; if (header.findIndex(title => title == null || title.match(/^Column\d+$/)) >= 0) { console.warn(`文件 ${filePath} 包含 Column 列, 请检查title是否正确: ${header}`) } if (rows2.length == 0) { rows2.push([header[0], ...langs]); } let langIndexes: number[] = []; for (const lang of langs) { let langIndex = header.indexOf(lang); if (langIndex != -1) { langIndexes.push(langIndex); } } for (let index = 1; index < rows0.length; index++) { const row = rows0[index]; let newRow: string[] = []; let key = row[0]; if (keySpace.has(key)) { conflictKeySpace.add(key); } else { keySpace.add(key); } newRow.push(key); for (const langIndex of langIndexes) { newRow.push(row[langIndex]); } rows2.push(newRow); } } if (existAnyInput == false) { console.warn(`不存在有效文件, 未输出文件:`, filePaths); return; } if (rows2.length > 0) { await CSVUtils.writeCsv(outFilePath, rows2); if (conflictKeySpace.size > 0) { console.error(`冲突的key有 ${conflictKeySpace.size} 个:`, conflictKeySpace); } } else { console.warn(`没有生成任何数据,未输出文件。`); } } }