昨天写完几篇文章后觉得意犹未尽,我想想了,既然字库文件是二进制文件,完全可以转化为十六进制,存储在数组中,这样在寻找字符时就不用操作文件了,直接在内存中获取。

经过一番调研,证明这个思路是对的,是具有可行性的,同时也具有很强的实践意义的。(此为胡扯,不可相信)

 

这次写的代码全部使用C语言标准库中与文件有关的函数,所涉及的函数均以“f”开头,做到了平台无关性,为跨平台打下基础,具有很强的移植性。不过由于时间、精力、金钱、能力、水平关系,没有在vc6.0、vs2008、MiniGW、Dev c++下一一测试。

无特别说明,文中说的“ASCII字库”是指ASC16文件,完整给出了0~255的字符。“汉字字库”是指HZK16,包含了GB2312编码中的字符。

 

啥也别说,直接上代码,完整的代码如下:

#include <stdio.h>

//#define ASCII

#ifdef ASCII  /* ascii */
#define ZK  "ASC16"
#define OUT_FILE  "ascii16.h"
#define ARRAY  "ascii16"

#else  /* hz */

#define ZK  "HZK16"
#define OUT_FILE  "hzk16.h"
#define ARRAY  "hzk16"

#endif

int main( void)
{
         int i;
        FILE *fp_c;
        FILE *fp;
         int len;
         unsigned  char mat[ 32] = { 0};
        
        fp   = fopen(ZK,  "rb");
        fp_c = fopen(OUT_FILE,  "w+");
        
         /* get file size */
        fseek(fp,  0, SEEK_END);
        len = ftell(fp);
         //printf("len: %d/n", len);
        fprintf(fp_c,  "/******************************************************//n");
        fprintf(fp_c,  "/* Font file powered by Late Lee *//n");
        fprintf(fp_c,  "/* http://www.latelee.org *//n");
        fprintf(fp_c,  "/* %s %s *//n", __DATE__, __TIME__);
        fprintf(fp_c,  "/******************************************************//n");
        fprintf(fp_c,  "unsigned char %s[] = /n{/n", ARRAY);
         /* for ascii */
         #ifdef ASCII
         //for (i = 0; i < len; i += 16) /* full */
         for (i =  0x20* 16; i < len/ 2; i+= 16)  /* 96 printable ascii code */
        {
                fseek(fp,i,SEEK_SET);
                fread(mat, 16, 1,fp);
                fprintf(fp_c,  "/* %d 0x%x ' %c ' *//n", i/ 16, i/ 16, i/ 16);
                fprintf(fp_c, 
                 "0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,/n/n",
                mat[ 0],mat[ 1],mat[ 2],mat[ 3],mat[ 4],mat[ 5],mat[ 6],mat[ 7],mat[ 8],mat[ 9],mat[ 10],mat[ 11],mat[ 12],mat[ 13],mat[ 14],mat[ 15]);
                
        }
         #else
         for (i =  0; i < len; i +=  32)
        {
                fseek(fp,i,SEEK_SET);
                fread(mat, 32, 1,fp);
                fprintf(fp_c, 
                 "0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,/n",
                mat[ 0],mat[ 1],mat[ 2],mat[ 3],mat[ 4],mat[ 5],mat[ 6],mat[ 7],mat[ 8],mat[ 9],mat[ 10],mat[ 11],mat[ 12],mat[ 13],mat[ 14],mat[ 15],
                mat[ 16],mat[ 17],mat[ 18],mat[ 19],mat[ 20],mat[ 21],mat[ 22],mat[ 23],mat[ 24],mat[ 25],mat[ 26],mat[ 27],mat[ 28],mat[ 29],mat[ 30],mat[ 31]);
                
        }
         #endif
        fprintf(fp_c,  "};/n");
        fprintf(stdout,  "Job done!/n");
         return  0;
}

代码毫无算法可言,如果一定要说点什么,步骤大约是这样的:

1、打开字库文件,创建字库数组头文件,使用的函数为fopen。

2、获取字库文件长度大小,使用fseek和ftell函数。

3、由于ASCII字库中ASCII码占16个字节的空间,因此以16字节为单位,逐一读取该文件的中数据。读取完整的字库文件条件为:

