字符编码

中文unicode编码表.doc   常用3753个汉字的UTF-8编码.txt

Windows 的字符转换函数: MultiByteToWideChar() / WideCharToMultiByte()
Linux 的字符转换库: GLIBC iconv函数组.

char str[ ]中存储的是“中文"的UTF-8编码

wchar_t wstr[ ]中存放的是“中文"的Unicode码值

linux命令iconv:
    iconv -f encoding -t encoding inputfile
比如将一个GBK 编码的文件转换成UTF-8编码: iconv -f GBK -t UTF-8 file1 -o file2
其中的参数的意义表示
-f From 某个编码
-t To 某个编码
-o 输出到文件
-l, --list 列举所有已知的字符集
--verbose 打印进度信息
-?, --help 给出该系统求助列表
--usage 给出简要的用法信息
-V, --version 打印程序版本号

locale 查看当前系统使用的字符编码

代码实现判断文件编码方式:
用记事本打开,使用另存为,下面就有选择编码格式。
Windows中默认的文件编码格式是GBK(gb2312), 而Linux一般都是UTF-8
不同的国家和地区制定了不同的标准,由此产生了 GB2312, BIG5, JIS 等各自的编码标准。这些使用 2 个字节来代表一个字符的各种汉字延伸编码方式,称为 ANSI 编码。在简体中文系统下,ANSI 编码代表 GB2312 编码,在日文操作系统下,ANSI 编码代表 JIS 编码。
文件的字符集在Windows下有两种,一种是ANSI,一种Unicode。
对于Unicode,Windows支持了它的三种编码方式,一种是小尾编码(Unicode),一种是大尾编码(BigEndianUnicode),一种是UTF-8编码。
我们可以从文件的头部来区分一个文件是属于哪种编码。
当头部开始的两个字节为 FF FE时,是Unicode的小尾编码;
当头部的两个字节为FE FF时,是Unicode的大尾编码;
当头部两个字节为EF BB时,是Unicode的UTF-8编码;
当它不为这些时,则是ANSI编码。

判断文件编码方式
linux下:
···.cpp
        windows下
判断.cpp

linux环境设置系统编码方式:
设置字符编码:
在Linux 中通过locale 来设置程序运行的不同语言环境,locale 由ANSI C 提供支持。locale 的命名规则为< 语言>_< 地区>.< 字符集编码> ,如zh_CN.UTF-8 ,zh 代表中文,CN 代表大陆地区,UTF-8 表示字符集。在locale 环境中,有一组变量,代表国际化环境中的不同设置:
1. LC_COLLATE
定义该环境的排序和比较规则  
2. LC_CTYPE
用于字符分类和字符串处理,控制所有字符的处理方式,包括字符编码,字符是单字节还是多字节,如何打印等。是最重要的一个环境变量。  
3. LC_MONETARY
货币格式  
4. LC_NUMERIC
非货币的数字显示格式  
5. LC_TIME
时间和日期格式  
6. LC_MESSAGES
提示信息的语言。另外还有一个LANGUAGE 参数,它与LC_MESSAGES 相似,但如果该参数一旦设置,则LC_MESSAGES 参数就会失效。LANGUAGE 参数可同时设置多种语言信息,如LANGUANE="zh_CN.GB18030:zh_CN.GB2312:zh_CN" 。
7. LANG
LC_* 的默认值,是最低级别的设置,如果LC_* 没有设置,则使用该值。类似于 LC_ALL 。  
8. LC_ALL
它是一个宏,如果该值设置了,则该值会覆盖所有LC_* 的设置值。注意,LANG 的值不受该宏影响。 

一个例子: 
设置前,使用默认locale

[root@db-wanggaofei ~]# export LC_ALL=zh_CN.GBK

[root@db-wanggaofei ~]# locale

"C" 是系统默认的locale ,"POSIX" 是"C" 的别名。所以当我们新安装完一个系统时,默认的locale 就是C 或POSIX 。 

修改系统文件方式:

修改 /etc/sysconfig/i18n 文件,如
LANG="en_US.UTF-8",xwindow会显示英文界面,
LANG="zh_CN.GB18030",xwindow会显示中文界面。 
还有一种方法 cp /etc/sysconfig/i18n $HOME/.i18n 
修改 $HOME/.i18n 文件,如
LANG="en_US.UTF-8",xwindow会显示英文界面,
LANG="zh_CN.GB18030",xwindow会显示中文界面。
这样就可以改变个人的界面语言,而不影响别的用户

