点阵字和字模
?
前不久,在網上看到一個生成點陣字的網站。覺得很有意思!
到底什么是點陣字,點陣字和字模之間有什么關系?
讓我們先看一個點陣漢字和一個英文字母:
** ?????
************************** ?????
** ?????
** ?????
** ?????
** ?????
** ** ?????
************************** ?????
** ?????
** ?????
** ?????
** ?????
** ?????
** ** ?????
****************************** ?????
????????? ........?????????????
????????? ...#....?????????????
????????? ..###...?????????????
????????? .##.##..?????????????
????????? ##...##.?????????????
????????? ##...##.?????????????
????????? #######.?????????????
????????? ##...##.?????????????
????????? ##...##.??????? ??????
????????? ##...##.?????????????
????????? ##...##.?????????????
????????? ........?????????????
????????? ........?????????????
????????? ........?????????????
????????? ........?????????????
????????? ........???????????
這就是點陣字,也就是根據字符的字模用符號畫出來的,當然你可以把*號#號改成其它的任何符號都可以。
是不是覺得很有意思了?
漢字內碼:
我們都知道,英文只有少數的幾十個字符,在計算機中用一個字節可以很容易的表示出來(也就是ASCII碼);而漢字由于結構本身的原因,數量很大,常用的也有幾千個。顯然計算機中按照英文字符的方式對處理漢字是不可取的。
由是前人們就將ASCII表的高128位很少用到的數值以兩個為一組來表示漢字,這就是漢字的內碼。而剩下的低128位則留給英文字符使用,即英文的內碼。
看一個C程序示例:
程序代碼
main()
{
??? unsigned char *s,*e="A",*c="王";
??? clrscr();
??? printf("English char =");
??? s=e;
??? while(*s!=0) /*C的字符串以0為結束符*/
??? {
??????? printf("%3d,",*s);
??????? s++;
??? }
??? printf("\nChinease char=");
??? s=c;
??? while(*s!=0)
??? {
??????? printf("%3d,",*s);
??????? s++;
??? }
??? getch();
}
編譯運行以后,輸出的結果為:
English char = 65,
Chinease char= 205,245,
查ASCII碼字符表,很容易得到A的ASCII碼為65。
我們可以查ASCII碼表,得到出ASCII碼對應的字符,那我們有什么辦法來知道一個漢字內碼對應的漢字了?
讓我們先來認識一下區位碼:
1981年5月,我國國家標準總局頒布了《信息交換用漢字編碼字符集》(GB2312-80),簡稱國家標準漢字編碼,也叫國標碼。國標碼共收進標準字符7445個。其中一級漢字3755個,二級漢字3008個,共計6763個漢字。 由于漢字的字符多,一個字節(即8位二進制代碼)不足以表示所有的常用漢字。漢字國標碼的每個漢字或符號在計算機中都使用2個字節(16位二進制)代碼來表示。
在GB2312-80代碼表中,縱向分為0~93,共94行。將行號稱為區號,列號稱為位號,分別有94個區和94個位。區號和位號用十進制表示,不足兩位前面補0。這樣每個漢字或符號都可用4位十進制表示。這就是我們常說的區位碼。每一區共有94個漢字,而位記錄該漢字在該區中的具體位置。(記得我們以前讀書的時候,報考計算機考試,填寫姓名都要我們用區位碼填,我們都拿著自己的姓名一個個去查,我們查的那個東東就是區位碼。現在想想,真是心寒呀。還好,我后來就沒有查了,弄了個excel的宏。把班上人的姓名全部放到一個excel中,然后一點鼠標,哈哈……? 區位碼全自動出來了)。
現在我們知道,可以從區位碼得到漢字,也可以從漢字反查出區位碼。那么我們如何從內碼得到區位嗎?
漢字內碼與區位碼之間有一個簡單的數學關系:?
?內碼高字節 = 區碼+A0H = 區碼+160?
?內碼低字節 = 位碼+A0H = 區碼+160
這個轉換關系,我也不清楚原因,有誰知道還望指點。網上的大師們說這樣轉換,咱們就這樣轉換吧。
我們剛剛輸出“王”的內碼為:205(高字節),245(低字節)。由上面的換算關系,可以得到“王”字的區位碼為:
區碼=205-160=45
位碼=245-160=85
查一下區位碼表,4585所表示的漢字正好是“王”。
也可以打開輸入法,選擇內碼輸入法,然后選擇區位碼,輸入4585,就會輸出“王”字。
漢字字模:
現在讓我們來認識一下什么是字模,所謂字模就是是漢字(或者字符)的形態。字模中保存了漢字的點陣信息,記錄組成一個字符的點在何處顯示,在何處不顯示。我們只要得到漢字的字模,我們就可以很容易的程序來控制,把這個字符畫出來。
我們剛剛得到的僅僅是漢字的內碼,并根據漢字內碼得到區位碼,由區位碼查表得到漢字。那么我們如何來得到漢字的字模了?
?用過UCDOS(或者CCDOS,估計現在只有少數人還知道UCDOS是什么東東)的人應該知道,通過UCDOS可以讓DOS系統下正確的顯示中文目錄。不通過UCDOS之類的軟件,在純DOS下,我們看到的中文目錄會是一堆的亂碼,而英文目錄能夠正確顯示,這是什么原因了。
這是因為,英文的字模信息是一般固化在ROM里。中文字模信息一般記錄在一個專門的文件中,這個文件在UCDOS和CCDOS中都有,文件名是HZK16。也就是16x16點陣的漢字字模信息,所謂16x16,就是說這個漢字在橫向有16個點,和縱向16個點的區域里顯示。還有24x24,32x32等。
我們也可以在UCDOS下找到英文字模的信息文件,文件名是ASC16,這里記錄了英文字符的字模信息。ASC16文件記錄的英文字符是8x16點陣的。這些記錄字符字模信息的文件通常也叫字庫文件。
ASC16文件的大小剛好為 4K (4,096 個字節),每一英文字符橫向有8個點,縱向有16個點。也就是說要描述一個英文字符的點陣信息,必須要16*8=128bit=16Byte。而英文字符是一個字節表示,所能表示的最字符數為2的8次方,也就是256個字符(ASCII中是從0到255)。256個字符*16(每個字符要16個字節) = 4096 字節。 剛好為ASC16文件的大小。因此,我們要讀取英文字模的信息,我們就先得到這個字符的ASCII碼。
以得到字符"A"的字模信息為例:
假如我們要得到的字母A的字模信息,我們得到"A"的ASCII值為65,我們就可以算出字符"A"的字模信息在ASC16文件中的偏移量=(65*16)+1=1041字節(注意這個數字,我們將在后面用程序進行驗證),我們只需要從ASC16文件中1041字節開始讀取16個字節就可以得到"A"的字模信息了。
同樣,我們來看一下漢字字模。
漢字是16*16的所以描述一個漢字字模信息的大小為:16*16=256bit=32Byte,漢字是按照區位碼的順序來排列的。
我們以得到”王“字的字模信息為例:
我們先得到”王“字的內碼為:205,245,根據內碼與區位碼的轉換關系得到”王“字的區位碼為:45,85。
由前面區位碼介紹中,我們知道,每一區有94個漢字,位號表示在該區的位置。因此“王”字中區位碼中的位置為:
94*(區號-1) + (位號-1) = 94*((45-1)+(85-1)) = 4220。
而每一個漢字占32個字節,因此我們得到“王”字在字庫文件(HZK16)中的偏移量為:4220*32=135040字節(注意這個數字,我們在后面將用程序進行驗證)。我們只需要從HZK16文件中135040字節開始讀取32個字節就可以得到“王”字的字模信息了。
完整的原程序代碼如下:
程序代碼
/**********************************
* C 程序得到漢字字模信息
* by DreamTime [夢想年華]
* fanwsp@126.com???
* www.FreeAge.cn
?* 2007-11
***********************************/
#include "stdio.h"
/**********************************
* 得到英文字符的字模信息,存入數組
* 參數:
*?? *c:要得到字模信息的字符指針
*?? buffer[]:存儲得到字模信息的數組
* 無返回值
***********************************/
void getAscCode(char *c,char buff[])
{
??? unsigned long offset;
??? FILE *ASC;
??? /*打開字庫文件asc16*/
??? if((ASC=fopen("asc16","rb"))==NULL){
?????? printf("Can't open asc,Please add it?");
?????? getch();
?????? exit(0);
??? }
??? offset = *(c)*16+1;???????????? /*通過ascii碼算出偏移量*/
??? fseek(ASC,offset,SEEK_SET);???? /*將文件指針移動到偏移量的位置*/
??? fread(buff, 16, 1, ASC);??????? /*從偏移量的位置讀取32個字節*/
??? printf("ASCII:%d,offset:%d \n\r",*c,offset);
}
/**********************************
* 得到漢字字符的字模信息,存入數組
* 參數:
*?? *c:要得到字模信息的字符指針
*?? buffer[]:存儲字模信息的數組
* 無返回值
***********************************/
void getHzKCode(char *c,char buff[])
{
??? unsigned char qh,wh;
??? unsigned long offset;
??? FILE *HZK;
??? /*打開字庫文件hzk16*/
??? if((HZK=fopen("hzk16","rb"))==NULL){
?????? printf("Can't open haz16,Please add it?");
?????? getch();
?????? exit(0);
??? }
??? /*區碼=內碼(高字節)-160? 位碼=內碼(低字節)-160*/
??? qh???? = *(c) -0xa0;??????????? /*10進制的160等于16進制的A0*/
??? wh???? = *(c+1) -0xa0;????????? /*獲得區碼與位碼*/
??? offset = (94*(qh-1)+(wh-1))*32L;/*計算該漢字在字庫中偏移量*/
? ??fseek(HZK,offset,SEEK_SET);???? /*將文件指針移動到偏移量的位置*/
??? fread(buff,32,1,HZK);?????????? /*從偏移量的位置讀取32個字節*/
??? printf("qh:%d,wh:%d,offset:%ld\n\r",qh,wh,offset);
}
/**********************************
* 根據字模信息輸出英文字符
* 參數:
*?? *mat:字模指針
*?? *c1 :字模中為1的點顯示的字符,也就是前景字符
*?? *c2 :字模中為0的點顯示的字符,也就是背景字符
* 無返回值
***********************************/
void printAscChar(char *mat,char *c1,char *c2)
{
? int i,j;
? for(i=0;i<16;i++)???????????????? /* 8x16的點陣,一共有16行*/
? {
??? for(j=0;j<8;j++)??????????????? /*橫向一個字節8位,依次判斷每位是否為0*/
??????? if(mat[i]&(0x80>>j))??????? /*測試當前位是否為1*/
??????????? printf("%s",c1);??????? /*為1的顯示為字符c1*/
??????? else printf("%s",c2);?????? /*為0的顯示為字符c2*/
??? printf("\n");?????????????????? /*輸完一行以后,進行換行*/
? }
}
/**********************************
* 根據字模信息輸漢字字符
* 參數:
*?? *mat:字模指針
*?? *c1 :字模中為1的點顯示的字符,也就是前景字符
*?? *c2 :字模中為0的點顯示的字符,也就是背景字符
* 無返回值
***********************************/
void printHzKChar(char *mat,char *c1,char *c2)
{
? int i, j, k;
? for(i=0;i<16;i++)???????????????? /*16x16點陣漢字,一共有16行*/
? {
??? for(j=0;j<2;j++)??????????????? /*橫向有2個字節,循環判斷每個字節的*/
????? for(k=0;k<8;k++)????????????? /*每個字節有8位,循環判斷每位是否為1*/
??????? if(mat[i*2+j]&(0x80>>k))??? /*測試當前位是否為1*/
????????? printf("%s",c1);????????? /*為1的顯示為字符c1*/
???????? else printf("%s",c2);????? /*為0的顯示為字符c2*/
??? printf("\n");?????????????????? /*輸完一行以后,進行換行*/
?? }
}
/**********************************
* 主函數
***********************************/
void main()
{
??? unsigned char *AscC? = "A";
??? unsigned char *AscC1 = "*";
??? unsigned char *AscC2 = " ";
??? unsigned char *HzkC? = "王";
??? /*漢字占兩個字節,前景字符和背景字符都要有兩個英文字符或一個中文字符,否則字體將變形*/
??? unsigned char *HzkC1 = "**";
??? unsigned char *HzkC2 = "? ";
??? char *asc;
??? char *hzk;
??? char buffer1[16]; /*存儲英文字模信息*/
??? char buffer2[32]; /*存儲中文字模信息*/
??? /*輸出英文字符*/
??? getAscCode(AscC,buffer1);
??? asc = buffer1;
??? printAscChar(asc,AscC1,AscC2);
??? /*暫停一下*/
??? getch();
??? clrscr();
??? /*輸出中文字符*/
??? getHzKCode(HzkC,buffer2);
??? hzk = buffer2;
??? printHzKChar(hzk,HzkC1,HzkC2);
? ??getch(); /* 暫停一下 */
}
運行結果如下:
這是運行以后輸出的界面。我們可以看到輸出了 “ A ” 的 ASCII 為 65 ,偏移量為: 1041 ,與我們前面算出來的結果完全吻合。
按任意鍵,程序將輸出中文字符信息:
我們可以看到,輸出了 “ 王 ” 的區位碼為: 45,85 ,偏移量為: 135040 ,與我們前面算出來的結果一樣的。
到此,我們讀取字模的信息就已經完成了,知道了原理,你也可以用畫圖的方式來顯示漢字。
本文只是自己對于字模的一些膚淺的認識,有什么錯誤的地方,還望各位同仁給予批評指正。
總結
- 上一篇: Springboot接入华为云短信平台
- 下一篇: 本地图片转换成网络链接图片