确定表格宽度
表格宽度默认是终端的列数宽度(如果不能获取终端的列数,默认值是80个半角英文字符宽度),用户可以另指定,需要注意的是,为了表格能较优雅的输出,表格宽度=各列宽度之和+列分隔符占的宽度,在确定列宽的时候,表格宽度保持不变。
确定表格列数
表格的列数由表格第一行的列数确定,第一行是第一行非模式匹配原样输出的行。需要用户确保每行的列数相等,如果某行中列数如果多于表格列数,多于的列被忽略;如果某行列数少于表格的列数,以空列补齐。
确定表格列宽
考虑到中文等,为了表格的能正常显示,定义表格列的最小宽度为foco_COLWIDTH_MIN,其值大于等于2个半角英文字符宽度;同时要确保各列宽度与列分隔符宽度之和等于表格宽度。
读取一遍文件确定列宽方法
这种情况下,可以指定某些列不需要换行输出,该列采用其各行中最宽的宽度为列宽(这显然需要先读入一遍文件确定,因此这只适合于从文件输入)。
基本方法是读取一遍文件,统计每列的所有字符宽度,列宽就是此列的字符宽度占文件所有字符宽度的比例,原样输出的行和不换行的列不在统计范围之内,特别注意的是要确保各列宽度不小于表格列最小宽度,这里采用先为每列预留出最小宽度的方法。注意如果正常列的字符宽度和是0,此时这些列均分未分配的宽度。
/* 代码参见libfoldcolumn/foldcolumn.c */
void foco_initColsWidthByReadFile(foco_t*pFoco,void**inFiles,foco_argv_t type,int fNum){
for(每个文件中的一行数据){
if(!该行原样输出){
if(还未确定表格列数)
表格列数 pFoco->colNum=本行列数;
for(该行中的每一列i)
if(用户指定该列不换行){
pFoco->widths[i]=max(pFoco->widths[i],该行中第i列字符宽度);
}else{
pFoco->widths[i]+=该行中第i列字符宽度;
sumWidth+=该行中第i列字符宽度;
}
}
}
/*表格未分配的宽度*/
leftWidth=pFoco->width;
/*确保各列宽度 大于等于 表格列最小宽度*/
for(表格的每一列){
if(用户指定该列不换行){
pFoco->widths[i]=max(pFoco->widths[i],列最小宽度);
leftWidth-=pFoco->widths[i];
}else{
pFoco->widths[i]+=该行中第i列字符宽度;
sumWidth+=该行中第i列字符宽度;
}
/*正常列个数*/
++colNum;
}
/*为各正常列预留最小列宽*/
leftWidth-= colNum*列最小宽度;
/*减去列分隔符宽度*/
leftWidth=foco_minusDeliWidth(pFoco,leftWidth);
for(表格的每一正常列){
if(width>0)
pFoco->widths[colIndex]=(double)pFoco->widths[colIndex]/width*leftWidth;
else
pFoco->widths[colIndex]=leftWidth/notMatchNum;
pFoco->widths[i]+=列最小宽度
}
为确保各列宽度与列分隔符和等于表格宽度,需将表格剩余宽度附加到最后一列
}
用户指定列宽
用户指定部分或全部列宽度,其余列均分剩余未分配的宽度,此方法适用性好,适用于从文件、管道等读入数据。
/*
* 表格列数为pFoco->colNum
* 用户指定的列宽数为pFoco->widthsNum
* 用户指定的列宽存储在pFoco->widths中
*/
void foco_specifyColsWidth(foco_t*pFoco){
/*判断列宽的有效性,如果有效返回table width - sum(pFoco->widths[i]);*/
/*0=<i<用户指定的列宽数与表格的实际列宽数之间的较小者*/
int leftWidth=foco_isValidColsWidth(pFoco);
/*减去列分隔符宽度*/
leftWidth=foco_minusDeliWidth(pFoco,leftWidth);
/*用户已指定列宽的列数*/
int i=pFoco->widthsNum;
if(leftWidth<0){
/*column width error*/
exit(1);
}
/*注意最后一列列宽,确保表格宽度等于各列宽度与列分隔符宽度之和 */
if(pFoco->colNum > pFoco->widthsNum){
/*用户指定的列宽数小于表格的列数,未指定列均分剩余宽度*/
average=leftWidth/(pFoco->colNum-pFoco->widthsNum);
/*平均值小于最小列宽*/
if(average<foco_COLWIDTH_MIN){
/*column width error*/
exit(1);
}
for(i;i<pFoco->colNum-1;++i)
pFoco->widths[i]=average;
/*last column width*/
pFoco->widths[i]=leftWidth-average*(pFoco->colNum-pFoco->widthsNum-1);
}else{
/* 用户指定的列宽数大于表格的列数,将剩余宽度加在最后一列上*/
pFoco->widths[pFoco->colNum-1]+=leftWidth;
}
pFoco->widthsNum=pFoco->colNum;
}
表格列对齐方式的实现
用户可以指定每列的对齐方式,如果用户不指定,默认是数字采用右对齐,其他左对齐,对齐方式通过列内填充空格实现,同在考虑中。
注:列内补齐填充空格字符,行、列分隔符以及填充字符均是半角。
支持的表格样式
样式一
列间用space分隔,行间(文本行,不是表格行)采用空行分隔,如下图所示:
样式二
行间采用'-'分隔,列间采用space分隔,如下图所示:
样式三
列间采用'|'分隔,行间采用空行分隔,如下图所示:
样式四
采用box-drawing characters(参见开源夏令营之foldcolumn工具及解决方案之box-drawing character)画表,如下图所示:
表格绘制的实现
确定了表格的上述属性后,接下来就是循环读如一行数据,将数据以分隔符分列,然后 输出行分隔符,循环输出列、列分隔符进行表格输出。
下面是简单的结合表格样式和表格列宽的不同选择方法的简单测试,命令行参数-t选择表格样式,-w指定部分列宽,其余均分,不指定-w参数将先读入一遍文件进行列宽选择。
读一遍文件按字符宽度比例确定列宽
各列均分表格宽度
指定部分列宽
自动换行效果
右对齐实现
下一步计划
**目前程序对第一行的依赖很重,如表格列数取决与第一行,需要考虑一种比较好的解决办法;
**着重考虑换行断单词问题;