計算機不一樣文字的編碼方式,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

相關文章
相關標籤/搜索