修改后的/etc/sysconfig/i18n 文件为:
LANG="en_US.UTF-8"
SUPPORTED="zh_CN.GB18030:zh_CN:zh:en_US.UTF-8:en_US:en"
SYSFONT="latarcyrheb-sun16"
LC_ALL="en_US.UTF-8"
export LC_ALL

设置完毕后重启或者用rc.local使生效
或修改登录用户的.bash_profile文件加入
export LANG=zh_CN.GB18030
export LANGUAGE=zh_CN.GB18030:zh_CN.GB2312:zh_CN

或者:

默认为utf8,如出现乱码可设置为GBK

手动更改配置文件的命令:
shell> vi /etc/sysconfig/i18n

将LANG="zh_CN.UTF-8"修改为:
LANG="zh_CN.GBK"

保存并关闭,运行下面的命令使配置生效:
shell> source /etc/sysconfig/i18n 

将终端字符编码显示为简体中文:
shell> vi /etc/profile.d/lang.sh

添加下面这行:export LC_ALL=zh_CN.GBK
shell> source /etc/profile.d/lang.sh


汉字编码:
GB2312字集是简体字集,全称为GB2312(80)字集,共包括国标简体汉字6763个。 
BIG5字集是台湾繁体字集,共包括国标繁体汉字13053个。 
GBK字集是简繁字集,包括了GB字集、BIG5字集和一些符号,共包括21003个字符。 
GB18030是国家制定的一个强制性大字集标准,全称为GB18030-2000,它的推出使汉字集有了一个“大一统”的标准。

Unicode 的实现方式不同于编码方式。 一个字符的Unicode编码是确定的,但是在实际传输过程中,由于不同系统平台的设计不一定一致,以及出于节省空间的目的,对Unicode编码的实现方式有所不同。 
Unicode的实现方式称为Unicode转换格式(Unicode Translation Format,简称为 UTF)。 
* UTF-8: 8bit变长编码,对于大多数常用字符集(ASCII中0~127字符)它只使用单字节,而对其它常用字符(特别是朝鲜和汉语会意文字),它使用3字节。 
* UTF-16: 16bit编码,是变长码,大致相当于20位编码,值在0到0x10FFFF之间,基本上就是unicode编码的实现,与CPU字序有关。

iconv常用函数:
iconv使用例子以及库:linux系统默认包含在系统中
windows库安装程序: libiconv-1.9.2-1.rar  (exe)
vs2010可以运行例子: iconv.rar  各种转码

#include < iconv.h>
iconv函数族有三个函数,原型如下:
(1) iconv_t iconv_open(    const char *tocode,     const char *fromcode);
此函数说明将要进行哪两种编码的转换,tocode是目标编码,fromcode是原编码,该函数返回一个转换句柄,供以下两个函数使用。返回cd
iconv_open函数返回一个新申请的转换描述符。出错时,修改errno并返回(iconv_t)(-1)。
除了其它错误以外,出现以下错误:
EINVAL:从fromcode到tocode的编码转换不被支持。

(2) size_t iconv(    iconv_t cd,    char **inbuf,    size_t *inbytesleft,    char **outbuf,    size_t *outbytesleft    );
此函数从inbuf中读取字符,转换后输出到outbuf中,inbytesleft用以记录还未转换的字符数,outbytesleft用以记录输出缓冲的剩余空间。
以下四种情况不能完成转换:
输入含无效的多字节序列。 errno设置为 EILSEQ并返回(size_t)(-1)。 *inbuf指向无效序列的最左端。
输入的字节序列已经全部被转换过,也就是 *inbytesleft减少至0。此时, iconv返回本次调用中完成转换的数目(可逆的转换不计入)。
输入含不完整多字节序列。此时, errno设置为 EINVAL并返回(size_t)(-1)。 *inbuf指向不完整多字节序列的最左端。
输出缓存区没有足够空间。此时,它将 errno设置为 E2BIG并返回(size_t)(-1)。
 
(3) int iconv_close(iconv_t cd);
此函数用于关闭转换句柄,释放资源。
成功,iconvctl函数返回0。出错时,它修改errno并返回 (size_t)(-1)

(4) int iconvctl (iconv_t cd , int request, void * argument);
以下是request参数的允许值:
ICONV_TRIVIALP
argument类型应是int*,普通转换(conversion is trivial)为1,反之为0。

