前两天,有一个需求,excel的某一列中含有不同字符,改为统一字符。如A列(某列)的单元格含有“数字”、“字母”、“商品”、“数字”、“货品”,需求:把“数字”、“字母”改为“ABC123”。
从网上搜索到一个插件名为:js-xlsx。其GitHub地址为:https://github.com/SheetJS/js-xlsx。
该插件支持IE8及以上版本浏览器,当然还支持safri、firefox、chrome这类优秀浏览器。它提供了脚本js引入、npm及bower等模块引入。
先叙述下我使用它解决那个需求的过程,更多的内容下次分解。
html页面显示如图:
js:
var reader = new FileReader();
var fileExcel={
"oldFile":"",
};
function addLis() { //监听输入框,引入文件
var xlf = document.getElementById('xlf');
if(xlf.addEventListener) xlf.addEventListener('change', handleFile, false);
}
addLis();
// 变量fileExcel赋值,获取文件
function handleFile(e) {
var files = e.target.files;
fileExcel.oldFile = files[0];
readFile(fileExcel.oldFile);
}
function readFile(file) {
var name = file.name;
reader.onload = function (e) {
fileExcel.oldFile = e.target.result;
var wb = XLSX.read(fileExcel.oldFile, { type: "binary" });
console.log(wb);
var wb_sheetname=wb.SheetNames[0];
console.log(wb.Sheets[wb_sheetname]);
};
reader.readAsBinaryString(file);
}
这样,就有了一个全局变量fileExcel,用于存储相关参数,如同vuejs的vuex功能。
// 读取文件
var wb = XLSX.read(fileExcel.oldFile, { type: "binary" });
// console.log(wb);
var wb_sheetname=wb.SheetNames[0]; //获取第一张表的名字
var wb_sheet=wb.Sheets[wb_sheetname]; //通过表名获取第一张表
// 加入变量
fileExcel.wb=wb;
fileExcel.wb_sheetname=wb_sheetname;
fileExcel.wb_sheet=wb_sheet;
该插件注册了一个全局变量XLSX,提供各种操作。如读取文件接口:
XLSX.read(File, options)
options是解析文件时参数对象,here:
这里使用 type:"binary",表示:以字符编码的方式解析。我们可以看到控制台的结果是:
这里可以看到工作簿的详细信息。其中:
SheetNames:由工作簿中工作表的名字组成的数组。
Sheets:包含各工作表的详细内容:
我上传的表格文件中含有3张表,名称分别为:Sheet1、Sheet2、Sheet3。其中!ref表示表格的数据分布范围,是一个字符串类型的数据。有了这个我们在对表格进行各中操作时就有了一个参考。
可以通过正则表达式把!ref信息转化为可使用的数据:
// 开始查询
var columnNum=$("#queryColumn").val(); //指定查询哪一列
var columnMax=wb_sheet["!ref"];
//console.log(columnMax)
// 匹配数字
var column_arr=/([A-Z]+)([0-9]+):([A-Z]+)([0-9]+)/i.exec(columnMax)
console.log(column_arr)
这样,就可以通过数组来操作数据范围了。得到以下结果:
可以看到第一个工作表Sheet1的数据范围是:A列到AD列,1行到12行。得到这个数据可以让我们在进行表格操作时有一个明确的范围,比如查找A列的数据时,只要遍历A1到A12就可以了。
在“修改的列名”输入框中键入A,点击查询按钮,得到以下结果:
这里我加入了一段去除重复的代码:
var arr_sheet_name=[]; //存储列名
var arr_sheet_value=[]; //存储单元格的内容,不重复
for(var i=column_arr[2]; i<=column_arr[4]; i++){
var column_query_num=columnNum.toString()+i;
console.log(wb_sheet[column_query_num])
var flag=true; //标记是否可以存储单元格内容,
if(wb_sheet[column_query_num]){
for(var j=0; j
把两个数组显示到页面的第一个大框框中,以备用户选择:
for (var i = 0; i<=arr_sheet_value.length - 1; i++) {
var stringTag=''+arr_sheet_value[i]+'';
$("#leftSelect").append(stringTag)
}
divfununbind();
divfunbind();
在左框中点击要修改的内容:
点击 >> 按钮,输入要修改的期望文字:
点击OK按钮,
// 更改内容
var change_arr=$("#rightSelect div").map(function() {
return $(this).attr("data-inde");
}).get().join("_").split("_"); //获取需要修改的单元格地址
for(var i=0; i"+changeColumn_word+"";
fileExcel.wb_sheet[change_arr[i]].v=changeColumn_word;
//fileExcel.wb_sheet[change_arr[i]].w=changeColumn_word;
console.log(change_arr[i])
console.log(fileExcel.wb_sheet[change_arr[i]])
}
// 全局声明更改表格
fileExcel.wb.Sheets[fileExcel.wb_sheetname]=fileExcel.wb_sheet;
console.log(fileExcel.wb_sheet)
更改单元格内容时的操作对象是Sheet[cell]的属性.v,不是Sheet[cell]本身(它的类型是[Object]),
只需更改 .v 的值,因为它指向单元格的值(我猜测此处是引用类型的传值),其他的 .h .r .w 等等都是由它转换而来的。here
我这里把要操作的工作表单独保存为全局变量fileExcel的属性fileExcel.wb_sheet,所以修改数据之后,需要重新给fileExcel.wb.Sheets[fileExcel.wb_sheetname]赋值。
点击保存按钮,弹出浏览器自带的下载框:
其中的网址是该插件默认的,可以自行修改:
// /* bookType can be any supported output type */
// var wopts = { bookType:'xlsx', bookSST:false, type:'array' };
// var wbout = XLSX.write(fileExcel.wb, wopts);
// /* the saveAs call downloads a file on the local machine */
// XLSX.writeFile.saveAs(new Blob([wbout],{type:"application/octet-stream"}), "test.xlsx");
我这里,使用的是:
XLSX.writeFile(fileExcel.wb, 'out.xlsx');
writeFile()接口将自动调用saveAs()。保存文件时,一定要保存工作簿wb,保存工作表wb_sheet是错误的。
该程序有两个bug,因为制作时的目标是:是否有能力实现需求,并没有要求功能十分完整,程序尽可能完美。
第一个bug是:上传文件的input标签,有一个accept属性,给属性只有一个声明功能,并不进行验证。W3C建议进行后台验证。此处如果上传的文件不是Excel是可以上传的。
第二个bug是:输入列名的输入框应该输入规范的列名,如A、AB,如果输入a、Ab、aB或数字或字母与数字的组合是没有结果的。此处应该进行判断和输入有误的提示型告知。
软件本没有bug,用的人多了就有了bug。解决bug的第一条思路,鄙人认为是:数据验证、数据约束。
如果不是输入类型的bug,我想也可以从数据约束的思路找到一点点启发。