for (i = 0; i < len; i += 16)

但是ASCII中只有区区96个可打印的字符,因此为节省空间起见,就将那么字符存储起来,条件为:

for (i = 0x20*16; i < len/2; i+=16)

至于为什么,前面强调了“实践性”,当然是实践得到的。由于ASC16包含了0~255个,一半即为0~127,可打印字符从0x20处开始,即0x20是第一个可打印的字符——虽然它是空格。有了循环条件,就可以合理地读取,保存到文件中了。使用fseek定位某个字符的偏移,使用fread读取该偏移处的16个字符。之后再使用fprintf写入另一文件中。当然可以每次读取一个字节,写入一个字节,也可以读取32个字节,写入32个字节。至于

fprintf(fp_c,  "/* %d 0x%x ' %c ' *//n", i/ 16, i/ 16, i/ 16);

主要是打印这个ASCII的十进制、十六进制以及它本身显示的字符,从后面三个数可以看出,这几个东西其实是一个东西,本质是一样,只不过表现形式不一样而已。

至于汉字字库,一样的道理,只是以32个字节为一单位。

 

以ASCII为例,下面是生成的ascii16.h文件的部分内容:

/******************************************************/
/*          Font file powered by Late Lee             */
/*             http://www.latelee.org                 */
/*              May 26 2011 07:08:09                  */
/******************************************************/
unsigned  char ascii16[] = 
{
/*   32     0x20     '   ' */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

/*   33     0x21     ' ! ' */
0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,

/*   34     0x22     ' " ' */
0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

/*   35     0x23     ' # ' */
0x00, 0x00, 0x00, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00,

/*   36     0x24     ' $ ' */
0x18, 0x18, 0x7c, 0xc6, 0xc2, 0xc0, 0x7c, 0x06, 0x06, 0x86, 0xc6, 0x7c, 0x18, 0x18, 0x00, 0x00,

/*   37     0x25     ' % ' */
0x00, 0x00, 0x00, 0x00, 0xc2, 0xc6, 0x0c, 0x18, 0x30, 0x60, 0xc6, 0x86, 0x00, 0x00, 0x00, 0x00,
/* 省略很多 */
}


上面格式不太整齐,原因在前面的文章已经说了,其实在编辑器中是非常整齐大方的。

研究成果已经出来了,那么要看看它能不能在实践中经受得起考验。

下面是是昨天经过修改后,并使用上面程序生成的英文字库数组及中文字库数组用ncurses来显示的完整代码:

#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <ncurses.h>  /* ncurses库头文件 */

#include  "ascii16.h"
#include  "hzk16.h"

#define ascii_code ascii16
#define hzk_code hzk16

/* for debug */
//#define DEBUG
#ifdef DEBUG
#define debug(fmt, ...) printw(fmt, ##__VA_ARGS__)
#else
#define debug(fmt, ...)
#endif

static  void __display_font( int y,  int x,  unsigned  char *mat,  char *code)
{
         int i, j, k;
         for(i= 0;i< 16;i++) {
                 for(j= 0;j< 2;j++)
                {
                         for(k= 0;k< 8;k++)
                        {
                                 /* 从高位开始,逐位相与,为1者,输出“*” */
                                 if(mat[i* 2+j] & ( 0x80>>k))
                                        mvprintw(y+i, x+j* 8+k, code);
                                 else
                                        mvprintw(y+i, x+j* 8+k,  " ");
                        }
                }
        }
        refresh();
}

static  void __display_ascii( int y,  int x,  unsigned  char *ascii,  char *code)
{
         int i, j;
         int bits;
         for(i= 0;i< 16;i++) {
                bits = ascii[i];
                 for(j= 0;j< 8;j++, bits<<= 1) {
                         if (bits &  0x80)
                                mvprintw(y+i, x+j, code);
                         else
                                mvprintw(y+i, x+j,  " ");
                }
        }
        refresh();
}