ICONV_GET_TRANSLITERATE
argument类型应是int*,字译功能已打开为1,反之为0。

ICONV_SET_TRANSLITERATE
argument类型应是const int*,指向一个int值。非0将打开字译功能,0则关闭。

ICONV_GET_DISCARD_ILSEQ
argument类型应是int*,“忽略无效序列并继续转换”功能已打开为1,反之为0。

ICONV_SET_DISCARD_ILSEQ
argument类型应是const int*,指向一个int值。非0将打开“忽略无效序列并继续转换”功能,0则关闭。

返回值:    成功,iconvctl函数返回0。出错时,它修改errno并返回(size_t)(-1)。
出现以下错误:EINVAL        request的值无效。
本函数仅出现GUN libiconv中,其它版本未实现。你可以通过(_LIBICONV_VERSION >= 0x0108)测试它是否存在。

例子:
void UTF8_2_GB2312(char*  in,  int  inLen,  char*  out,  int  outLen)
{  
  iconv_t  cd;
  char *pin = in;
  char *pout = out;
  int inLen_ = inlen + 1;
  int outLen_ = outlen;
   
  cd =  iconv_open(  "GB2312",  "UTF-8"  );
  if (cd  == (iconv_t)-1)
  printf("iconv_open false"); 

  iconv(  cd,  &pin,  (size_t*)&inLen_,  &pout,  (size_t*)&outLen_  );  
  iconv_close(cd);
}

