表格宽度默认是终端的列数宽度(如果不能获取终端的列数,默认值是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参数将先读入一遍文件进行列宽选择。
**目前程序对第一行的依赖很重,如表格列数取决与第一行,需要考虑一种比较好的解决办法;
**着重考虑换行断单词问题;