计算机国际通用编码,计算机不同文字的编码方式,Unicode和CodePage

在计算机中文字的编码和存储

ASCII编码方式

ASCII是基本的文字编码方式,它的方式是用一个字节Byte来表示一个符号,比如说0x30代表'0',而0x41代表'A',这个链接有详细的ASCII每个数字对应的字符。

以下的C代码打印出你所需要的ASCII符号 'A':

printf("ASCII: %c\r\n", 0x41);

// 或者

printf("ASCII: %c\r\n", 'A');

Code Page

但大家可能会有疑问,一个字节一共有256个值,明明可以使用256个字符,为什么ASCII只有128个?如果是printf("Code Page: %c\r\n", 0xA0);会打印出什么结果?

答案是另外128个字符预留给了不同的Code Page(码页)。为了打印出各个国家的符号,我们定义了多个码页,一般来说每个码页第0-127 是一样的,即拉丁数字和26个英文字母等常用符号,而第128-255是根据每个码页而不同的。比如说Visual Studio的系统预设(default)的码页是Code Page 437。因此上面的代码会打印出:

printf("Code Page: %c\r\n", 0xA0);

//结果

Code Page: á

我们可以切换不同的码页,比如说Code Page 864 有阿拉伯文字所需的符号。Code Page 932 有日文符号。

// Code Page 932 Japanese

SetConsoleOutputCP(932); // Set to Code Page 932

printf("Code Page: %c\r\n", 0xC0);

// 结果

Code Page: タ

这个链接 是目前windows 支持的码页ID(SetConsoleOutputCP支持的参数)。

Unicode

接下来大家可能会想到这个问题,我们的中文一共可不止128个字符,经常使用的中文字符大约有5000个,是无法使用码表来表示的。

因此我们用Unicode来包含更多的字符,我们只需要使用Unicode就可以表示不同国家的文字,无需在不同文字系统之间系统切换。Unicode的想法非常直接,就是用多个字节Byte来表示文字符号。

在Unicode中,常用的编码方式用两种,一个是UTF-8,一个是UTF-16。

UTF-8

UTF-8是最为常用的编码方式,这种方式最大的优点是它和ASCII码表是通用的。这种编码方式中,我们用1-4的字节来表示一个文字符号。网上有不少查询UTF-8/16的工具,链接。如果我们想知道“大家好”的UTF-8的值,一次查询每一个文字字符,我们得到:

大:0xE5 0xA4 0xA7

家:0xE5 0xAE 0xB6

好:0xE5 0xA5 0xBD

我们可以在Windows系统中用gcc编译下面的UTF-8测试文件并运行

#include

#include

int main( int argc, char **argv )

{

char PrintData[] = { 0xE5, 0xA4, 0xA7, 0xE5, 0xAE, 0xB6, 0xE5, 0xA5, 0xBD, 0x00}; //0x00 is for NUL termination

SetConsoleOutputCP(65001);

printf( "%s\n", PrintData);

return 0;

}

// 输出

大家好

UTF-16

这种编码方式中,我们固定用两个字节来表示一个文字符号。这种编码的优势在于因为长度是固定的,所以利于存储。我们可以方便地建立一个uint16_t的数组来存储UTF-16。如果我们想知道“大家好”的UTF-16的值,一次查询每一个文字字符,我们得到:

大:0x5927

家:0x5BB6

好:0x597D

我们也可以在Visual Studio中打印出UTF-16编码的“大家好”(参考链接1, 参考链接2):

// Windows System

#include

#include

#include

#include

int main ()

{

wchar_t SampleData[] = {0x5927, 0x5BB6, 0x597D, 0x0};

_setmode(_fileno(stdout), _O_U16TEXT);

wprintf(L"%s\n", SampleData);

return 0;

}

// Linux System

#include

#include

#include

#include

int main() {

setlocale(LC_ALL,"");

wchar_t SampleData[] = {0x5927, 0x5BB6, 0x597D, 0x0};

wprintf(L"%ls\n", SampleData);

}