windows宽字符转码:
      在标准c++中有string和wstring,前者为多字节ANSI字串,后者为宽字节wide字串(UTF-16)。在一些应用中常常需要在二者之间以及与UTF-8之间进行字串转换
      Windows 2000是使用Unicode从头进行开发的,如果调用任何一个Windows函数并给它传递一个ANSI字符串,那么系统首先要将字符串转换成Unicode,然后将Unicode字符串传递给操作系统。如果希望函数返回ANSI字符串,系统就会首先将Unicode字符串转换成ANSI字符串,然后返回给你( 多字节ANSI(windows下是gb2312)要先转换为宽字节给windows系统,然后再转换为UTF-8发送到网络上
      Windows CE 本身就是使用Unicode的一种操作系统,完全不支持ANSI Windows函数  
      Windows 98 只支持ANSI,只能为ANSI开发应用程序。  
      Microsoft公司将COM从16位Windows转换成Win32时,公司决定需要字符串的所有COM接口方法都只能接受Unicode字符串。
以utf-8转gb2312为例:
      虽然调用一下iconv就好了,但其实gb2312就是宽字符,utf-8是单字符,只是在iconv函数内部处理了,我们没看到而已
//#include "stdafx.h"
#include <stdio.h>
#include <windows.h>
#include <locale.h>
void WideToAnsi(WCHAR wszBuffer[],char pszBuffer[])
{
 do
 {
  int nLen = WideCharToMultiByte(CP_ACP, 0, wszBuffer, -1, NULL, 0, NULL, NULL);
  if(nLen == 0)
  {
            printf("errorCode = %d\n", GetLastError());
            break;
        }
  nLen = WideCharToMultiByte(CP_ACP, 0, wszBuffer, -1, pszBuffer, nLen, NULL, NULL);
  if(nLen == 0)
  {
            printf("errorCode = %d\n", GetLastError());
            break;
        }
 } while (false);
}
void AnsiToWide(char szBuffer[],WCHAR pwszBuffer[])
{
 do
 {
  int nLen = MultiByteToWideChar(CP_ACP, 0, szBuffer, -1, NULL, 0);
  if(nLen == 0)
  {
            printf("errorCode = %d\n", GetLastError());
            break;
        }
  nLen = MultiByteToWideChar(CP_ACP, 0, szBuffer, -1, pwszBuffer, nLen);
  if(nLen == 0)
  {
            printf("errorCode = %d\n", GetLastError());
            break;
        }
 } while (false);
}
void WideToUTF8(WCHAR pwszBuffer[],char pszBuffer[])
{
 do
 {
  int nLen = WideCharToMultiByte(CP_UTF8, 0, pwszBuffer, -1, NULL, 0, NULL, NULL);
  if(nLen == 0)
  {
            printf("errorCode = %d\n", GetLastError());
            break;
        }
  WideCharToMultiByte(CP_UTF8, 0, pwszBuffer, -1, pszBuffer, nLen, NULL, NULL);
  if(nLen == 0)
  {
            printf("errorCode = %d\n", GetLastError());
            break;
        }
 } while (false);
}
void UTF8ToWide(char pszBuffer[],WCHAR wszBuffer[])
{
 do
 {
  printf("utf8:%s\n",pszBuffer);
  int nLen = MultiByteToWideChar(CP_UTF8, 0, pszBuffer, -1, NULL, 0);
  if(nLen == 0)
  {
            printf("errorCode = %d\n", GetLastError());
            break;
        }
  nLen = MultiByteToWideChar(CP_UTF8, 0, pszBuffer, -1, wszBuffer, nLen);
  if(nLen == 0)
  {
            printf("errorCode = %d\n", GetLastError());
            break;
        }
 } while (false);
}
void AnsiToUTF8(char szBuffer[],WCHAR pwszBuffer[],char pszBuffer[])
{
 AnsiToWide(szBuffer,pwszBuffer);//多字节szBuffer转宽字节pwszBuffer
 WideToUTF8(pwszBuffer,pszBuffer);//宽字节pwszBuffer转多字节pszBuffer
}
void UTF8ToAnsi(char szBuffer[],WCHAR pwszBuffer[],char pszBuffer[])
{
 UTF8ToWide(szBuffer,pwszBuffer);
 WideToAnsi(pwszBuffer,pszBuffer);
}
int lcl_GetTextEncode(char* strTxtPath)
{
    int nType = -1;
    FILE *pFile = NULL;
    pFile = fopen(strTxtPath,"rt+");
 if (NULL == pFile)
 {
  printf("loser");
 }
 
    //这里要注意是用unsigned char,不然的话读取到的数据会因为溢出而无法正确判断
    unsigned char* chFileFlag = new unsigned char[3];
    fread(chFileFlag, 1, 3, pFile);
 
    if(chFileFlag[0] == 0xEF && chFileFlag[1] == 0xBB && chFileFlag[2] == 0xBF)
        nType = 1;//UTF-8
    else if (chFileFlag[0] == 0xFF && chFileFlag[1] == 0xFE)
        nType = 2;//Unicode
    else if (chFileFlag[0] == 0xFE && chFileFlag[1] == 0xFF)
        nType = 3;//Unicode big endian text
    else
        nType = 4;//ASCII
    fclose(pFile);
    delete chFileFlag;
    return nType;
}
int main()
{
 setlocale(LC_ALL, "chs"); // 这里设置locale是为了wprintf能正确的输出宽字符类型的中文
 int ret = 100;
 ret = lcl_GetTextEncode("wolegequ.TXT");//获取文件的编码方式
 char szFrom[32] = "殷翔";
 WCHAR *pwszTemp = new WCHAR[32];//typedef unsigned short WCHAR; 短整型两个字节
 char *pszTo = new char[32];
 AnsiToUTF8(szFrom,pwszTemp,pszTo);
 UTF8ToAnsi(pszTo,pwszTemp,szFrom);
 wprintf(L"pwszTemp = %s\n", pwszTemp);
 delete []pwszTemp;
 delete []pszTo;
 return 0;
}

asc码:0~255     0~32 控制码   33~127字符    128~255 横线、竖线、交叉等形状
中文 两个大于127的字符连在一起,就表示一个汉字,前面的一个字节(称之为高字节)从0xA1(161)用到0xF7(247),后面一个字节(低字节)从0xA1(161)到0xFE(254),这样我们就可以组合出大约7000多个简体汉字了。

编码的故事
很久很久以前,有一群人,他们决定用8个可以开合的晶体管来组合成不同的状态,以表示世界上的万物。他们看到8个开关状态是好的,于是他们把这称为"字节"。
再后来,他们又做了一些可以处理这些字节的机器,机器开动了,可以用字节来组合出很多状态,状态开始变来变去。他们看到这样是好的,于是它们就这机器称为"计算机"。
ASCII码
八位的字节一共可以组合出256(2的8次方)种不同的状态。
其中的编号从0开始的32种状态分别规定了特殊的用途, 这些0x20以下的字节状态称为"控制码"。(0~32)
空格、标点符号、数字、大小写字母分别用连续的字节状态表示,一直编到了第127号
画表格时需要用下到的横线、竖线、交叉等形状,一直把序号编到了最后一个状态255。从128到255这一页的字符集被称"扩展字符集"。
中文编码
中国人民,不客气地把那些127号之后的奇异符号们直接取消掉, 规定:一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,前面的一个字节(他称之为高字节)从0xA1(161)用到0xF7(247),后面一个字节(低字节)从0xA1(161)到0xFE(254),这样我们就可以组合出大约7000多个简体汉字了。
在这些编码里,我们还把数学符号、罗马希腊的字母、日文的假名们都编进去了,连在 ASCII 里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的"全角"字符,而原来在127号以下的那些就叫"半角"字符了。 这种汉字方案叫做 " GB2312 "。
unicode
正在这时, ISO(国际标谁化组织)解决了这个问题: 废了所有的地区性编码方案,重新搞一个包括了地球上所有文化、所有字母和符号的编码!他们打算叫它"Universal Multiple-Octet Coded Character Set",简称 UCS, 俗称 "UNICODE"。
UNICODE 开始制订时,计算机的存储器容量极大地发展了,空间再也不成为问题了。于是 ISO 就直接规定必须用两个字节,也就是16位来统一表示所有的字符,对于ascii里的那些"半角"字符,UNICODE 包持其原编码不变,只是将其长度由原来的8位扩展为16位,而其他文化和语言的字符则全部重新统一编码。由于"半角"英文符号只需要用到低8位,所以其高8位永远是0,因此这种大气的方案在保存英文文本时会多浪费一倍的空间。
从 UNICODE 开始,无论是半角的英文字母,还是全角的汉字,它们都是统一的"一个字符"!同时,也都是统一的"两个字节"。
GBK 与UNICODE 在汉字的内码编排上完全是不一样的,没有一种简单的算术方法可以把文本内容从UNICODE编码和另一种编码进行转换,这种转换必须通过查表来进行。(所以要转换的话只能一个字符一个字符地去查)
网络传输
UNICODE 来到时,一起到来的还有计算机网络的兴起,UNICODE 如何在网络上传输也是一个必须考虑的问题,于是面向传输的UTF(UCS Transfer Format)标准出现了,顾名思义,UTF8就是每次8个位传输数据,而UTF16就是每次16个位。
在网络里传递信息时有一个很重要的问题,就是对于 数据高低位 的解读方式。 一些计算机是采用低位先发送的方法,例如我们PC机采用的 INTEL 架构,而另一些是采用高位先发送的方式。网络中交换数据,在文本流的开始向对方发送一个 标志符 --如果文本是高位先发送,那就发送" FEFF ",反之,则发送" FFFE "。
UNICODE到UTF8的转换规则:
    Unicode                       UTF-8 
  0000 - 007F                   0xxxxxxx                         单字节模板 
  0080 - 07FF                  110xxxxx 10xxxxxx            双字节模板 
  0800 - FFFF                  1110xxxx 10xxxxxx 10xxxxxx   三字节模板 
例如:“汉”      Unicode编码是6C49 ,  6C49在0800-FFFF之间   用三字节模板
   6C49写成二进制是:0110 1100 0100 1001, 将这个比特流按三字节模板的分段方法分为0110 110001 001001,依次代替模板中的x,得到:1110-0110 10-110001 10-001001,即E6 B1 89,这就是其UTF8的编码。

小趣味:在记事本中输入“联通”两个字保存再打开会变成乱码。
当你新建一个文本文件时,记事本的编码默认是ANSI, 如果你在ANSI的编码输入汉字,那么他实际就是GB系列的编码方式,在这种编码下,"联通"的内码是: "联通"的内码是:
c1 1100 0001
aa 1010 1010
cd 1100 1101
a8 1010 1000
第一二个字节、第三四个字节的起始部分的都是"110"和"10",正好与UTF8规则里的两字节模板是一致的,于是再次打开记事本时,记事本就误认为这是一个UTF8编码的文件。
让我们把第一个字节的110和第二个字节的10去掉,我们就得到了"00001 101010",再把各位对齐,补上前导的0,就得到了"0000 0000 0110 1010",不好意思,这是UNICODE的006A,也就是小写的字母"j",而之后的两字节用UTF8解码之后是0368,这个字符什么也不是。
如果你在"联通"之后多输入几个字,其他的字的编码不见得又恰好是110和10开始的字节,这样再次打开时,记事本就不会坚持这是一个utf8编码的文件,而会用ANSI的方式解读之,这时乱码就不出现了。

乱码的话先转换成unicode方式(纠正成通用编码方式),再用word打开(字体识别功能)


























  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值