UTF-8编码数据在命令行终端编织成表格并输出
1.简介
我最近在Fadore23操作系统上学习C语言,发现在学习数据结构与算法这方面知识的时候,数据域中常常都只有一个简单类型数据或者是一个字符串,这样纯学算法而不去考虑数据结构,学到最后觉得很空,因为算法的本质是去操作数据。后来突发奇想,想去实现一个类似于mysql一样的数据库,当然实现这样一个数据库对现阶段的我肯定是个遥远梦想,但是为了梦想总是得迈出步伐,而我的第一步就是对单表进行增、删、改、查。
众所周知,database都是有命令行终端进行操作的(sqlserver除外),那么数据以表格的形式在终端打印就成了我的第一个问题,为了后面的学习,我必须解决这个问题,通过了两天的努力,就有了今天的这篇博文,或许下面的代码效率低,也或许我的设计结构就不对,但是这是我的第一版,实现了自己的需求,后面我有了新的想法,我会不断的完善,或者重写。若您对我的代码有什么好的建议或者意见,请Email联系我。( Email: andensemail@163.com ) 对于您的关注我深表感激。
先上两张图看看^_^~
2.最需要解决的问题
Linux系统常用的是UTF-8编码,一个UTF-8的汉字在内存占用三个字节,而在终端打印时,它只占用二个字符的位置,那么表格的格式(各字段对齐)成了主要问题。
3.数据结构介绍
typedef struct table {
char *line;//字符串指针
struct table *pNext;//后续节点指针
} TABLE, *PTABLE;
将需要写在表格一行中的数据有规则的组合成一个字符串,放在动态内存中。然后创建一个TABLE节点Tnode,让Tnode.line指向字符串的首地址,而一张表格是由多行字符串组成的,那就用一个以TABLE 为节点的链表表示。 Tnode.pNext指向下一个行字符串。当需要将表格打印在终端时,只要遍历打印这个链表就行。
4.接口函数介绍( 我就写了三个 )
a. 打开一张表格
PTABLE OpenTable( char symble1, char symble2, char *tableName, char *tableTail, char **data );
char symble1: 这个符号用于画表格的外边框
char symble2: 这个符号用于画表格内行的分格,而列我用的是空白分格
char *tableName: 表格的名字
char *tableTail: 表格的尾行,用于写一些统计数据,本程序只实现了统计数据的个数,后面有需求时我再完善,统计数据用一个 ‘$’号,写在文本中。
chr **data: 这个就是要插入表格的数据,是一个字符串数组,长度任意,但最后一个字符指针指向 NULL
return : PTABLE数据类型的,是一个TABLE的指针。
使用: PTABLE pHead = NULL;//定义一个表的头指针
*data[10] = {
"学号 姓名 性别 年龄 年级 班级 语文成绩 数学成绩 英语成绩 总成绩",
"2008 王小晨 男 10 4 丙 95.5 100 98.5 294",
"2009 张胜 男 10 4 丙 94.5 99 97.5 291",
"2010 刘倩 女 10 4 丙 90.5 100 95.5 286",
"2011 Mary 女 10 4 甲 99.5 100 95.5 295",
NULL
};// 每个字段间可用 空格,逗号,问号,感叹号格开。
pHead = OpenTable( '*' , '-' , "成绩表" , "共计 $ 人" , data );//本函数的调用
b.在命令行终端打印表格
void PrintTable( PTABLE pHead );
PTABLE pHead: 是表指针,表格链表中每个节点都必须是 TABLE 类型的。
return void 此函数返回值为空,我们用的主要是这个函数的副作用遍历调用C语言的 I / O 库函数,在命令行终端打印。
使用:PrintTable( pHead );
c. 关闭一张表格
void CloseTable( PTABLE pHead );
PTABLE pHead: 是表指针,表格链表中每个节点都必须是 TABLE 类型的。
return void 此函数返回值为空,我们用的主要是这个函数的副作用,依次释放每个节点内动态分配的字符串空间与该节点的动态空间。
使用:CloseTable( pHead );
5.初始化配置文件
# define MALLOC( type, len ) (type*)malloc( sizeof(type) * len ) /*malloc预定义函数*/
# define TRUE 1 /* BOOL 真*/
# define FLASE 0 /* BOOL 假*/
# define PRE 15 /* 表格的前置空白数 */
# define COL 102 /* 表格的横向长度 */
# define DATACOL 11 /* 表格中最最大字段数 */
a. 后三个是表格的配置,可以根据自己的需求改变其值,也可以改变OpenTable函数,使这三个配置数据做为参数传入。
b. 表格没有列数限制,所有数据全部打印,数据量大的时候,需配置好终端。我后面会做一个有列数限制且带上下翻页的,我想这个主要问题在于缓存的设计。
6.代码
a.测试主函数
b.制表函数库
众所周知,database都是有命令行终端进行操作的(sqlserver除外),那么数据以表格的形式在终端打印就成了我的第一个问题,为了后面的学习,我必须解决这个问题,通过了两天的努力,就有了今天的这篇博文,或许下面的代码效率低,也或许我的设计结构就不对,但是这是我的第一版,实现了自己的需求,后面我有了新的想法,我会不断的完善,或者重写。若您对我的代码有什么好的建议或者意见,请Email联系我。( Email: andensemail@163.com ) 对于您的关注我深表感激。
先上两张图看看^_^~
2.最需要解决的问题
Linux系统常用的是UTF-8编码,一个UTF-8的汉字在内存占用三个字节,而在终端打印时,它只占用二个字符的位置,那么表格的格式(各字段对齐)成了主要问题。
3.数据结构介绍
typedef struct table {
char *line;//字符串指针
struct table *pNext;//后续节点指针
} TABLE, *PTABLE;
将需要写在表格一行中的数据有规则的组合成一个字符串,放在动态内存中。然后创建一个TABLE节点Tnode,让Tnode.line指向字符串的首地址,而一张表格是由多行字符串组成的,那就用一个以TABLE 为节点的链表表示。 Tnode.pNext指向下一个行字符串。当需要将表格打印在终端时,只要遍历打印这个链表就行。
4.接口函数介绍( 我就写了三个 )
a. 打开一张表格
PTABLE OpenTable( char symble1, char symble2, char *tableName, char *tableTail, char **data );
char symble1: 这个符号用于画表格的外边框
char symble2: 这个符号用于画表格内行的分格,而列我用的是空白分格
char *tableName: 表格的名字
char *tableTail: 表格的尾行,用于写一些统计数据,本程序只实现了统计数据的个数,后面有需求时我再完善,统计数据用一个 ‘$’号,写在文本中。
chr **data: 这个就是要插入表格的数据,是一个字符串数组,长度任意,但最后一个字符指针指向 NULL
return : PTABLE数据类型的,是一个TABLE的指针。
使用: PTABLE pHead = NULL;//定义一个表的头指针
*data[10] = {
"学号 姓名 性别 年龄 年级 班级 语文成绩 数学成绩 英语成绩 总成绩",
"2008 王小晨 男 10 4 丙 95.5 100 98.5 294",
"2009 张胜 男 10 4 丙 94.5 99 97.5 291",
"2010 刘倩 女 10 4 丙 90.5 100 95.5 286",
"2011 Mary 女 10 4 甲 99.5 100 95.5 295",
NULL
};// 每个字段间可用 空格,逗号,问号,感叹号格开。
pHead = OpenTable( '*' , '-' , "成绩表" , "共计 $ 人" , data );//本函数的调用
b.在命令行终端打印表格
void PrintTable( PTABLE pHead );
PTABLE pHead: 是表指针,表格链表中每个节点都必须是 TABLE 类型的。
return void 此函数返回值为空,我们用的主要是这个函数的副作用遍历调用C语言的 I / O 库函数,在命令行终端打印。
使用:PrintTable( pHead );
c. 关闭一张表格
void CloseTable( PTABLE pHead );
PTABLE pHead: 是表指针,表格链表中每个节点都必须是 TABLE 类型的。
return void 此函数返回值为空,我们用的主要是这个函数的副作用,依次释放每个节点内动态分配的字符串空间与该节点的动态空间。
使用:CloseTable( pHead );
5.初始化配置文件
# define MALLOC( type, len ) (type*)malloc( sizeof(type) * len ) /*malloc预定义函数*/
# define TRUE 1 /* BOOL 真*/
# define FLASE 0 /* BOOL 假*/
# define PRE 15 /* 表格的前置空白数 */
# define COL 102 /* 表格的横向长度 */
# define DATACOL 11 /* 表格中最最大字段数 */
a. 后三个是表格的配置,可以根据自己的需求改变其值,也可以改变OpenTable函数,使这三个配置数据做为参数传入。
b. 表格没有列数限制,所有数据全部打印,数据量大的时候,需配置好终端。我后面会做一个有列数限制且带上下翻页的,我想这个主要问题在于缓存的设计。
6.代码
a.测试主函数
/******************************************************************************************
* *
* 测试主函数 *
* *
******************************************************************************************/
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include "DrawTable.c"
int
main( void ){
PTABLE pHead1 = NULL,pHead2 = NULL, pHead3 = NULL;
char *data1[10] = {
"学号 姓名 性别 年龄 年级 班级 语文成绩 数学成绩 英语成绩 总成绩",
"2008 王小晨 男 10 4 丙 95.5 100 98.5 294",
"2009 张胜 男 10 4 丙 94.5 99 97.5 291",
"2010 刘倩 女 10 4 丙 90.5 100 95.5 286",
"2011 Mary 女 10 4 甲 99.5 100 95.5 295",
NULL
};
char *data2[10] = {
"编号 名称 数量 单价 产地",
"1001011 流量计 1000 4584.3 中国广州",
"1001012 液位计 1500 3363.6 中国湖南 aabbcc",
"1001013 压力变送器 1200 758.9 中国上海",
"1001014 温度变送器 2300 543.2 中国山东",
NULL
};
pHead1 = OpenTable('*','-',"成绩表","共计 $ 人" ,data1 );
PrintTable( pHead1 );
CloseTable( pHead1 );
pHead1 = NULL;
pHead2 = OpenTable('*','-',"这是一张产品清单","共计 $ 种" ,data2 );
PrintTable( pHead2 );
CloseTable( pHead2 );
pHead2 = NULL;
pHead3 = OpenTable('*','-',"公共信息","共计 $ 种" ,NULL );
PrintTable( pHead3 );
CloseTable( pHead3 );
pHead3 = NULL;
return EXIT_SUCCESS;
}
b.制表函数库
/************************************************************************************************
* *
* 命令行终端上画表格函数库 *
* *
************************************************************************************************/
/* 预处理器可以移到自己的头文件或配置中
* */
# define MALLOC( type, len ) (type*)malloc( sizeof(type) * len )
# define TRUE 1
# define FLASE 0
# define PRE 15 /* 表格的前置空白数 */
# define COL 102 /* 表格的横向长度 */
# define DATACOL 11 /* 表格中最最大字段数 */
/****************************************声明*************************************************/
/* 数据结构 */
typedef struct table {
char *line;//字符串指针
struct table *pNext;//后续节点指针
} TABLE,*PTABLE;
/* 接口函数 */
PTABLE OpenTable(char symble1, char symble2, char *tableName, char *tableTail, char **data );//打开一张表格
void PrintTable( PTABLE pHead );//在stdout上打印一张表格
void CloseTable( PTABLE pHead );//关闭一张表格
/* 内部画图函数 */
static PTABLE CreateTable();//创建一个表格
static int DrawNull( PTABLE pHead, char symble ); //以symble字符画一条空心直线
static int DrawLine( PTABLE pHead, char symble ); //以symble字符画一条实心直线
static int DrawTitle( PTABLE pHead, char const *name, char symble );//画标题栏
static int DrawData( PTABLE pHead, char **data, char symble );//填充并统计数据,第一行为导航
static int DrawTail( PTABLE pHead, char *tableTail,int totleData, char symble);//画结尾统计行
/* 内部功能函数 */
static void AppendTable( PTABLE pHead, PTABLE new );//追加一个节点
static void CopyData( char *line, char *data, int *dataPos );// 字符串格式化copy
static void GetDataPos(char *buffer,int *dataPos , int offset );//在数据行中定位每个数据字段
static void GetUtf8Length( char const *str, int *cnt );//utf-8编码的字符串统计
/***********************************END**********************************************/
/*********************************接口函数*******************************************/
/*
* Function: 打开一张表格
* symble1: 符号1,用于画表格的边框
* symble2: 符号2,用于画表格中的分割横线
* tableName: 表格名称,用于写入名称栏
* tableTail: 表格统计字符串的格式
* data: 表格中的数据,是一个字符串数组
* return: 返回一个表指针PTABLE,如果打开失败返回NULL
* */
PTABLE
OpenTable( char symble1, char symble2, char *tableName, char *tableTail, char **data ){
PTABLE pHead = NULL;
pHead = CreateTable();//初始化一个头节点
if( pHead != NULL ){
int totalData = 0; //数据行统计器
DrawLine( pHead, symble1); //画横线
DrawTitle( pHead, tableName, symble2); //写表格名
DrawLine( pHead, symble2); //画横线
totalData = DrawData( pHead, data, symble1 ); //写数据
DrawLine( pHead, symble2); //画横线
DrawTail( pHead, tableTail,totalData, symble2); //画结尾统计
DrawLine( pHead, symble1); //画横线
}
return pHead;
}
/*
* Function: 在标准输出打印表格
* pHead: 表格指针
* return: void
* */
void
PrintTable( PTABLE pHead ){
//链表拥有头节点,头节点中无数据,故跳过
while( pHead = pHead->pNext, pHead != NULL )
printf( "%s\n", pHead->line );
}
/*
* Function: 关闭一个表格( 释放所有动态分配的指针 )
* pHead: 表格指针
* return: void
* */
void
CloseTable( PTABLE pHead ){
PTABLE p;
//临时指针指向当前节点,当前节点指针前移,释放临时指针所指节点中所有的动态内存
while( p = pHead, p != NULL){
pHead = pHead->pNext;
free( p->line);
p->line = NULL;//拴野指针
free( p );
p = NULL;//拴野指针
}
}
/***********************************END**********************************************/
/*********************************内部画图函数*******************************************/
/*
* Function:创建一个表格 (分配一个头节点)
* return: PTABLE 失败则返回NULL
* * */
static PTABLE
CreateTable(){
PTABLE pHead;
//动态分配一个节点
pHead = MALLOC( TABLE , 1 );
if( pHead != NULL){
//该节点不包涵任何数据
pHead->line = NULL;
pHead->pNext = NULL;
}
return pHead;
}
/* function: 画一条空心直线 if( symbel == '*' ) line = " * *\0"
* pHead: 表格指针.
* symble: 制线符号,用symble这个字符为表格画两端.
* return: TRUE 画线成功, FLASE 画线失败,且释放了相应内存.
* */
static int
DrawNull( PTABLE pHead , char symble ){
//定义新节点空间并为其分配内存
PTABLE new;
new = MALLOC( TABLE, 1 );
if( new != NULL ){
//为新节点分配字符串内存
new->line = MALLOC( char, PRE+COL);// PRE 表格前的空格数, COL表格行的长度
if( new->line != NULL ){
//画空心直线 ( 两头有字符,中间是空格,结尾加上NUL )
memset( new->line, 32, PRE+COL );// PRE+COL个字符全部画成空格
memset( new->line+PRE, (int)symble, 1 );//第一个位置画上符号
memset( new->line+PRE+COL-2, (int)symble, 1 );//倒数第二个位置画上符号
memset( new->line+PRE+COL-1, 0 , 1 );//倒数第一个位置写入NUL,标致字符串结束
/*当style == '*'时 行的表达:| * *\0| */
//追加
new->pNext = NULL;//这步也可以放到追加函数中去
AppendTable( pHead, new );
return TRUE;//这是函数唯一正确的出口
}
//字符串内存分配失败时,释放节点的内存-----------拒绝内存泄漏,拴住野指针
free( new );
new = NULL; //拴住野指针
return FLASE;
}
return FLASE;
}
/*
* Function: 画一条实心直线 if(symble == '*') line = " *********************************\0"
* pHead: 表格指针.
* symble: 制表符号,用symble这个字符为表格画直线.
* return TRUE 画线成功, FLASE 画线失败,且释放了相应内存.
*/
static int
DrawLine( PTABLE pHead, char symble ){
//定义新节点并分配内存空间
PTABLE new;
new = MALLOC( TABLE, 1 );
if( new != NULL ){
//分配字符指针内存空间
new->line = MALLOC( char, PRE+COL );
if( new->line != NULL ){
//画出字符串
memset( new->line, 32, PRE+COL ); //字符串清空
memset( new->line+PRE, (int)symble, COL-1 ); //画线
memset( new->line+PRE+COL-1, 0, 1 );//加NUL字符
//追加节点
new->pNext = NULL;
AppendTable( pHead, new );
return TRUE;
}
//拒绝内存泄漏,拴住野指针
free( new );
new=NULL;
return FLASE;
}
return FLASE;
/*若有不清楚之处,参考DrawNull() 函数讲解*/
}
/*
* Function: 画标题栏 if( symble == '-') line = " - TABLENAME -\0"
* pHead: 表格指针
* name: 标题内容指针
* symble: 表格符号
* return TRUE 画线成功, FLASE 画线失败,且释放了相应内存.
* */
static int
DrawTitle( PTABLE pHead, char const *name, char symble ){
//定义新节点指针并分配动态内存
PTABLE new;
new = MALLOC( TABLE, 1 );
if( new != NULL ){
//统计utf8汉字的长度
int cnt[5];//定义字符串统计数组
memset( cnt, 0, sizeof(int) * 5 );//清空字符串统计数组
GetUtf8Length( name, cnt );//统计字符串中utf-8编码字符的个数。
/* 一个汉字以utf-8编码占3个字节。
* 一个汉字在终端显示,占2个字符的大小。
* 也就是说,一个汉字在内存中占了3个字符的位置,在终端打印时却只占用2个字符的大小。
* 问题:英文字符串与中文字符,让其文字个数一样,但是在终端显示长度却不一样,汉字短,刚好短汉字的个数个字符。
* PRE+COL+cnt[3], 故分配内存时就要加上被汉字多占的字符数,也就是加上汉字的个数。
* */
new->line = MALLOC( char, PRE+COL+cnt[3] );
if( new->line != NULL ){
//居中定位
int len,pos;
len = strlen( name );
pos = ( COL - len + cnt[3] - 1 ) / 2;
//画线
memset( new->line, 32, PRE+COL+cnt[3] );
memset( new->line+PRE, (int)symble, 1 );
memcpy( new->line+PRE+pos, name, len );//把标题字符串复制到,新字符串的居中位置
//这里还可以用 strncpy 函数, 有长度了就不要用 strcpy 函数,strcpy函数会复制 NUL字符
memset( new->line+PRE+COL+cnt[3] - 2, (int)symble, 1 );
memset( new->line+PRE+COL+cnt[3] - 1, 0, 1 );
//追加
new->pNext = NULL;
AppendTable( pHead, new );
return TRUE;
}
//拒绝内存泄漏,拴住野指针
free( new );
new = NULL;
return FLASE;
}
return FLASE;
}
/*
* Function:添加并统计数据
* if( symble = '*' ) " PTABLE: * xxx xxx xxxxx xx xxx xxxx *\0"
* * xx xx xxx xxx xx xx *\0"
* * xxxx x xxxx xx x xxxx *\0"
* .
* .
* .
* pHead: 表格指针
* data: 数据,以字符串数组存储的数据。
* symble: 制表符号
* return: 统计的数据数,以行为单位.
* */
static int
DrawData( PTABLE pHead, char **data, char symble ){
int totalData = 0; // 数据计数器
//用导航计算每个字段的定位点,整张表每条数据的每个字段的定位点都一样
int dataPos[DATACOL];//定位点数组, DATACOL为最大字段数
char buffer[COL];// strtok会破坏原字符串,所以要用一个缓存来解决这个问题
if( data != NULL && *data != NULL){//数据不为空
strcpy( buffer, *data );//数据写入缓存
GetDataPos( buffer, dataPos, 3 );//获取字段定位点数据
dataPos[1] += 1;//第一个字段定位点要存放制表符号,所以定位点得右移,让出这个位置
}else{
DrawNull( pHead,symble );
DrawNull( pHead,symble );
DrawTitle( pHead,"您还没有添加数据呢^_^",symble );
DrawNull( pHead,symble );
DrawNull( pHead,symble );
return totalData;
}
//循环追加数据行
while( data != NULL && *data != NULL ){
//定义新节点指针并分配动态内存
PTABLE new = NULL;
new = MALLOC( TABLE, 1 );
if( new != NULL ){
//统计汉字数量
int cnt[5];
memset( cnt, 0, sizeof(int) * 5 );
GetUtf8Length( *data, cnt );
//为字符串分配动态内存
new->line = MALLOC( char, PRE + COL + cnt[3]);//DrawTitle函数中的有详解
if( new->line != NULL ){
//画数据行
memset( new->line, 32, PRE+COL+cnt[3] );
memset( new->line+PRE, (int)symble, 1 );
strcpy( buffer, *data);// strtok会破坏原字符串,所以要用一个缓存来解决这个问题
// 在每个字段定位点上,写入每个字段
CopyData( new->line, buffer, dataPos );
memset( new->line+PRE+COL+cnt[3]-2, (int)symble, 1 );
memset( new->line+PRE+COL+cnt[3]-1, 0, 1 );
//追加
new->pNext = NULL;
AppendTable( pHead, new );
data++;//指向下一个字符串指针。
totalData++;//统计加1
continue;
}
free( new );
new = NULL;
continue;
}
continue;
}
return --totalData;//导航栏纳入统计
}
/* Function: 画结尾统计行 if( strcmp( tableTail, "共计 $ 人"))
* line = " * 共计 totalData 人 *\0";
* pHead: 表格指针
* tableTail: 统计风格
* totalData: 数据统计个数
* symble: 制表符号
* return TRUE 画线成功, FLASE 画线失败,且释放了相应内存.
* */
static int
DrawTail( PTABLE pHead, char *tableTail, int totalData, char symble){
//将整数保存为字符串并计算长度
char str[5];
int numLen;
sprintf( str, "%d", totalData );
numLen = strlen( str );
//以$分解tableTail为2个字符串并计算各长度
char pre[100], tail[100];
int lenP, lenT;
sscanf( tableTail, "%s $ %s", pre, tail );
lenP = strlen( pre );
lenT = strlen( tail );
//tableTail的汉字统计
int cnt[5];
memset( cnt, 0, sizeof(int) * 5 );
GetUtf8Length( tableTail, cnt);
//定义新节点指针并分配动态内存
PTABLE new;
new = MALLOC( TABLE, 1 );
if( new != NULL ){
//为字符串分配动态内存
new->line = MALLOC( char, (PRE+COL+cnt[3]) );
if( new->line != NULL){
//画统计行
memset( new->line, 32, (PRE+COL+cnt[3]) );
memset( new->line+PRE,(int)symble, 1 );
memcpy( new->line+PRE+COL-18,pre, lenP );
memcpy( new->line+PRE+COL-18+lenP+2,str, numLen );
memcpy( new->line+PRE+COL-18+lenP+numLen+4,tail, lenT );
memset( new->line+PRE+COL+cnt[3]-2,(int)symble, 1 );
memset( new->line+PRE+COL+cnt[3]-1,0, 1 );
//追加数据
new->pNext = NULL;
AppendTable( pHead, new );
return TRUE;
}
//拒绝内存泄漏,拴住野指针
free( new );
return FLASE;
}
return FLASE;
}
/**************************************内部功能函数 *****************************************/
/* Function:追加一个节点
* phead: 表格指针
* new: 新的节点指针
* return void
* */
static void
AppendTable( PTABLE pHead , PTABLE new ){
//寻找表格的最后一个节点
while( pHead->pNext != NULL )
pHead = pHead->pNext;
pHead->pNext = new;
//注: new->pNext = NULL 这向工作在创建new节点时完成。
return;
}
/*
* Function: 分配插入位置
* buffer: 导航字符串指针
* dataPos: 统计位置数组指针
* offset: 数据写入时的向右偏移量 如*data 偏移0个字节,* data 偏移1个字节,* data偏移2个字节....
* return void
* */
static void
GetDataPos(char *buffer,int *dataPos, int offset ){
//以 token 里的字符分解字符串 buffer, 统计字段数量
int i;
char *tmp;
char token[] = "/ ,!?|-";
for( i=0, tmp = strtok( buffer, token ); tmp !=NULL && i < DATACOL-1; tmp = strtok( NULL, token) )
i++;
dataPos[0] = i;//dataPos[1] 存放字段数量, dataPos[1]存放第一个字段的定位,dataPos[2].....
//将ave平分i份,得到每份的长度
int ave;
ave = COL / i; //数据行的长度平分给i个字段,由于不一点完全整除,可能最后一个会长一点。
//所有字段定位写入
for( i = 1; i <= dataPos[0]; i++ )
dataPos[i] = PRE + ave * (i-1) + offset;
// 该字段位置 = 前置空白 + 平均值 * 第几个字段 + 偏移量
return;
}
/*
* Function: 将数据写入表格已分配了好的空间
* *line: 已分配好的字符串空间
* *data: 被写入的数据
* *dataPos: 每行所有字段的定位
* return: void
* */
void
CopyData( char *line, char *data, int *dataPos ){
//分解数据字符串,将其写入subData字符指针数组
char *subData[DATACOL];//字段(字符)数组
char token[] = " ,/!?";
int i;//字段计数器
for( i=0, subData[i] = strtok( data, token); i < dataPos[0] && subData[i] != NULL;
subData[i] = strtok(NULL,token) )
i++;
//subData数组必须以NULL结尾
if( subData[i] != NULL )
subData[i] = NULL;//字符串指针数组以null结尾
//统计本条数据中,每个字段中文的个数,存放在pcnt数组中
int pcnt[DATACOL];
int cnt[5];
memset( pcnt, 0, sizeof(int) * DATACOL );//置0
for( i = 1; i <= dataPos[0]; i++ ){// pcnt 与 dataPos 一致,数据从 1 开始写
cnt[3] = 0;//汉字计数器清零
GetUtf8Length( subData[i-1], cnt );
pcnt[i] = cnt[3];
}
pcnt[0] = 0;//由于pcnt用于加法运算,写上这一行更安全。
//数据写入
int j, pos, len;
for( i = 1; i <= dataPos[0]; i++ ){
//数据行中,有了汉字后,计算其实际定位
for( j = i - 1, pos = 0; j > 0; j-- )
pos +=pcnt[j];//当前字段前面所有字段中汉字的总和
len = strlen( subData[i-1] );
strncpy( line+dataPos[i]+pos, subData[i-1], len );//复制数据
}
return;
}
/*
* Function: utf-8字符串统计函数
* str: 字符串指针
* cnt: 统计数组指针
* return void
* */
static void
GetUtf8Length( char const *str, int *cnt ){
//memset( cnt, 0 ,sizeof(int) * 5 );
//我的其它博文中有讲解
while( *str != '\0' ){
if( *str & 1<<7 ){
if( *str & 1<<6 ){
if( *str & 1<<5 ){
if( *str & 1<<4){
cnt[4]++, cnt[0]++, str += 4;/* 11110xxx*/
continue;
}
cnt[3]++, cnt[0]++, str += 3;/* 1110xxxx*/
continue;
}
cnt[2]++, cnt[0]++, str += 2;/* 110xxxxx*/
continue;
}
}
cnt[1]++, cnt[0]++, str += 1;/*0xxxxxxx*/
continue;
}
}
/********************************************END*****************************************/
/***********************************解除预定义, 让项目更安全*********************************/
# undef MALLOC
# undef TURE
# undef FLASE
# undef PRE
# undef COL
# undef DATACOL
/**************************************END OF FILE***************************************/
Writer: Anden Email: andensemail@163.com Time: 2016.04.04