这几天遇到一个需求要将一个不定列且带复杂表头的excel文件导入到数据库里面,想把数据和一部分表头转置,就找了网上一个将Excel数据转换成二维数组,但是不巧只支持A~Z行,所以做了点扩展,记录一下方便回看
再原来的基础上做了一下调整
// sheet_to_array的实现
export function parseExcelSheetToArrays(sheet) {
const Range = {
x1: sheet["!ref"].split(":")[0].match(/^[A-Z]+/)[0], // 匹配字母
y1: sheet["!ref"].split(":")[0].match(/\d+$/)[0], // 匹配数字
x2: sheet["!ref"].split(":")[1].match(/^[A-Z]+/)[0],
y2: sheet["!ref"].split(":")[1].match(/\d+$/)[0],
};
let tableDataArr = [];
for (let y = Range.y1; y <= Range.y2; y++) { // 起始行到结束行
// 行遍历
let rowArr = [];
// 将字母部分设定为一个二十六进制的编码,转成十进制数字
for (let x = getIntFromChar(Range.x1); x <= getIntFromChar(Range.x2); x++) {
// 反过来将数字转回字母拼接成单元格
let position = getCharFromInt(x) + y;
rowArr.push(sheet[position] ? sheet[position].v : null);
}
tableDataArr.push(rowArr);
}
return tableDataArr;
}
// 将字母按排列顺序相加
function getIntFromChar(char){
if(char.length==1){
return char.charCodeAt() - 'A'.charCodeAt();
} else {
return getIntFromChar(char.substring(1)) + ((char[0].charCodeAt() - 'A'.charCodeAt() + 1) * Math.pow(26 , char.length-1));
}
}
// 将数字按排列顺序转回字母
function getCharFromInt(num){
if(num < 26){
return String.fromCharCode(num + 'A'.charCodeAt());
} else {
return getCharFromInt(num / 26 - 1) + String.fromCharCode(num % 26 + 'A'.charCodeAt());
}
}
按照原来参考的例子,函数parseExcelSheetToArrays将sheet里面返回的表数据范围!ref按照字母和数字拆解成了起始单元格x1y1和结束单元格x2y2,用y1和y2记录行,x1和x2记录列
const Range = {
x1: sheet["!ref"].split(":")[0].match(/^[A-Z]+/)[0], // 匹配字母
y1: sheet["!ref"].split(":")[0].match(/\d+$/)[0], // 匹配数字
x2: sheet["!ref"].split(":")[1].match(/^[A-Z]+/)[0],
y2: sheet["!ref"].split(":")[1].match(/\d+$/)[0],
};
然后利用x1和x2的UTF-16编码实现列的循环,但是超过Z的列如'AA'没办法直接用这种方式解决,所以在原来的基础上,用类似二十六进制和十进制转换的方式,来实现循环的条件
// 字母转十进制
function getIntFromChar(char){
if(char.length==1){
return char.charCodeAt() - 'A'.charCodeAt();
} else {
return getIntFromChar(char.substring(1)) + ((char[0].charCodeAt() - 'A'.charCodeAt() + 1) * Math.pow(26 , char.length-1));
}
}
// 数字转字母
function getCharFromInt(num){
if(num < 26){
return String.fromCharCode(num + 'A'.charCodeAt());
} else {
return getCharFromInt(num / 26 - 1) + String.fromCharCode(num % 26 + 'A'.charCodeAt());
}
}
将循环的部分转码成数字,循环中需要用单元格的时候转回字母
for (let x = getIntFromChar(Range.x1); x <= getIntFromChar(Range.x2); x++) {
// 反过来将数字转回字母拼接成单元格
let position = getCharFromInt(x) + y;
rowArr.push(sheet[position] ? sheet[position].v : null);
}
tableDataArr.push(rowArr);
理论上支持三位数以上的列,不过我自己测试到'AAA'列没问题就结束了,一般也用不到这么多
最后的效果大概是这个样子的,因为是真实数据就不展示实际数据了
顺带一提,等我哼哧哼哧敲完之后才想起来能直接搜一个二十六进制和十进制转换的,头皮发麻,顺便把连接放这里