计算机不一样文字的编码方式,Unicode和CodePage

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

ASCII编码方式

ASCII是基本的文字编码方式,它的方式是用一个字节Byte来表示一个符号,好比说0x30表明'0',而0x41表明'A',这个连接有详细的ASCII每一个数字对应的字符。
如下的C代码打印出你所须要的ASCII符号 'A':linux

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。所以上面的代码会打印出:windows

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。ide

  • 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 <stdio.h>
#include <Windows.h>

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的值,一次查询每个文字字符,咱们获得:ui

大:0x5927
家:0x5BB6
好:0x597D

咱们也能够在Visual Studio中打印出UTF-16编码的“你们好”(参考连接1, 参考连接2):编码

// Windows System
#include <stdio.h>
#include <windows.h>
#include <fcntl.h>
#include <io.h>

int main () 
{
  wchar_t SampleData[] = {0x5927, 0x5BB6, 0x597D, 0x0};
  _setmode(_fileno(stdout), _O_U16TEXT);
  wprintf(L"%s\n", SampleData);
  return 0;
}

// Linux System
#include <stdio.h>
#include <wchar.h>
#include <stdlib.h>
#include <locale.h>

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, 参考连接2code

相关文章
相关标签/搜索