因此我们需要在UTF-8和UTF-16之间进行转换。系统会接受外部传入的UTF-8编码的字符串(比如网络通信中,HTTP协议一般用UTF-8),然后再将其转换成UTF-16编码的字符串,找到对应的字形(Glyph),然后将字形(Glyph)发到显示器上显示。下面的C程序实现UTF-8和UTF-16的相互转换。

int8_t Utf8To16Converter(const uint8_t * Utf8Val, uint16_t * Utf16Result, uint16_t NumOfUtf8Byte)

{

int8_t Status = 0; // -1 for process fail

uint16_t Utf16Val = 0;

uint8_t TrailingNumOfBytes = 0; // The number of bytes of UTF8 is 1 - 4

uint16_t i = 0;

const uint8_t * InputTraveler;

InputTraveler = Utf8Val;

while (i < NumOfUtf8Byte)

{

if (*InputTraveler <= 0x7F)

{

Utf16Val = *InputTraveler;

TrailingNumOfBytes = 0;

}

else if (*InputTraveler <= 0xBF)

{

Status = -1;

}

else if (*InputTraveler <= 0xDF)

{

Utf16Val = (*InputTraveler) & 0x1F;

TrailingNumOfBytes = 1;

}

else if (*InputTraveler <= 0xEF)

{

Utf16Val = (*InputTraveler) & 0x0F;

TrailingNumOfBytes = 2;

}

else if (*InputTraveler <= 0xF7)

{

Utf16Val = (*InputTraveler) & 0x07;

TrailingNumOfBytes = 3;

}

else

{

Status = -1;

}

if (-1 == Status)

{

break;

}

else

{

uint8_t j;

for (j = 0; j < TrailingNumOfBytes; j++)

{

InputTraveler ++;

if (*InputTraveler == 0x0)

{

Status = -1;

break;

}

else

{

Utf16Val <<= 6;

Utf16Val += (*InputTraveler) & 0x3F;

}

i ++;

}

if (-1 == Status)

{

break;

}

else

{

*Utf16Result = Utf16Val;

Utf16Result ++;

InputTraveler ++;

i ++;

Utf16Val = 0;

TrailingNumOfBytes = 0;

}

}

}

return Status;

}

int8_t Utf16ToUtf8Converter(const uint16_t * Utf16Val, uint8_t * Utf8Result, uint16_t NumOfUtf16Vals)

{

int Status = 0;

uint8_t Utf8Val;

uint16_t i = 0;

for (i = 0; i < NumOfUtf16Vals; i++)

{

uint16_t Utf16TempVal = Utf16Val[i];

if (Utf16TempVal <= 0x7F)

{

*(Utf8Result) = (uint8_t)Utf16TempVal;

Utf8Result ++;

}

else if (Utf16TempVal <= 0x7FF)

{

*(Utf8Result) = 0xC0 | (Utf16TempVal >> 6); /* 110xxxxx */

Utf8Result ++;

*(Utf8Result) = 0x80 | (Utf16TempVal & 0x3F); /* 10xxxxxx */

Utf8Result ++;

}

else if (Utf16TempVal <= 0xFFFF)

{

*(Utf8Result) = 0xE0 | (Utf16TempVal>> 12); /* 1110xxxx */

Utf8Result ++;

*(Utf8Result) = 0x80 | ((Utf16TempVal >> 6) & 0x3F); /* 10xxxxxx */

Utf8Result ++;

*(Utf8Result) = 0x80 | (Utf16TempVal & 0x3F); /* 10xxxxxx */

Utf8Result ++;

}

else if (Utf16TempVal <= 0x10FFFF) {

*(Utf8Result) = 0xF0 | (Utf16TempVal >> 18); /* 11110xxx */

Utf8Result ++;

*(Utf8Result) = 0x80 | ((Utf16TempVal >> 12) & 0x3F); /* 10xxxxxx */

Utf8Result ++;

*(Utf8Result) = 0x80 | ((Utf16TempVal >> 6) & 0x3F); /* 10xxxxxx */

Utf8Result ++;

*(Utf8Result) = 0x80 | (Utf16TempVal & 0x3F); /* 10xxxxxx */

Utf8Result ++;

}

else

{

Status = -1;

}

if (-1 == Status)

{

break;

}

}

return Status;

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值