/*
* 打印ASCII,使用96个可打印字符版本的ASCII码数组
* y:屏幕行
* x:屏幕列
* font:ASCII字符串
* note:注意函数中的unsigned char*类型
*/
void display_ascii( int y,  int x,  unsigned  char *font)
{
         unsigned  char *p_ascii;
         int offset;
         unsigned  char *p = font;

         while (*p !=  0) {
                offset = (*p -  0x20 ) *  16;
                 //offset = *p * 16;
                p_ascii = ascii_code + offset;
                 //debug("offset: %x %x/n", p_ascii, offset);
                __display_ascii(y, x, p_ascii,  "*");
                x +=  10;
                p++;
        }
}

/*
* 打印汉字,使用HZK16文件
* fp:汉字库文件指针
* y:屏幕行
* x:屏幕列
* font:汉字字符串
* note:注意函数中的unsigned char*类型
*/
void display_hz(FILE *fp,  int y,  int x,  unsigned  char *font)
{
         unsigned  char mat[ 32]={ 0};
         int qh,wh;
         unsigned  long offset;
         unsigned  char *p = font;

         while (*p !=  0) {
                qh = *p   -  0xa0;
                wh = *(p+ 1) -  0xa0;
                debug( "code : %x %x/n", *p, *(p+ 1));
                offset = (  94*(qh- 1) + (wh- 1) ) *  32;
                debug( "qh: %x wh: %x offset: %x/n", qh, wh, offset);
                fseek(fp,offset,SEEK_SET);
                fread(mat, 32, 1,fp);
                __display_font(y, x, mat,  "*");
                x +=  18;
                p+= 2;         /* 中文字符,移动2个字节 */
        }
}

/*
* 打印字符,中英文混合版本,不使用字库文件
* y:屏幕行
* x:屏幕列
* font:字符串
* note:注意函数中的unsigned char*类型
*/
void display_font( int y,  int x,  unsigned  char *font)
{
         int qh,wh;
         unsigned  long offset;
         unsigned  char *p = font;
         unsigned  char *p_ascii;
         unsigned  char *p_hzk;

         while (*p !=  0) {
                qh = *p   -  0xa0;
                wh = *(p+ 1) -  0xa0;
                 if (qh >  0 && wh >  0){
                        debug( "code : %x %x/n", *p, *(p+ 1));
                        offset = (  94*(qh- 1) + (wh- 1) ) *  32;
                        debug( "qh: %x wh: %x offset: %x/n", qh, wh, offset);
                        p_hzk = hzk_code + offset;
                        __display_font(y, x, p_hzk,  "*");
                        x +=  18;  /* 16或以上 */
                        p+= 2;         /* 中文字符,移动2个字节 */
                }  else {
                         int offset1;
                        offset1 = (*p -  0x20 ) *  16;
                        p_ascii = ascii_code + offset1;
                        __display_ascii(y, x, p_ascii,  "*");
                        x +=  10;  /* 8或以上 */
                        p+= 1;         /* 英文字符,移动1个字节 */
                }
        }
}

int main()
{
         //unsigned char incode[] = "我Az你个pf"; /* 全部是中文字符,英文为全角状态下输入 */
         //unsigned char incode[] = "波神留我看斜阳"; /* 全部中文 */
         //unsigned char incode[] = "人生如梦"; /* 全部中文 */
         //unsigned char incode[] = "I'm Late Lee"; /* 全部英文 */
         unsigned  char incode[] =  "我AZ你个pf";        /* 中文、英文 */

        initscr();         /* init screen */

        display_font( 1,  0, incode);

         //getch(); /*暂停*/
        endwin();  /* close it */
         return  0;
}fate/article/details/6446927


至于代码中为什么将y放到前面,是因为这样更能直观表示我们对面的屏幕的坐标,比如

 display_font( 1,  0, incode);

就表明了在第1行,第0列显示incode数组的字符。

 

经过修改后的代码比较整洁,效果与前面的一致,虽然没有了文件的操作,但是占用内存空间比较大,这个代码编译得到的可执行文件大小有271KB,算比较大的了。在操作文件及占用空间之间如何选择,就仁者见仁,智者见智了。

 

(实践证明,本研究符合当初的设计,经过一段时间的使用,达到预期目标,在生产实践中具有很强的指导意义及教育意义。为将来进一步研究打下牢固的基础。)

 

至此,近来的研究暂时告一段落,以后搞些什么,再说吧。