629 lines
18 KiB
TypeScript
629 lines
18 KiB
TypeScript
|
import { _decorator } from "cc";
|
|||
|
|
|||
|
const { ccclass, property } = _decorator;
|
|||
|
|
|||
|
var CELL_DELIMITERS = [",", ";", "\t", "|", "^"];
|
|||
|
var LINE_DELIMITERS = ["\r\n", "\r", "\n"];
|
|||
|
|
|||
|
var getterCast = function(value: any, index: any, cast: any, d: any) {
|
|||
|
|
|||
|
if (cast instanceof Array) {
|
|||
|
if (cast[index] === "number") {
|
|||
|
return Number(d[index]);
|
|||
|
} else if (cast[index] === "boolean") {
|
|||
|
return d[index] === "true" || d[index] === "t" || d[index] === "1";
|
|||
|
} else {
|
|||
|
return d[index];
|
|||
|
}
|
|||
|
} else {
|
|||
|
if (!isNaN(Number(value))) {
|
|||
|
return Number(d[index]);
|
|||
|
} else if (value == "false" || value == "true" || value == "t" || value == "f") {
|
|||
|
return d[index] === "true" || d[index] === "t" || d[index] === "1";
|
|||
|
} else {
|
|||
|
return d[index];
|
|||
|
}
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
var CSV = {
|
|||
|
//
|
|||
|
|
|||
|
/* =========================================
|
|||
|
* Constants ===============================
|
|||
|
* ========================================= */
|
|||
|
|
|||
|
STANDARD_DECODE_OPTS: {
|
|||
|
skip: 0,
|
|||
|
limit: false,
|
|||
|
header: false,
|
|||
|
cast: false,
|
|||
|
comment: ""
|
|||
|
},
|
|||
|
|
|||
|
STANDARD_ENCODE_OPTS: {
|
|||
|
delimiter: CELL_DELIMITERS[0],
|
|||
|
newline: LINE_DELIMITERS[0],
|
|||
|
skip: 0,
|
|||
|
limit: false,
|
|||
|
header: false
|
|||
|
},
|
|||
|
|
|||
|
quoteMark: '"',
|
|||
|
doubleQuoteMark: '""',
|
|||
|
quoteRegex: /"/g,
|
|||
|
|
|||
|
/* =========================================
|
|||
|
* Utility Functions =======================
|
|||
|
* ========================================= */
|
|||
|
assign: function () {
|
|||
|
var args = Array.prototype.slice.call(arguments);
|
|||
|
var base = args[0];
|
|||
|
var rest = args.slice(1);
|
|||
|
for (var i = 0, len = rest.length; i < len; i++) {
|
|||
|
for (var attr in rest[i]) {
|
|||
|
base[attr] = rest[i][attr];
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return base;
|
|||
|
},
|
|||
|
|
|||
|
map: function (collection: any, fn: Function) {
|
|||
|
var results = [];
|
|||
|
for (var i = 0, len = collection.length; i < len; i++) {
|
|||
|
results[i] = fn(collection[i], i);
|
|||
|
}
|
|||
|
|
|||
|
return results;
|
|||
|
},
|
|||
|
|
|||
|
getType: function (obj: any) {
|
|||
|
return Object.prototype.toString.call(obj).slice(8, -1);
|
|||
|
},
|
|||
|
|
|||
|
getLimit: function (limit: any, len: any) {
|
|||
|
return limit === false ? len : limit;
|
|||
|
},
|
|||
|
|
|||
|
buildObjectConstructor: function(fields: any, sample: any, cast: any) {
|
|||
|
return function(d: any) {
|
|||
|
var object: any = new Object();
|
|||
|
var setter = function(attr: any, value: any) {
|
|||
|
return object[attr] = value;
|
|||
|
};
|
|||
|
if (cast) {
|
|||
|
fields.forEach(function(attr: any, idx: number) {
|
|||
|
setter(attr, getterCast(sample[idx], idx, cast, d));
|
|||
|
});
|
|||
|
} else {
|
|||
|
fields.forEach(function(attr: any, idx: number) {
|
|||
|
setter(attr, getterCast(sample[idx], idx, null, d));
|
|||
|
});
|
|||
|
}
|
|||
|
// body.push("return object;");
|
|||
|
// body.join(";\n");
|
|||
|
return object;
|
|||
|
};
|
|||
|
},
|
|||
|
|
|||
|
buildArrayConstructor: function(fields: any, sample: any, cast: any) {
|
|||
|
return function(d: any) {
|
|||
|
var row = new Array(sample.length);
|
|||
|
var setter = function(idx: any, value: any) {
|
|||
|
return row[idx] = value;
|
|||
|
};
|
|||
|
if (cast) {
|
|||
|
fields.forEach(function(attr: any, idx: number) {
|
|||
|
setter(attr, getterCast(sample[idx], idx, cast, d));
|
|||
|
});
|
|||
|
} else {
|
|||
|
fields.forEach(function(attr: any, idx: number) {
|
|||
|
setter(attr, getterCast(sample[idx], idx, null, d));
|
|||
|
});
|
|||
|
}
|
|||
|
return row;
|
|||
|
};
|
|||
|
},
|
|||
|
|
|||
|
frequency: function (coll: any, needle: any, limit: any) {
|
|||
|
if (limit === void 0) limit = false;
|
|||
|
|
|||
|
var count = 0;
|
|||
|
var lastIndex = 0;
|
|||
|
var maxIndex = this.getLimit(limit, coll.length);
|
|||
|
|
|||
|
while (lastIndex < maxIndex) {
|
|||
|
lastIndex = coll.indexOf(needle, lastIndex);
|
|||
|
if (lastIndex === -1) break;
|
|||
|
lastIndex += 1;
|
|||
|
count++;
|
|||
|
}
|
|||
|
|
|||
|
return count;
|
|||
|
},
|
|||
|
|
|||
|
mostFrequent: function (coll: any, needles: any, limit: any) {
|
|||
|
var max = 0;
|
|||
|
var detected;
|
|||
|
|
|||
|
for (var cur = needles.length - 1; cur >= 0; cur--) {
|
|||
|
if (this.frequency(coll, needles[cur], limit) > max) {
|
|||
|
detected = needles[cur];
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return detected || needles[0];
|
|||
|
},
|
|||
|
|
|||
|
unsafeParse: function (text: any, opts: any, fn: any) {
|
|||
|
var lines = text.split(opts.newline);
|
|||
|
|
|||
|
if (opts.skip > 0) {
|
|||
|
lines.splice(opts.skip);
|
|||
|
}
|
|||
|
|
|||
|
var fields;
|
|||
|
var constructor;
|
|||
|
|
|||
|
function cells(lines: any) {
|
|||
|
var line = lines.shift();
|
|||
|
if (line.indexOf('"') >= 0) {// 含引号
|
|||
|
|
|||
|
// 找到这行完整的数据, 找到对称的双引号
|
|||
|
var lastIndex = 0;
|
|||
|
var findIndex = 0;
|
|||
|
var count = 0;
|
|||
|
while (lines.length > 0) {
|
|||
|
lastIndex = line.indexOf('"', findIndex);
|
|||
|
if (lastIndex === -1 && count % 2 === 0) break;
|
|||
|
|
|||
|
if (lastIndex !== -1) {
|
|||
|
findIndex = lastIndex + 1;
|
|||
|
count++;
|
|||
|
} else {
|
|||
|
line = line + opts.newline + lines.shift();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
var list = [];
|
|||
|
var item;
|
|||
|
|
|||
|
var quoteCount = 0;
|
|||
|
|
|||
|
var start = 0;
|
|||
|
var end = 0;
|
|||
|
var length = line.length;
|
|||
|
for (var key in line) {
|
|||
|
if (!line.hasOwnProperty(key)) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
let numKey = parseInt(key);
|
|||
|
var value = line[key];
|
|||
|
|
|||
|
if (numKey === 0 && value === '"') {
|
|||
|
quoteCount++;
|
|||
|
start = 1;
|
|||
|
}
|
|||
|
|
|||
|
if (value === '"') {
|
|||
|
quoteCount++;
|
|||
|
|
|||
|
if (line[numKey - 1] === opts.delimiter && start === numKey) {
|
|||
|
start++;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (value === '"' && quoteCount % 2 === 0) {
|
|||
|
|
|||
|
if (line[numKey + 1] === opts.delimiter || numKey + 1 === length) {
|
|||
|
end = numKey;
|
|||
|
item = line.substring(start, end);
|
|||
|
list.push(item);
|
|||
|
start = end + 2;
|
|||
|
end = start;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if (value === opts.delimiter && quoteCount % 2 === 0) {
|
|||
|
end = numKey;
|
|||
|
if (end > start) {
|
|||
|
item = line.substring(start, end);
|
|||
|
list.push(item);
|
|||
|
start = end + 1;
|
|||
|
end = start;
|
|||
|
} else if (end === start) {
|
|||
|
list.push("");
|
|||
|
start = end + 1;
|
|||
|
end = start;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
end = length;
|
|||
|
|
|||
|
if (end >= start) {
|
|||
|
item = line.substring(start, end);
|
|||
|
list.push(item);
|
|||
|
}
|
|||
|
|
|||
|
return list;
|
|||
|
} else {
|
|||
|
return line.split(opts.delimiter);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (opts.header) {
|
|||
|
if (opts.header === true) {
|
|||
|
opts.comment = cells(lines); // 第一行是注释
|
|||
|
opts.cast = cells(lines); // 第二行是数据类型
|
|||
|
fields = cells(lines);
|
|||
|
} else if (this.getType(opts.header) === "Array") {
|
|||
|
fields = opts.header;
|
|||
|
}
|
|||
|
|
|||
|
constructor = this.buildObjectConstructor(fields, lines[0].split(opts.delimiter), opts.cast);
|
|||
|
} else {
|
|||
|
constructor = this.buildArrayConstructor(fields, lines[0].split(opts.delimiter), opts.cast);
|
|||
|
}
|
|||
|
|
|||
|
while (lines.length > 0) {
|
|||
|
var row = cells(lines);
|
|||
|
if (row.length > 1) {
|
|||
|
fn(constructor(row), fields[0]);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return true;
|
|||
|
},
|
|||
|
|
|||
|
safeParse: function (text: any, opts: any, fn: Function) {
|
|||
|
var delimiter = opts.delimiter;
|
|||
|
var newline = opts.newline;
|
|||
|
|
|||
|
var lines = text.split(newline);
|
|||
|
if (opts.skip > 0) {
|
|||
|
lines.splice(opts.skip);
|
|||
|
}
|
|||
|
|
|||
|
return true;
|
|||
|
},
|
|||
|
|
|||
|
encodeCells: function (line: any, delimiter: any, newline: any) {
|
|||
|
var row = line.slice(0);
|
|||
|
for (var i = 0, len = row.length; i < len; i++) {
|
|||
|
if (row[i].indexOf(this.quoteMark) !== -1) {
|
|||
|
row[i] = row[i].replace(this.quoteRegex, this.doubleQuoteMark);
|
|||
|
}
|
|||
|
|
|||
|
if (row[i].indexOf(delimiter) !== -1 || row[i].indexOf(newline) !== -1) {
|
|||
|
row[i] = this.quoteMark + row[i] + this.quoteMark;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return row.join(delimiter);
|
|||
|
},
|
|||
|
|
|||
|
encodeArrays: function(coll: any, opts: any, fn: Function) {
|
|||
|
var delimiter = opts.delimiter;
|
|||
|
var newline = opts.newline;
|
|||
|
|
|||
|
if (opts.header && this.getType(opts.header) === "Array") {
|
|||
|
fn(this.encodeCells(opts.header, delimiter, newline));
|
|||
|
}
|
|||
|
|
|||
|
for (var cur = 0, lim = this.getLimit(opts.limit, coll.length); cur < lim; cur++) {
|
|||
|
fn(this.encodeCells(coll[cur], delimiter, newline));
|
|||
|
}
|
|||
|
|
|||
|
return true;
|
|||
|
},
|
|||
|
|
|||
|
encodeObjects: function (coll: any, opts: any, fn:Function) {
|
|||
|
var delimiter = opts.delimiter;
|
|||
|
var newline = opts.newline;
|
|||
|
var header;
|
|||
|
var row;
|
|||
|
|
|||
|
header = [];
|
|||
|
row = [];
|
|||
|
for (var key in coll[0]) {
|
|||
|
header.push(key);
|
|||
|
row.push(coll[0][key]);
|
|||
|
}
|
|||
|
|
|||
|
if (opts.header === true) {
|
|||
|
fn(this.encodeCells(header, delimiter, newline));
|
|||
|
} else if (this.getType(opts.header) === "Array") {
|
|||
|
fn(this.encodeCells(opts.header, delimiter, newline));
|
|||
|
}
|
|||
|
|
|||
|
//@ts-ignore
|
|||
|
fn(this.encodeCells(row, delimiter));
|
|||
|
|
|||
|
for (var cur = 1, lim = this.getLimit(opts.limit, coll.length); cur < lim; cur++) {
|
|||
|
row = [];
|
|||
|
for (var key$1 = 0, len = header.length; key$1 < len; key$1++) {
|
|||
|
row.push(coll[cur][header[key$1]]);
|
|||
|
}
|
|||
|
|
|||
|
fn(this.encodeCells(row, delimiter, newline));
|
|||
|
}
|
|||
|
|
|||
|
return true;
|
|||
|
},
|
|||
|
|
|||
|
parse: function (text: any, opts: any, fn: Function) {
|
|||
|
var rows: any;
|
|||
|
|
|||
|
if (this.getType(opts) === "Function") {
|
|||
|
fn = opts;
|
|||
|
opts = {};
|
|||
|
} else if (this.getType(fn) !== "Function") {
|
|||
|
rows = [];
|
|||
|
fn = rows.push.bind(rows);
|
|||
|
} else {
|
|||
|
rows = [];
|
|||
|
}
|
|||
|
|
|||
|
//@ts-ignore
|
|||
|
opts = this.assign({}, this.STANDARD_DECODE_OPTS, opts);
|
|||
|
//@ts-ignore
|
|||
|
this.opts = opts;
|
|||
|
|
|||
|
if (!opts.delimiter || !opts.newline) {
|
|||
|
var limit = Math.min(48, Math.floor(text.length / 20), text.length);
|
|||
|
opts.delimiter = opts.delimiter || this.mostFrequent(text, CELL_DELIMITERS, limit);
|
|||
|
opts.newline = opts.newline || this.mostFrequent(text, LINE_DELIMITERS, limit);
|
|||
|
}
|
|||
|
|
|||
|
// modify by jl 由表自行控制不要含有双引号.提高解析效率
|
|||
|
return this.unsafeParse(text, opts, fn) &&
|
|||
|
(rows.length > 0 ? rows : true);
|
|||
|
},
|
|||
|
|
|||
|
encode: function (coll: any, opts: any, fn: Function) {
|
|||
|
var lines: any;
|
|||
|
|
|||
|
if (this.getType(opts) === "Function") {
|
|||
|
fn = opts;
|
|||
|
opts = {};
|
|||
|
} else if (this.getType(fn) !== "Function") {
|
|||
|
lines = [];
|
|||
|
fn = lines.push.bind(lines);
|
|||
|
}
|
|||
|
|
|||
|
//@ts-ignore
|
|||
|
opts = this.assign({}, this.STANDARD_ENCODE_OPTS, opts);
|
|||
|
|
|||
|
if (opts.skip > 0) {
|
|||
|
coll = coll.slice(opts.skip);
|
|||
|
}
|
|||
|
|
|||
|
return (this.getType(coll[0]) === "Array" ? this.encodeArrays : this.encodeObjects)(coll, opts, fn) &&
|
|||
|
(lines.length > 0 ? lines.join(opts.newline) : true);
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
@ccclass("CSVManager")
|
|||
|
export class CSVManager {
|
|||
|
/* class member could be defined like this */
|
|||
|
|
|||
|
static _instance: CSVManager;
|
|||
|
|
|||
|
static get instance () {
|
|||
|
if (this._instance) {
|
|||
|
return this._instance;
|
|||
|
}
|
|||
|
|
|||
|
this._instance = new CSVManager();
|
|||
|
return this._instance;
|
|||
|
}
|
|||
|
private _csvTables:any = {};
|
|||
|
private _csvTableForArr:any = {};
|
|||
|
private _tableCast:any = {};
|
|||
|
private _tableComment:any = {};
|
|||
|
|
|||
|
addTable (tableName:string, tableContent:string, force?:boolean) {
|
|||
|
if (this._csvTables[tableName] && !force) {
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
let tableData: any = {};
|
|||
|
let tableArr: any[] = [];
|
|||
|
let opts = { header: true };
|
|||
|
CSV.parse(tableContent, opts, function (row: any, keyName: string) {
|
|||
|
tableData[row[keyName]] = row;
|
|||
|
tableArr.push(row);
|
|||
|
});
|
|||
|
|
|||
|
this._tableCast[tableName] = (CSV as any).opts.cast;
|
|||
|
this._tableComment[tableName] = (CSV as any).opts.comment;
|
|||
|
|
|||
|
this._csvTables[tableName] = tableData;
|
|||
|
this._csvTableForArr[tableName] = tableArr;
|
|||
|
|
|||
|
//this.csvTables[tableName].initFromText(tableContent);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 根据表名获取表的所有内容
|
|||
|
* @param {string} tableName 表名
|
|||
|
* @returns {object} 表内容
|
|||
|
*/
|
|||
|
getTableArr (tableName:string) {
|
|||
|
return this._csvTableForArr[tableName];
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 根据表名获取表的所有内容
|
|||
|
* @param {string} tableName 表名
|
|||
|
* @returns {object} 表内容
|
|||
|
*/
|
|||
|
getTable (tableName:string) {
|
|||
|
return this._csvTables[tableName];
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 查询一条表内容
|
|||
|
* @param {string} tableName 表名
|
|||
|
* @param {string} key 列名
|
|||
|
* @param {any} value 值
|
|||
|
* @returns {Object} 一条表内容
|
|||
|
*/
|
|||
|
queryOne (tableName:string, key:string, value:any) {
|
|||
|
var table = this.getTable(tableName);
|
|||
|
if (!table) {
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
if (key) {
|
|||
|
for (var tbItem in table) {
|
|||
|
if (!table.hasOwnProperty(tbItem)) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
if (table[tbItem][key] === value) {
|
|||
|
return table[tbItem];
|
|||
|
}
|
|||
|
}
|
|||
|
} else {
|
|||
|
return table[value];
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 根据ID查询一条表内容
|
|||
|
* @param {string}tableName 表名
|
|||
|
* @param {string}ID
|
|||
|
* @returns {Object} 一条表内容
|
|||
|
*/
|
|||
|
queryByID (tableName:string, ID:string) {
|
|||
|
//@ts-ignore
|
|||
|
return this.queryOne(tableName, null, ID);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 查询key和value对应的所有行内容
|
|||
|
* @param {string} tableName 表名
|
|||
|
* @param {string} key 列名
|
|||
|
* @param {any} value 值
|
|||
|
* @returns {Object}
|
|||
|
*/
|
|||
|
queryAll (tableName:string, key:string, value:any) {
|
|||
|
var table = this.getTable(tableName);
|
|||
|
if (!table || !key) {
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
var ret: any = {};
|
|||
|
for (var tbItem in table) {
|
|||
|
if (!table.hasOwnProperty(tbItem)) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
if (table[tbItem][key] === value) {
|
|||
|
ret[tbItem] = table[tbItem];
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return ret;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 选出指定表里所有 key 的值在 values 数组中的数据,返回 Object,key 为 ID
|
|||
|
* @param {string} tableName 表名
|
|||
|
* @param {string} key 列名
|
|||
|
* @param {Array}values 数值
|
|||
|
* @returns
|
|||
|
*/
|
|||
|
queryIn (tableName:string, key:string, values:Array<any>) {
|
|||
|
var table = this.getTable(tableName);
|
|||
|
if (!table || !key) {
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
var ret: any = {};
|
|||
|
var keys = Object.keys(table);
|
|||
|
var length = keys.length;
|
|||
|
for (var i = 0; i < length; i++) {
|
|||
|
var item = table[keys[i]];
|
|||
|
if (values.indexOf(item[key]) > -1) {
|
|||
|
ret[keys[i]] = item;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return ret;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 选出符合条件的数据。condition key 为表格的key,value 为值的数组。返回的object,key 为数据在表格的ID,value为具体数据
|
|||
|
* @param {string} tableName 表名
|
|||
|
* @param {any} condition 筛选条件
|
|||
|
* @returns
|
|||
|
*/
|
|||
|
queryByCondition (tableName:string, condition: any) {
|
|||
|
if (condition.constructor !== Object) {
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
var table = this.getTable(tableName);
|
|||
|
if (!table) {
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
var ret: any = {};
|
|||
|
var tableKeys = Object.keys(table);
|
|||
|
var tableKeysLength = tableKeys.length;
|
|||
|
var keys = Object.keys(condition);
|
|||
|
var keysLength = keys.length;
|
|||
|
for (var i = 0; i < tableKeysLength; i++) {
|
|||
|
var item = table[tableKeys[i]];
|
|||
|
var fit = true;
|
|||
|
for (var j = 0; j < keysLength; j++) {
|
|||
|
var key = keys[j];
|
|||
|
fit = fit && (condition[key] === item[key]) && !ret[tableKeys[i]];
|
|||
|
}
|
|||
|
|
|||
|
if (fit) {
|
|||
|
ret[tableKeys[i]] = item;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return ret;
|
|||
|
}
|
|||
|
|
|||
|
queryOneByCondition (tableName:string, condition: any) {
|
|||
|
if (condition.constructor !== Object) {
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
var table = this.getTable(tableName);
|
|||
|
if (!table) {
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
var keys = Object.keys(condition);
|
|||
|
var keysLength = keys.length;
|
|||
|
|
|||
|
for (let keyName in table) {
|
|||
|
var item = table[keyName];
|
|||
|
|
|||
|
var fit = true;
|
|||
|
for (var j = 0; j < keysLength; j++) {
|
|||
|
var key = keys[j];
|
|||
|
fit = fit && (condition[key] === item[key]);
|
|||
|
}
|
|||
|
|
|||
|
if (fit) {
|
|||
|
return item;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return null;
|
|||
|
}
|
|||
|
}
|