赞
踩
今天要分享的这个问题其实也属于excel的高级用法。我们想通过libxlsxwriter来实现一个能对用户的输入进行数据验证的表格功能。
内心os:小白最近是不是有点走火入魔了……我们真的会遇上这样的场景吗:需要通过代码生成一个表格,这个表格中还有若干需要潜在用户输入的单元格,并且还要对这些单元格做数据有效性验证!且不管这个需求是不是很少遇到,就当小白在这里钻了一个技术的牛角尖。
小白最近特别直率,二话不说,先上例程(扒自libxlsxwriter官方例程,小白进行了中文改造):
#include "xlsxwriter.h" #include <iostream> #include <wchar.h> #include <windows.h> using std::string; string GBKToUTF8(const char* strGBK) { int len = MultiByteToWideChar(CP_ACP, 0, strGBK, -1, NULL, 0); wchar_t* wstr = new wchar_t[len + 1]; memset(wstr, 0, len + 1); MultiByteToWideChar(CP_ACP, 0, strGBK, -1, wstr, len); len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL); char* str = new char[len + 1]; memset(str, 0, len + 1); WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, len, NULL, NULL); string strTemp = str; if (wstr) delete[] wstr; if (str) delete[] str; return strTemp; } string UTF8ToGBK(const char* strUTF8) { int len = MultiByteToWideChar(CP_UTF8, 0, strUTF8, -1, NULL, 0); wchar_t* wszGBK = new wchar_t[len + 1]; memset(wszGBK, 0, len * 2 + 2); MultiByteToWideChar(CP_UTF8, 0, strUTF8, -1, wszGBK, len); len = WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, NULL, 0, NULL, NULL); char* szGBK = new char[len + 1]; memset(szGBK, 0, len + 1); WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, szGBK, len, NULL, NULL); string strTemp(szGBK); if (wszGBK) delete[] wszGBK; if (szGBK) delete[] szGBK; return strTemp; } /* * 向excel表中写入数据,这些数据用来为后续做合法性判断作辅助作用. */ void write_worksheet_data(lxw_worksheet* worksheet, lxw_format* format) { worksheet_write_string(worksheet, CELL("A1"), GBKToUTF8("使用libxlsxwriter进行数据验证").data(), format); worksheet_write_string(worksheet, CELL("B1"), GBKToUTF8("向此列中输入值").data(), format); worksheet_write_string(worksheet, CELL("D1"), GBKToUTF8("示例数据").data(), format); worksheet_write_string(worksheet, CELL("D3"), GBKToUTF8("整数").data(), NULL); worksheet_write_number(worksheet, CELL("E3"), 1, NULL); worksheet_write_number(worksheet, CELL("F3"), 10, NULL); worksheet_write_string(worksheet, CELL("D4"), GBKToUTF8("列表数据").data(), NULL); worksheet_write_string(worksheet, CELL("E4"), GBKToUTF8("开").data(), NULL); worksheet_write_string(worksheet, CELL("F4"), GBKToUTF8("高").data(), NULL); worksheet_write_string(worksheet, CELL("G4"), GBKToUTF8("关").data(), NULL); worksheet_write_string(worksheet, CELL("D5"), GBKToUTF8("公式").data(), NULL); worksheet_write_formula(worksheet, CELL("E5"), "=AND(F5=50,G5=60)", NULL); worksheet_write_number(worksheet, CELL("F5"), 50, NULL); worksheet_write_number(worksheet, CELL("G5"), 60, NULL); } /* * 创建一个带有数据合法性验证的数据表 */ int main() { lxw_workbook* workbook = workbook_new("data_validate1.xlsx"); lxw_worksheet* worksheet = workbook_add_worksheet(workbook, NULL); // 定义一个验证变量 lxw_data_validation* data_validation = (lxw_data_validation*)calloc(1, sizeof(lxw_data_validation)); /* 创建带颜色强调的表头格式 */ lxw_format* format = workbook_add_format(workbook); format_set_border(format, LXW_BORDER_THIN); format_set_fg_color(format, 0xC6EFCE); format_set_bold(format); format_set_text_wrap(format); format_set_align(format, LXW_ALIGN_VERTICAL_CENTER); format_set_indent(format, 1); format_set_font_name(format, "SimSun"); /* 创建带颜色强调的表头格式 */ lxw_format* format1 = workbook_add_format(workbook); format_set_border(format1, LXW_BORDER_THIN); format_set_align(format1, LXW_ALIGN_VERTICAL_CENTER); format_set_font_name(format1, "SimHei"); /* 向表中写入一些辅助数据 */ write_worksheet_data(worksheet, format); /* 设置数据表中的列宽等页面数据 */ worksheet_set_column(worksheet, 0, 0, 70, NULL); worksheet_set_column(worksheet, 1, 1, 22, NULL); worksheet_set_column(worksheet, 3, 3, 15, NULL); worksheet_set_row(worksheet, 0, 36, NULL); /* * 例1:向表中输入一个范围值 */ worksheet_write_string(worksheet, CELL("A3"), GBKToUTF8("输入一个1到10之间的整数").data(), format1); data_validation->validate = LXW_VALIDATION_TYPE_INTEGER; data_validation->criteria = LXW_VALIDATION_CRITERIA_BETWEEN; data_validation->minimum_number = 1; data_validation->maximum_number = 10; worksheet_data_validation_cell(worksheet, CELL("B3"), data_validation); /* * 例2:向表中输入一个范围之外的值 */ worksheet_write_string(worksheet, CELL("A5"), GBKToUTF8("输入一个不在1到10之间的整数(1和10由单元格E3和F3指定)").data(), format1); data_validation->validate = LXW_VALIDATION_TYPE_INTEGER; data_validation->criteria = LXW_VALIDATION_CRITERIA_NOT_BETWEEN; data_validation->minimum_formula = "=E3"; data_validation->maximum_formula = "=F3"; worksheet_data_validation_cell(worksheet, CELL("B5"), data_validation); /* * 例3:向表中输入一个大于某值的数 */ worksheet_write_string(worksheet, CELL("A7"), GBKToUTF8("输入一个比0大的整数").data(), format1); data_validation->validate = LXW_VALIDATION_TYPE_INTEGER; data_validation->criteria = LXW_VALIDATION_CRITERIA_GREATER_THAN; data_validation->value_number = 0; worksheet_data_validation_cell(worksheet, CELL("B7"), data_validation); /* * 例4:向表中输入一个小于某值的数 */ worksheet_write_string(worksheet, CELL("A9"), GBKToUTF8("输入一个比10小的整数").data(), format1); data_validation->validate = LXW_VALIDATION_TYPE_INTEGER; data_validation->criteria = LXW_VALIDATION_CRITERIA_LESS_THAN; data_validation->value_number = 10; worksheet_data_validation_cell(worksheet, CELL("B9"), data_validation); /* * 例5:限制输入为一个范围内的小数 */ worksheet_write_string(worksheet, CELL("A11"), GBKToUTF8("请输入一个在0.1到0.5之间的小数").data(), format1); data_validation->validate = LXW_VALIDATION_TYPE_DECIMAL; data_validation->criteria = LXW_VALIDATION_CRITERIA_BETWEEN; data_validation->minimum_number = 0.1; data_validation->maximum_number = 0.5; worksheet_data_validation_cell(worksheet, CELL("B11"), data_validation); /* * 例6:限制输入为下拉列表中的值 */ worksheet_write_string(worksheet, CELL("A13"), GBKToUTF8("从下拉列表中选择一个值").data(), format1); char temp1[16] = ""; sprintf_s(temp1, GBKToUTF8("开").data()); char temp2[16] = ""; sprintf_s(temp2, GBKToUTF8("高").data()); char temp3[16] = ""; sprintf_s(temp3, GBKToUTF8("关").data()); char* list[] = { temp1, temp2, temp3, NULL }; data_validation->validate = LXW_VALIDATION_TYPE_LIST; data_validation->value_list = list; worksheet_data_validation_cell(worksheet, CELL("B13"), data_validation); /* * 例7:限制输入为下拉列表的值(下拉列表由单元格指定) */ worksheet_write_string(worksheet, CELL("A15"), GBKToUTF8("在下拉列表中选择一个值(下拉列表中的值来自单元格指定)").data(), format1); data_validation->validate = LXW_VALIDATION_TYPE_LIST_FORMULA; data_validation->value_formula = "=$E$4:$G$4"; worksheet_data_validation_cell(worksheet, CELL("B15"), data_validation); /* * 例8:限制输入为指定范围内的日期 */ worksheet_write_string(worksheet, CELL("A17"), GBKToUTF8("输入一个 1/1/2008 和 12/12/2008 之间的日期").data(), format1); lxw_datetime datetime1 = { 2008, 1, 1, 0, 0, 0 }; lxw_datetime datetime2 = { 2008, 12, 12, 0, 0, 0 }; data_validation->validate = LXW_VALIDATION_TYPE_DATE; data_validation->criteria = LXW_VALIDATION_CRITERIA_BETWEEN; data_validation->minimum_datetime = datetime1; data_validation->maximum_datetime = datetime2; worksheet_data_validation_cell(worksheet, CELL("B17"), data_validation); /* * 例9:输入指定范围内的时间 */ worksheet_write_string(worksheet, CELL("A19"), GBKToUTF8("输入 6:00 和 12:00之间的时间").data(), format1); lxw_datetime datetime3 = { 0, 0, 0, 6, 0, 0 }; lxw_datetime datetime4 = { 0, 0, 0, 12, 0, 0 }; data_validation->validate = LXW_VALIDATION_TYPE_DATE; data_validation->criteria = LXW_VALIDATION_CRITERIA_BETWEEN; data_validation->minimum_datetime = datetime3; data_validation->maximum_datetime = datetime4; worksheet_data_validation_cell(worksheet, CELL("B19"), data_validation); /* * 例10:限制输入的字符比指定的长度要长 */ worksheet_write_string(worksheet, CELL("A21"), GBKToUTF8("输入一个比3个字符长的字符串").data(), format1); data_validation->validate = LXW_VALIDATION_TYPE_LENGTH; data_validation->criteria = LXW_VALIDATION_CRITERIA_GREATER_THAN; data_validation->value_number = 3; worksheet_data_validation_cell(worksheet, CELL("B21"), data_validation); /* * 例11:基于公式来限制输入. */ worksheet_write_string(worksheet, CELL("A23"), GBKToUTF8("如果以下为真则允许输入一个值 \"=AND(F5=50,G5=60)\"").data(), format1); data_validation->validate = LXW_VALIDATION_TYPE_CUSTOM_FORMULA; data_validation->value_formula = "=AND(F5=50,G5=60)"; worksheet_data_validation_cell(worksheet, CELL("B23"), data_validation); /* * 例12:为数据验证显示提示信息 */ worksheet_write_string(worksheet, CELL("A25"), GBKToUTF8("选中此单元格时显示提示").data(), format1); data_validation->validate = LXW_VALIDATION_TYPE_INTEGER; data_validation->criteria = LXW_VALIDATION_CRITERIA_BETWEEN; data_validation->minimum_number = 1; data_validation->maximum_number = 100; data_validation->input_title = const_cast<char*>(GBKToUTF8("输入一个整数值:").c_str()); data_validation->input_message = const_cast<char*>(GBKToUTF8("在1到100之间").c_str()); worksheet_data_validation_cell(worksheet, CELL("B25"), data_validation); /* * 例13:输入错误时以错误对话框弹出提示 */ worksheet_write_string(worksheet, CELL("A27"), GBKToUTF8("当输入的值不在1到100之间时显示一个错误对话框").data(), format1); data_validation->validate = LXW_VALIDATION_TYPE_INTEGER; data_validation->criteria = LXW_VALIDATION_CRITERIA_BETWEEN; data_validation->minimum_number = 1; data_validation->maximum_number = 100; data_validation->input_title = const_cast<char*>(GBKToUTF8("输入一个整数值:").c_str()); data_validation->input_message = const_cast<char*>(GBKToUTF8("在1到100之间").c_str()); data_validation->error_title = const_cast<char*>(GBKToUTF8("输入的值不合法").c_str()); data_validation->error_message = const_cast<char*>(GBKToUTF8("输入的值必须在1到100之间").c_str()); worksheet_data_validation_cell(worksheet, CELL("B27"), data_validation); /* * 例14:输入错误时以提示对话框进行提示 */ worksheet_write_string(worksheet, CELL("A29"), GBKToUTF8("当输入的值不在1到100之间时显示一个提示对话框").data(), format1); data_validation->validate = LXW_VALIDATION_TYPE_INTEGER; data_validation->criteria = LXW_VALIDATION_CRITERIA_BETWEEN; data_validation->minimum_number = 1; data_validation->maximum_number = 100; data_validation->error_type = LXW_VALIDATION_ERROR_TYPE_INFORMATION; data_validation->input_title = const_cast<char*>(GBKToUTF8("输入一个整数值:").c_str()); data_validation->input_message = const_cast<char*>(GBKToUTF8("在1到100之间").c_str()); data_validation->error_title = const_cast<char*>(GBKToUTF8("输入的值不合法").c_str()); data_validation->error_message = const_cast<char*>(GBKToUTF8("输入的值必须在1到100之间").c_str()); // worksheet_data_validation_cell(worksheet, CELL("B29"), data_validation); /* 清理. */ free(data_validation); return workbook_close(workbook); }
由于内容比较多,所以我们不直接上效果图了,在第2小节里慢慢掰开了讲。
这次的例程比较,额,长。
因为这里面其实一共有14个例子。
但是这14个例子又有很强的重复性,因此它仍然是“一个”例程,而不是“14个”例程。
读者朋友们请先忽略例程头部的两个转码函数,这只是为了适用中文,小白冥思苦想的下策。关于这个问题小白也已经有专门文章有过解释,不明白的同学可以移步libxlsxwriter中文报错问题。小白在下面讲到这里的转码时也会再进行解释。
data_validation->validate = LXW_VALIDATION_TYPE_INTEGER;
data_validation->criteria = LXW_VALIDATION_CRITERIA_BETWEEN;
data_validation->minimum_number = 1;
data_validation->maximum_number = 10;
worksheet_data_validation_cell(worksheet, CELL("B3"), data_validation);
最简单的应用就是通过限定值的输入范围来控制输入:
worksheet_data_validation_cell
作为最终对数据验证作用的单元格起作用的函数;
另一个函数为worksheet_data_validation_range
,可以令数据验证对多个单元格起作用。
对例1来说,data_validation->validate
表示的是验证的类型,这里选择LXW_VALIDATION_TYPE_INTEGER
表示是整数型值验证。
data_validation->criteria
表示的是验证的规则,这里选择LXW_VALIDATION_CRITERIA_BETWEEN
表示“介于两个数之间”。
正因如此,以下指定上、下限:
data_validation->minimum_number
即下限为1;
data_validation->maximum_number
即上限为10。
于是例1 实际上就是在限制B3这个单元格的输入需要在1-10之间的整数,其他的输入皆为非法。
可见,当输入11时,就会弹出错误框提示不合法。
例2 其实和例1很相似,但有两个区别:
data_validation->validate = LXW_VALIDATION_TYPE_INTEGER;
data_validation->criteria = LXW_VALIDATION_CRITERIA_NOT_BETWEEN;
data_validation->minimum_formula = "=E3";
data_validation->maximum_formula = "=F3";
通过设置的参数可以看到,指定上下限的变量变成了minimum_formula
和maximum_formula
.分别指定Excel表格中的两个单元格,以其内容作为限定的上下限。
此时输入5,则为非法,一样弹出错误框提示。
例3 更简单,判断是不是需要比某个值更大。
data_validation->validate = LXW_VALIDATION_TYPE_INTEGER;
data_validation->criteria = LXW_VALIDATION_CRITERIA_GREATER_THAN;
data_validation->value_number = 0;
可以看到,criteria
变成了LXW_VALIDATION_CRITERIA_GREATER_THAN
;
value_number
设置为0。即需要输入数比0大。
效果图就不贴了,因为太简单。
例4跟例3相类似。
data_validation->validate = LXW_VALIDATION_TYPE_INTEGER;
data_validation->criteria = LXW_VALIDATION_CRITERIA_LESS_THAN;
data_validation->value_number = 10;
没啥要多说的,criteria
变成了LXW_VALIDATION_CRITERIA_LESS_THAN
.
例5输入的是小数
data_validation->validate = LXW_VALIDATION_TYPE_DECIMAL;
data_validation->criteria = LXW_VALIDATION_CRITERIA_BETWEEN;
data_validation->minimum_number = 0.1;
data_validation->maximum_number = 0.5;
注意validate
的类型变了,变成了LXW_VALIDATION_TYPE_DECIMAL
.
这也没啥要多说的,非常简单,不想贴图了。(小白最近加班有点过头,允许偷个懒)
oops,这里插入一则小白的炫耀帖(请允许本小白无法平抑有点激动的心情):在例7中小白找到了github代码中的一处bug,已经在github上跟libxlsxwriter的作者反馈(by xiaobai’s poor English)并得到了修改,这是小白的first issue in github. 传送门:围观可以,不要说话
本篇的重头戏来了。例6和例7,小白替大家连踩两坑,希望此处有点掌声(六个项目压在身上,早8晚9,晚上还在踩坑的小白是什么精神555555~)。
例6的坑不是由于libxlsxwriter本身引起的,纯纯的中文编码问题。
/* * 例6:限制输入为下拉列表中的值 */ worksheet_write_string(worksheet, CELL("A13"), GBKToUTF8("从下拉列表中选择一个值").data(), format1); char temp1[16] = ""; sprintf_s(temp1, GBKToUTF8("开").data()); char temp2[16] = ""; sprintf_s(temp2, GBKToUTF8("高").data()); char temp3[16] = ""; sprintf_s(temp3, GBKToUTF8("关").data()); char* list[] = { temp1, temp2, temp3, NULL }; data_validation->validate = LXW_VALIDATION_TYPE_LIST; data_validation->value_list = list; worksheet_data_validation_cell(worksheet, CELL("B13"), data_validation);
这里小白用了一个非常蹩脚的方式在做编码的转换,无奈,既要适应windows这位大爷的GBK程序源码编码,又要适应最终目标——office excel这位大爷的utf8编码格式。只能出此下策。
而且这里组合list
的时候需要注意,末尾需要以一个NULL
结尾。特别要注意的是,小白用了sprintf_s
函数来生成字符串,而不是在list
的定义那里直接赋值(因为会失败),想要尝试的同学也可以自己改一下看,看这个坑摔得疼不疼。
所以很可能小白采取的这一种折中的方式是唯一可行的方案。。。丑你也得接受一下。
下拉列表的效果图如下:
例7的坑小白已经用身躯帮大家填平了,现在大家直接去官网上看例程,bug已经修复(当然本篇博客中的代码也是没有bug的安全代码)。
原始代码中主要的错误为:
data_validation->validate = LXW_VALIDATION_TYPE_LIST;
这样赋值其实是不对的,但如果你是在例6的后面运行例7,很难发现这个错误,因为例6的值会影响到例7,而他们的下拉列表内容又一样,会生成一个看似正确的下拉列表。(正因为如此这个坑才这样难发现,但是如果你是按照错误的方式写的,你会发现无论怎么修改对应单元格中的内容,下拉列表的值都不会发生变化)。
data_validation->validate = LXW_VALIDATION_TYPE_LIST_FORMULA;
data_validation->value_formula = "=$E$4:$G$4";
例7和例6的最大差别在于validate
类型不同,设定后对应到value_formula
赋值。
可以看下效果图,这里小白把对应单元格中的值做了修改,看到下拉列表与单元格内容是匹配的。
例8和例9是进行了时间和日期的限定输入。
先看例8,注意lxw_datetime
的格式输入法。其他很简单。同上,不贴效果图。
lxw_datetime datetime1 = { 2008, 1, 1, 0, 0, 0 };
lxw_datetime datetime2 = { 2008, 12, 12, 0, 0, 0 };
data_validation->validate = LXW_VALIDATION_TYPE_DATE;
data_validation->criteria = LXW_VALIDATION_CRITERIA_BETWEEN;
data_validation->minimum_datetime = datetime1;
data_validation->maximum_datetime = datetime2;
再看例9,与例8仅有一点技巧,就是如何表示时间:表示日期的年月日全写0就好。
lxw_datetime datetime3 = { 0, 0, 0, 6, 0, 0 };
lxw_datetime datetime4 = { 0, 0, 0, 12, 0, 0 };
data_validation->validate = LXW_VALIDATION_TYPE_DATE;
data_validation->criteria = LXW_VALIDATION_CRITERIA_BETWEEN;
data_validation->minimum_datetime = datetime3;
data_validation->maximum_datetime = datetime4;
同上,此处不贴效果图。
例10的玩法有点适合英文。因为字符长度的计算,对于中文编码来说会有点混乱(小白已经在此事上吃了多少亏了,唉)。
data_validation->validate = LXW_VALIDATION_TYPE_LENGTH;
data_validation->criteria = LXW_VALIDATION_CRITERIA_GREATER_THAN;
data_validation->value_number = 3;
也没啥好注意的,validate
的类型是LXW_VALIDATION_TYPE_LENGTH
罢了。
一样,不用贴效果图。
例11有点像一个开关控制。如果公式表达式为true,则允许输入,反之则不允许输入。
data_validation->validate = LXW_VALIDATION_TYPE_CUSTOM_FORMULA;
data_validation->value_formula = "=AND(F5=50,G5=60)";
唯一需要注意的是这个类型的写法LXW_VALIDATION_TYPE_CUSTOM_FORMULA
.
此例中是需要F5单元格中为50,G5单元格中为60同时成立才允许输入。
例12到例14是带有输入提示信息的验证。
例12中添加了input_title
和input_message
两个变量的控制,分别表示提示框的标题,以及提示框的内容。需要注意的是这两者都有长度限制,不能过长。具体是多长参看文档,小白也不记得,这似乎不是很重要(谁会写一个太长的提示呢)
data_validation->validate = LXW_VALIDATION_TYPE_INTEGER;
data_validation->criteria = LXW_VALIDATION_CRITERIA_BETWEEN;
data_validation->minimum_number = 1;
data_validation->maximum_number = 100;
data_validation->input_title = const_cast<char*>(GBKToUTF8("输入一个整数值:").c_str());
data_validation->input_message = const_cast<char*>(GBKToUTF8("在1到100之间").c_str());
效果展示一下:
例13比例12多出了error_title
和error_message
。
data_validation->error_title = const_cast<char*>(GBKToUTF8("输入的值不合法").c_str());
data_validation->error_message = const_cast<char*>(GBKToUTF8("输入的值必须在1到100之间").c_str());
效果图贴一下,注意弹出的是错误提示框。
例14比例13只多出一样,但小白发现如果是中文编码时,一定要保证先定义error_type
,否则会报错,这应该又是编码的锅。
data_validation->error_type = LXW_VALIDATION_ERROR_TYPE_INFORMATION;
data_validation->input_title = const_cast<char*>(GBKToUTF8("输入一个整数值:").c_str());
data_validation->input_message = const_cast<char*>(GBKToUTF8("在1到100之间").c_str());
data_validation->error_title = const_cast<char*>(GBKToUTF8("输入的值不合法").c_str());
data_validation->error_message = const_cast<char*>(GBKToUTF8("输入的值必须在1到100之间").c_str());
贴下效果图,注意错误发生时提示框是一个信息提示而不是一个错误提示,这是与例13的区别。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。