亂碼演算法大全 (轉)

worldblog發表於2007-12-23
亂碼演算法大全 (轉)[@more@]

亂碼演算法大全

  相信上過網的朋友們都遇見過“亂碼”,也就是在瀏覽網頁或看E時出現的不能辨認的字元。以前也有許多的文章介紹過“亂碼”,不過他們的文章只是講怎樣辨別和怎樣用工具解碼,並沒有詳細介紹各種編碼的演算法的實現,本文將對網際網路上最常用的幾種編碼的編碼和解碼演算法作以詳細的闡述。希望對想了解“亂碼”演算法或想在自己中實現這些功能朋友們有一些參考價值。本文的源程式用C語言寫成,形式為,可直接使用。

一. 常用編碼
1. Uuencode
  Uuencode 是將二進位制以文字檔案方式進行編碼表示、以利於基於文字傳輸環境中進行二進位制檔案的傳輸/的編碼方法之一, 在/二進位制新聞組中使用頻率比較高,經常用於 Attach 二進位制檔案。
  這種編碼的特徵是:每一行開頭用“M”標誌。下面是我做的一個測試用的檔案mogao.txt,編碼為Uuencode:
begin 644 mogao.txt
M"0D)("
MU]6^HZAT96QN970Z+RR,#(N,3$R+C(P+C$S,CHR,Z.ILG4L:&0H)("`@
M("='`Z+R]M;V=A;RYB96YT:75N+FYE=`T">`@Q*JXWCMO/ZYI-?WRM*CNFAT='`Z+R]M;V=A;RYB96YT:75N+FYE=`T*
M"0D)16UA:6QT;SIM;V=A;T`S-S$N;F5T#0H)("
M*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ("`@("`@("`@("`@("`@
M#0H)("
MN'TS(J#0H)("
,*BHJ*BHJ*BHJ*BHJ
`
end

  你可以把它單獨存成一個檔案:mogao.uue,然後用開啟,解壓即得mogao.txt。
  Uuencode的演算法很簡單,編碼時它將3個字元順序放入一個 24 位的緩衝區,缺字元的地方補零,然後將緩衝區截斷成為 4 個部分,高位在先,每個部分 6 位,用下面的64個字元重新表示:
"`!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_"
在檔案的開頭有“begin xxx 被編碼的檔名”,在檔案的結尾有“end”,用來標誌Uue檔案的開始和結束。編碼時,每次讀取原始檔的45個字元,不足45個的用“NULL”補足為3的整數倍(如:23補為24),然後輸入目標檔案一個ASCII為:“32+實際讀取的字元數”的字元作為每一行的開始。讀取的字元編碼後輸入目標檔案,再輸入一個“換行符”。如果原始檔被編碼完了,那麼輸入“`(ASCII為96)”和一個“換行符”表示編碼結束。
  解碼時它將4個字元分別轉換為4個6位字元後,擷取有用的後六位放入一個 24 位的緩衝區,即得3個二進位制程式碼。
  下面我給出Uuencode編碼和解碼的C語言描述:
/*Uuencode編碼*/
void Uue(unsigned char chasc[3],unsigned char chuue[4])
/* 
chasc:未編碼的二進位制程式碼
chuue:編碼過的Uue程式碼
*/
{int i,k=2;
 unsigned char t=NULL;
 for(i=0;i<3;i++)
 {*(chuue+i)=*(chasc+i)>>k;
  *(chuue+i)|=t;
  if(*(chuue+i)==NULL) *(chuue+i)+=96;
  else *(chuue+i)+=32;
  t=*(chasc+i)<  t>>=2;
  k+=2;
 }
 *(chuue+3)=*(chasc+2)&63;
 if(*(chuue+3)==NULL) *(chuue+3)+=96;
 else *(chuue+3)+=32;
}

/*Uuencode解碼*/
void unUue(unsigned char chuue[4],unsigned char chasc[3])
/* 
chuue:未解碼的Uue程式碼
chasc:解碼過的二進位制程式碼
*/
{int i,k=2;
 unsigned char t=NULL;
 if(*chuue==96) *chuue=NULL;
 else *chuue-=32;
 for(i=0;i<3;i++)
 {*(chasc+i)=*(chuue+i)<  k+=2;
  if(*(chuue+i+1)==96) *(chuue+i+1)=NULL;
  else *(chuue+i+1)-=32;
  t=*(chuue+i+1)>>8-k;
  *(chasc+i)|=t;
 }
}

2. Xxencode
  提到Uuencode不可能不提Xxencode, Xxencode的編碼演算法和 Uuencode基本相同,但是使用的是不同的字符集。XxEncode編碼使用的字元是:
“+-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz”與 Uuencode 相比,它的特殊字元更少。很多支援 Uuencode 編解碼的工具都同時支援 Xxencode。
  這種編碼的特徵是:每一行開頭用“h”標誌。下面是Xxencode的一個例子:
begin 644 mogao.txt
h0EY760+U684qkh90uwjXhuWowwWfcPQB0UbLxxLTCapjNq3jcumkpxH4iwOu
hpxKycuVoNKliNLEu9mwmA16iAH2m9X6k9X2nAXcmAuCdgwbIgO4X1Ec760+U
h60+Ul8esrwXhjDutTrmh8XiaVoR5+u9mxhPqRVPmtWNKtoOLJi9atZR+o8
h0EY7FKpVOKloPndhPqRVPo+nBn2iPaJo1Ec760+U8Wce8Wce8Wce8Wce8Wce
h8Wce8Wce8Wce8Wce8Wce8Wce8Wce8Wce8Wce8Wce60+U60+U60+U60+U60+U
h1Ec760+U8W0nzQ59jATGtAemkvGqj98vhDXLruCggzr-mxTXj8D8ggCohfmm
hiw5onw6e1Ec760+U8Wce8Wce8Wce8Wce8Wce8Wce8Wce8Wce8Wce8Wce8Wce
A8Wce8Wce8Wce8Wce
+
end

  你可以把它單獨存成一個檔案:mogao.xxe,然後用Winzip開啟,解壓即得mogao.txt。
  Xxencode的編碼演算法和Uuencode基本相同,實現起來則更為簡單,在此就不詳述了。
  下面給出Xxencode編碼和解碼的C語言描述:
/*Xxencode編碼*/
 void Xxe(unsigned char chasc[3],unsigned char chxxe[4])
/*
chasc:未編碼的二進位制程式碼
chxxe:編碼過的Xxe程式碼
*/
{int i;
 static char set[]=
  "+-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
 chxxe[0]=chasc[0]>>2;
 chxxe[1]=(chasc[0]<<4)&48|(chasc[1]>>4)&15;
 chxxe[2]=(chasc[1]<<2)&60|(chasc[2]>>6)&3;
 chxxe[3]=chasc[2]&63;
 for(i=0;i<4;i++) chxxe[i]=set[chxxe[i]]; /*查表*/
}
/*需注意的是,Xxencode檔案正文部分中每一行的第一個字母是:從原始檔中實際  讀取的字元數的ASCII值取後六位後用set[]查表得到的。*/
/*Xxencode解碼*/
unsigned char set(unsigned char ch) /*查表函式*/
{if(ch==43) ch=0;
 else if(ch==45) ch=1;
 else if(ch>=48&&ch<=57) ch-=46;
 else if(ch>=65&&ch<=90) ch-=53;
 else if(ch>=97&&ch<=122) ch-=59;
 return ch;
}

void unXxe(unsigned char chxxe[4],unsigned char chasc[3])
/*
chxxe:未解碼的Xxe程式碼
chasc:解碼過的二進位制程式碼
*/
{int k=2 ,i;
 unsigned char t;
 t=NULL;
 *chxxe=set(*chxxe);
 for(i=0;i<3;i++)
 {*(chxxe+i+1)=set(*(chxxe+i+1));
  (chhex+i)=*(chxxe+i)<  k+=2;
  t=*(chxxe+i+1)>>8-k;
  *(chhex+i)|=t;
 }
}

3. Base64
  Base64和下面將要介紹的Quoted-Printable都屬於MIME(多部分( multi-part)、多電子郵件和 WWW 超文字的一種編碼標準,用於傳送諸如圖形、和傳真等非文字資料)。MIME定義在1341中。
  Base64是現今在網際網路上應用最多的一種編碼,幾乎所有的電子郵件頭把它作為預設的二進位制編碼,它已經成了現今電子郵件編碼的代名詞。
  下面是Base64的一個例子,從例子中,您也可以看到Base64與電子郵件的的緊密聯絡:
Content-Type: text/plain;charset="cn-gb"
Content-Traner-Encoding: BASE64

CQkJICAgIKG2wtLC68vjt6i088irobcNCgnX99XfOm1vZ2Fvo6yw19TGu8a619W+o6h0ZWxuZXQ6
Ly8yMDIuMTEyLjIwLjEzMjoyM6Ops8nUsaGjDQoJICAgICAgxKq438jtvP65pNf3ytKjumh0dHA6
Ly9tb2dhby5iZW50aXVuLm5ldA0KCQkJRW1haWx0bzptb2dhb0AzNzEubmV0DQoJICAgKioqKioq
KioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqICAgICAgICAgICAgICAgDQoJ
ICAgKiCz/cHLvMfS5Mqyw7S2vLK7tPjX36Oss/3By9fjvKPKssO0tryyu8H0z8IqDQoJICAgKioq
KioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioq

  你可以把它單獨存成一個檔案,可以取名為:mogao.eml,雙擊可以用開啟(前兩行為郵件的原始資訊,從第四行開始為編碼內容)。
  Base64的演算法同Uuencode的演算法很接近,也很簡單:它將字元流順序放入一個 24 位的緩衝區,缺字元的地方補零。然後將緩衝區截斷成為 4 個部分,高位在先,每個部分 6 位,用下面的64個字元重新表示:“ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/”。如果輸入只有一個或兩個位元組,那麼輸出將用等號“=”補足。這可以隔斷附加的資訊造成編碼的混亂。它每行一般為76個字元。
  下面我給出Base64的編碼和解碼的C語言描述:
/*Base64編碼*/
void Base64(unsigned char chasc[3],unsigned char chuue[4])
/* 
  chasc:未編碼的二進位制程式碼
  chuue:編碼過的Base64程式碼
*/
{
 int i,k=2;
 unsinged char t=NULL;
 for(i=0;i<3;i++)
 {
  *(chuue+i)=*(chasc+i)>>k;
  *(chuue+i)|=t;
  t=*(chasc+i)<  t>>=2;
  k+=2;
 }
 *(chuue+3)=*(chasc+2)&63;

 for(i=0;i<4;i++)
  if((*(chuue+i)>=0)&&(*(chuue+i)<=25)) *(chuue+i)+=65;
  else if((*(chuue+i)>=26)&&(*(chuue+i)<=51)) *(chuue+i)+=71;
  else if((*(chuue+i)>=52)&&(*(chuue+i)<=61)) *(chuue+i)-=4;
  else if(*(chuue+i)==62) *(chuue+i)=43;
  else if(*(chuue+i)==63) *(chuue+i)=47;

}
/*Base64解碼*/
void unBase64(unsigned char chuue[4],unsigned char chasc[3])
/* 
chuue:未解碼的Base64程式碼
chasc:解碼過的二進位制程式碼
*/
{int i,k=2;
 unsigned char t=NULL;
 
 for(i=0;i<4;i++)
  if((*(chuue+i)>=65)&&(*(chuue+i)<=90)) *(chuue+i)-=65;
  else if((*(chuue+i)>=97)&&(*(chuue+i)<=122)) *(chuue+i)-=71;
  else if((*(chuue+i)>=48)&&(*(chuue+i)<=57)) *(chuue+i)+=4;
  else if(*(chuue+i)==43) *(chuue+i)=62;
  else if(*(chuue+i)==47) *(chuue+i)=63;
  else if(*(chuue+i)==61) *(chuue+i)=0;

 for(i=0;i<3;i++)
 {*(chhex+i)=*(chuue+i)<  k+=2;
  t=*(chuue+i+1)>>8-k;
  *(chhex+i)|=t;
 }
}

4. Quoted-Printable
  Quoted-Printable簡稱QP, 一般用在系統中。它通常用於少量文字方式的8位字元的編碼,例如Foxmail就用它做對主題和信體的編碼。這種編碼的應該是很好辨認的:它有大量的“=”。下面是它的一個例子:

Mime-Version: 1.0
Content-Transfer-Encoding: quoted-printable

  =A1=B6=C2=D2=C2=EB=CB=E3=B7=A8=B4=F3=C8=AB=A1=B7
 =D7=F7=D5=DF:mogao=A3=AC=B0=D7=D4=C6=BB=C6=BA=D7=D5=BE=A3=A8://202.112.20.132:23=A3=A9=B3=C9=D4=B1=A1=A3
  =C4=AA=B8=DF=C8=ED=BC=FE=B9=A4=D7=F7=CA=D2=A3=BA
 Emailto:mogao@371.net
  ********************************************* 
  * =B3=FD=C1=CB=BC=C7=D2=E4=CA=B2=C3=B4=B6=BC=B2=BB=B4=F8=D7=DF=A3=AC=B3=FD=C1=CB=D7=E3=BC=A3=CA=B2=C3=B4=B6=BC=B2=BB=C1=F4=CF=C2*
  *********************************************

  你可以把它單獨存成一個檔案,取名為:mogao.eml,雙擊可以用OutLook開啟(前兩行為郵件的原始資訊,從第四行開始為編碼內容)。
  QP的演算法可以說是最簡單的也可以說是編碼最低的(它的編位元速率是1:3),它是專門為了處理8位字元制定的。它的演算法是:讀一個字元,如果ASCII碼大於127,即字元的第8位是1的話,進行編碼,否則忽略(有時也對7位字元編碼)。編碼很簡單,看下面的C語言描述即可:
/*QP編碼*/
void qp(unsigned char sour,unsigned char first,unsigned char second)
/* 
  sour:要編碼的字元
  first:編碼後的第一個字元
  second:編碼後的第二個字元
  first和second為返回值
*/
{
 if(sour>127) 
 {first=sour>>4;
  second=sour&15;
  if(first>9) first+=55;
  else first+=48;
  if(second>9) second+=55;
  else second+=48;
  printf("%c%c%c",'=',first,second);
 }
}

/*QP解碼*/
void uqp(unsigned char sour,unsigned char first,unsigned char second)
/*
  sour:解碼後的字元
  first:QP碼的第一個字元
  second:QP碼的第二個字元
  sour為返回值
*/
{
 if(first>=65) first-=55;
 else first-=48;
 if(second>=65) second-=55;
 else second-=48;
 sour=NULL;
 sour=first<<4;
 sour|=second;
}

  現在大家知道為什麼QP的編位元速率那麼低了吧!關於QP的詳細說明和準確定義可以參閱RFC2045。

二.漢字編碼
1. GB碼和Big5碼
  GB碼是中國大陸、新加坡等國家和地區使用的一種漢字編碼方法。Big5碼是台灣省用的一種漢字編碼方法。它們的編碼方法是完全不同的兩種方法,它們之間的轉換隻能透過“查表法”來進行。所以說轉換的方法很簡單,困難的是“表”的生成。很多文章對此都做過介紹,我在此就不詳述了。在我的主頁上有我寫的“漢字轉碼通V1.0”的源程式,其中有這兩個“表”,可以直接使用。

2. HZ碼
  HZ碼是為了使只能傳送7bit資訊的郵件或閘道器能傳送8bit資訊而定義的編碼,也是中文常用編碼的一種。它和上面介紹的Quoted-Printable碼都只能對文字進行編碼,即編碼時忽略控制字元。
  這種編碼的也是很好辨認的:有許多“~{”和“~}”,而且總是成對出現。下面是HZ碼的一個例子:
  ~{!6BRBkKc7(4sH+!7~}
 ~{WwU_~}:mogao~{#,0WTF;F:WU>#(~}telnet://202.112.20.132:23~{#)3IT1!#~}
  ~{D*8_Hm Emailto:mogao@371.net
  ********************************************* 
  * ~{3}AK  *********************************************

  您可以開啟“南極星”看這段文字。
  它的演算法更簡單:讀一個字元,如果是8位字元,就把它的最高位清零。把連續的8位字元清零後的輸出用“~{”和“~}”括起來。解碼時:把是用“~{”和“~}”括起來的部分每個字元的第8位置“1”即可。

  上面介紹的三種編碼之間的轉換是經常遇見的,我寫的“漢字轉碼通V1.0”可以方便的在這三種之間轉換,我把它的源程式公開,方便廣大網友的學習。

三.其他常用編碼
1.  Unicode
  Unicode應用中最典型的例子是:IE4以上版本對HTML的編碼。它可以說是未來下唯一的字符集。但它還很不完善,而且Win95和對它的支援還很有限,甚至它還沒有一套完整的標準。不過,最新推出的2000和馬上就要推出的將全面支援Unicode。Unicode取代其他編碼將會是必然的趨勢。不過,在近一兩年Unicode並不會占主導地位,就是在占主導地位後,因為的差異,其他編碼也不會立即消亡。它的中文資料可以在Office2000和Windows2000所帶的文件中找到,它的官方網站是:。

2. Binhex
  BinHex 編碼是 Macintosh (也就是俗稱的“蘋果”)上用可列印字元表示/傳輸二進位制檔案的一種編碼方法。它的主要用途是在電子郵件程式中Attach二進位制檔案。大部分的電子郵件程式不支援這種格式(Eudora支援),但用WINZIP可以進行解碼。它的資料請查閱Macintosh計算機帶的相關文件。


三.總結
  由於篇幅所限,除了本文介紹的這幾種常用編碼外,還是有很多種編碼的。如各種演算法產生的“亂碼”(我將在另外一篇文章中詳細介紹)。本文中提到的所有文件和源程式在我的主頁中均可,我的主頁地址是:。如果您對本文有什麼意見請來信商榷,我的地址是:mogao@371.net。

注:我在本文中使用的例子“mogao.txt”的內容是:
  《亂碼演算法大全》
 作者:mogao,白雲黃鶴站(telnet://202.112.20.132:23)成員。
  莫高軟體工作室:
 Emailto:mogao@371.net
  ********************************************* 
  * 除了記憶什麼都不帶走,除了足跡什麼都不留下*
  *********************************************

 


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-995484/,如需轉載,請註明出處,否則將追究法律責任。

相關文章