亚洲视频二区_亚洲欧洲日本天天堂在线观看_日韩一区二区在线观看_中文字幕不卡一区

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.430618.com 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

最近使用到了wchar_t類型,所以準(zhǔn)備詳細(xì)探究下,沒想到水還挺深,網(wǎng)上的資料大多都是復(fù)制粘貼,只有個(gè)結(jié)論,也沒個(gè)驗(yàn)證過程。本文記錄探究的過程及結(jié)論,如有不對(duì)請(qǐng)指正。

Unicode、UCS

UCS(Universal Character Set)本質(zhì)上就是一個(gè)字符集。

Unicode的開發(fā)結(jié)合了國(guó)際標(biāo)準(zhǔn)化組織所制定的 ISO/IEC 10646,即通用字符集(

Universal Character Set, UCS)。Unicode 與 ISO/IEC 10646 在編碼的運(yùn)作原理相同,但 The Unicode Standard 包含了更詳盡的實(shí)現(xiàn)信息、涵蓋了更細(xì)節(jié)的主題,諸如比特編碼(bitwise encoding)、校對(duì)以及呈現(xiàn)等。摘自(Unicode)

所以也可以簡(jiǎn)單的理解為,Unicode和UCS等價(jià),都是字符集。

UCS編碼的長(zhǎng)度是31位,可用4個(gè)字節(jié)表示,可以表示2的31次方個(gè)字符。如果兩個(gè)字符的高位相同,只有低16位不同,則它們屬于同一平面,所以一個(gè)平面由2的16次方個(gè)字符組成。目前大部分字符都位于第一個(gè)平面稱為BMP。BMP的編碼通常以U+xxxx這種形式表示,其中x是16進(jìn)制數(shù)。

比如中文“你”對(duì)應(yīng)的UCS編碼為U+4f60,“好”對(duì)應(yīng)的UCS編碼為U+597d。更多中文編碼可以在Unicode編碼表中查詢。

有了UCS編碼,任何一個(gè)字符在計(jì)算機(jī)中都最多可以用四個(gè)字節(jié)來表示,稱為碼點(diǎn)。

UTF8

現(xiàn)在有了UCS字符集,那么一個(gè)字符在計(jì)算機(jī)中真的要按四個(gè)字節(jié)(UTF-32)來存儲(chǔ)嗎?

答案是否定的,一方面每個(gè)字符都按四字節(jié)來存儲(chǔ)非常浪費(fèi)空間,因?yàn)榇蟛糠肿址荚贐MP,只有后16位有效,前16位都是0。另一方面這與C語(yǔ)言不兼容,在c語(yǔ)言中0字節(jié)表示字符串的結(jié)尾,庫(kù)函數(shù)strlen等函數(shù)依賴這一點(diǎn),如果按UTF-32存儲(chǔ),其中有很多0字節(jié)并不表示字符串結(jié)尾。

Ken Thompson發(fā)明了UTF-8編碼,可以很好的解決以上問題。Unicode 和 UTF-8 之間的轉(zhuǎn)換關(guān)系表如下:

碼點(diǎn)起值碼點(diǎn)終值字節(jié)序列Byte1Byte2Byte3Byte4Byte5Byte6U+0000U+007F10xxxxxxxU+0080U+07FF2110xxxxx10xxxxxxU+0800U+FFFF31110xxxx10xxxxxx10xxxxxxU+10000U+1FFFFF411110xxx10xxxxxx10xxxxxx10xxxxxxU+200000U+3FFFFFF5111110xx10xxxxxx10xxxxxx10xxxxxx10xxxxxxU+4000000U+7FFFFFFF61111110x10xxxxxx10xxxxxx10xxxxxx10xxxxxx10xxxxxx

第一個(gè)字節(jié)要么最高位是0(ASCII碼),要么最高位都是1,最高位之后的1的個(gè)數(shù)決定了后面的有多少個(gè)字節(jié)也屬于當(dāng)前字符編碼,例如111110xx,最高位之后還有4個(gè)1,表示后面的4個(gè)字節(jié)屬于當(dāng)前編碼。后面的每個(gè)字節(jié)的最高位都是10,可以和第一個(gè)字節(jié)區(qū)分開來。后面字節(jié)的x表示的就是UCS編碼。所以UTF-8就像一列火車,第一個(gè)字節(jié)是車頭,包含了后面的哪幾個(gè)字節(jié)也屬于當(dāng)前這列火車的信息,后面的字節(jié)是車廂,其中承載著UCS編碼。

以中文字符“你”為例,對(duì)應(yīng)的Unicode為"U+4f60",二進(jìn)制表示為0100 1111 0110 0000。按照表中的規(guī)則編碼成UTF-8就是11100100 10111101 10100000(0xe4 0xbd 0xa0)。

結(jié)論:

Unicode本質(zhì)是字符集,在這個(gè)集合中的任意一個(gè)字符都可以用一個(gè)四字節(jié)來表示。

UTF-8是編碼規(guī)則,可以通過這個(gè)規(guī)則將Unicode字符集中任一字符對(duì)應(yīng)的字節(jié)轉(zhuǎn)換為另一個(gè)字節(jié)序列。UTF-8只是編碼規(guī)則中的一種,其它的編碼規(guī)則還有UTF-16,UTF-32等。

寬字符類型wchar_t

在介紹寬字符前先了解下locale。因?yàn)槎嘧止?jié)字符串和寬字符串的轉(zhuǎn)換和locale相關(guān)。

locale

什么是locale

區(qū)域設(shè)置(locale),也稱作“本地化策略集”、“本地環(huán)境”,是表達(dá)程序用戶地區(qū)方面的軟件設(shè)定。在linux執(zhí)行l(wèi)ocale可以查看當(dāng)前l(fā)ocale設(shè)置:

ubuntu@VM-0-16-ubuntu:~$ locale
LANG=zh_CN.UTF-8
LANGUAGE=
LC_CTYPE="zh_CN.UTF-8"
LC_NUMERIC="zh_CN.UTF-8"
LC_TIME="zh_CN.UTF-8"
LC_COLLATE="zh_CN.UTF-8"
LC_MONETARY="zh_CN.UTF-8"
LC_MESSAGES="zh_CN.UTF-8"
LC_PAPER="zh_CN.UTF-8"
LC_NAME="zh_CN.UTF-8"
LC_ADDRESS="zh_CN.UTF-8"
LC_TELEPHONE="zh_CN.UTF-8"
LC_MEASUREMENT="zh_CN.UTF-8"
LC_IDENTIFICATION="zh_CN.UTF-8"
LC_ALL=

可以將locale理解為一系列環(huán)境變量。locale環(huán)境變量值的格式為language_area.charset。languag表示語(yǔ)言,例如英語(yǔ)或中文;area表示使用該語(yǔ)言的地區(qū),例如美國(guó)或者中國(guó)大陸;charset表示字符集編碼,例如UTF-8或者GBK。

這些環(huán)境變量會(huì)對(duì)日期格式,數(shù)字格式,貨幣格式,字符處理等多個(gè)方面產(chǎn)生影響。

參考資料:

  1. locale wiki
  2. Environment Variables

如何設(shè)置系統(tǒng)默認(rèn)的locale

修改配置文件/etc/default/locale,比如要將locale設(shè)為zh_CN.UTF-8,添加如下語(yǔ)句LANG=zh_CN.UTF-8

locale環(huán)境變量有何作用

以LC_TIME為例,該變量會(huì)影響strftime()等函數(shù)。size_t strftime(char *str, size_t maxsize, const char *format, const struct tm *timeptr)

strftime根據(jù)format中定義的格式化規(guī)則,格式化結(jié)構(gòu)timeptr表示的時(shí)間,并把它存儲(chǔ)在str中。

#include <locale.h>
#include <stdio.h>
#include <time.h>
int main () {
 time_t currtime;
 struct tm *timer;
 char buffer[80];
 time( &currtime );
 timer = localtime( &currtime );
 printf("Locale is: %s
", setlocale(LC_TIME, "en_US.iso88591"));
 strftime(buffer,80,"%c", timer );
 printf("Date is: %s
", buffer);
 printf("Locale is: %s
", setlocale(LC_TIME, "zh_CN.UTF-8"));
 strftime(buffer,80,"%c", timer );
 printf("Date is: %s
", buffer);
 printf("Locale is: %s
", setlocale(LC_TIME, ""));
 strftime(buffer,80,"%c", timer );
 printf("Date is: %s
", buffer);
 return(0);
}

編譯后運(yùn)行結(jié)果如下:

Locale is: en_US.iso88591
Date is: Sun 07 Jul 2019 04:08:39 PM CST
Locale is: zh_CN.UTF-8
Date is: 2019年07月07日 星期日 16時(shí)08分39秒
Locale is: zh_CN.UTF-8
Date is: 2019年07月07日 星期日 16時(shí)08分39秒

可以看到對(duì)LC_TIME設(shè)置不同的值后,調(diào)用strftime()會(huì)產(chǎn)生不同的結(jié)果。

char* setlocale (int category, const char* locale);可以用來對(duì)當(dāng)前程序進(jìn)行地域設(shè)置。

category:用于指定設(shè)置影響的范圍,LC_CTYPE影響字符分類和字符轉(zhuǎn)換,LC_TIME影響日期和時(shí)間的格式,LC_ALL影響所有內(nèi)容。

locale:用于指定變量的值,上例中分別使用了"en_US.iso88591","zh_CN.UTF-8"和空字符串"",""表示使用當(dāng)前操作系統(tǒng)默認(rèn)的區(qū)域設(shè)置。

參考資料:

setlocale()

為什么需要寬字符類型

“你好”對(duì)應(yīng)的Unicode分別為"U+4f60"和"U+597d”,對(duì)應(yīng)的UTF-8編碼分別為“0xe4 0xbd 0xa0”和“0xe5 0xa5 0xbd”

多字節(jié)字符串在編譯后的可執(zhí)行文件以UTF-8編碼保存

#include <stdio.h>
#include <string.h>
int main(void) {
 char s[] = "你好";
 size_t len = strlen(s);
 printf("len = %d
", (int)len);
 printf("%s
", s);
 return 0;
}

編譯后執(zhí)行,輸出如下:

len = 6
你好

od編譯后的可執(zhí)行文件,可以發(fā)現(xiàn)"你好"以UFT-8編碼保存,也就是“0xe4 0xbd 0xa0”和“0xe5 0xa5 0xbd”6個(gè)字節(jié)。

strlen()函數(shù)只管結(jié)尾的0字節(jié)而不管字符串里存的是什么,所以len是6,也就是“你好”的UFT-8編碼的字節(jié)數(shù)。

printf("%s ", s);相當(dāng)于將“0xe4 0xbd 0xa0”和“0xe5 0xa5 0xbd”6個(gè)字節(jié)write到當(dāng)前終端的設(shè)備文件,如果當(dāng)前終端的驅(qū)動(dòng)程序能識(shí)別UTF-8編碼就能打印漢字,如果當(dāng)前字符終端的驅(qū)動(dòng)程序不能識(shí)別UTF-8就打印不出漢字。

寬字符串在編譯后可執(zhí)行文件中以Unicode保存

#include <wchar.h>
#include <stdio.h>
#include <locale.h>
int main(void) {
 setlocale(LC_ALL, "zh_CN.UTF-8"); //設(shè)置locale
 wchar_t s[] = L"你好";
 size_t len = wcslen(s);
 printf("len = %d
", (int)len);
 printf("%ls
", s);
 return 0;
}

編譯后執(zhí)行,輸出如下:

len = 2
你好

對(duì)編譯后的可執(zhí)行文件執(zhí)行od命令,可以找到如下這些字節(jié):

193 0003020 001  002  ` O   } Y   
   
194 00020001 00004f60 0000597d 0000000a

00004f60正是“你”對(duì)應(yīng)的Unicode,0000597d是“好”對(duì)應(yīng)的Unicode。所以對(duì)于寬字符串是按Unicode保存在可執(zhí)行文件中的。

wchar_t是寬字符類型。在字符常量或者字符串前加L就表示寬字符常量或者寬字符串。所以len是2。

wcslen()和strlen()不同,不是見到0字節(jié)就結(jié)束而是要遇到UCS編碼為0的字符才結(jié)束。

目前寬字符在內(nèi)存中以Unicode進(jìn)行保存,但是要write到終端仍然需要以多字節(jié)編碼輸出,這樣終端驅(qū)動(dòng)程序才能識(shí)別,所以printf在內(nèi)部把寬字符串轉(zhuǎn)換成多字節(jié)字符串,然后write出去。這個(gè)轉(zhuǎn)換過程受locale影響,setlocale(LC_ALL, "zh_CN.UTF-8");設(shè)置當(dāng)前進(jìn)程的LC_ALL為zh_CN.UTF-8,所以printf將Unicode轉(zhuǎn)成多字節(jié)的UTF-8編碼,然后write到終端設(shè)備。如果將setlocale(LC_ALL, "zh_CN.UTF-8");改為setlocale(LC_ALL, en_US.iso88591):打印結(jié)果中將不會(huì)輸出"你好"。

一般來說程序在內(nèi)存計(jì)算時(shí)通常以寬字符編碼,存盤或者網(wǎng)絡(luò)發(fā)送則用多字節(jié)編碼。

多字節(jié)字符串和寬字符串相互轉(zhuǎn)換

c語(yǔ)言中提供了多字節(jié)字符串和寬字符串相互轉(zhuǎn)換的函數(shù)。

#include <stdlib.h>
size_t mbstowcs(wchar_t *dest, const char *src, size_t n);
size_t wcstombs(char *dest, const wchar_t *src, size_t n);

mbstowcs()將多字節(jié)字符串轉(zhuǎn)換為寬字符串。

wcstombs()將寬字符串轉(zhuǎn)換為多字節(jié)字符串。

考慮下面的例子:

#include <locale.h>
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <wchar.h>
#include <string.h>
wchar_t* str2wstr(const char const* s) {
 const size_t buffer_size = strlen(s) + 1;
 wchar_t* dst_wstr = (wchar_t *)malloc(buffer_size * sizeof (wchar_t));
 wmemset(dst_wstr, 0, buffer_size);
 mbstowcs(dst_wstr, s, buffer_size); 
 return dst_wstr;
}
void printBytes(const unsigned char const* s, int len) {
 for (int i = 0; i < len; i++) {
 printf("0x%02x ", *(s + i));
 }
 printf("
");
}
int main () {
 char s[10] = "你好"; //內(nèi)存中對(duì)應(yīng)0xe4 0xbd 0xa0 0xe5 0xa5 0xbd 0x00 
 wchar_t ws[10] = L"你好"; //內(nèi)存中對(duì)應(yīng)0x60 0x4f 0x00 0x00 0x7d 0x59 0x00 0x00 0x00 0x00 0x00 0x00 
 printf("Locale is: %s
", setlocale(LC_ALL, "zh_CN.UTF-8")); //Locale is: zh_CN.UTF-8
 printBytes(s, 7); //0xe4 0xbd 0xa0 0xe5 0xa5 0xbd 0x00 
 printBytes((char *)ws, 12); //0x60 0x4f 0x00 0x00 0x7d 0x59 0x00 0x00 0x00 0x00 0x00 0x00 
 printBytes((char *)str2wstr(s), 12); //0x60 0x4f 0x00 0x00 0x7d 0x59 0x00 0x00 0x00 0x00 0x00 0x00 
 return(0);
}

編譯后,執(zhí)行結(jié)果如下:

Locale is: zh_CN.UTF-8
0xe4 0xbd 0xa0 0xe5 0xa5 0xbd 0x00 
0x60 0x4f 0x00 0x00 0x7d 0x59 0x00 0x00 0x00 0x00 0x00 0x00 
0x60 0x4f 0x00 0x00 0x7d 0x59 0x00 0x00 0x00 0x00 0x00 0x00 

第二行輸出也印證了我們之前說的多字節(jié)字符串在內(nèi)存中以UTF-8存儲(chǔ),"0xe4 0xbd 0xa0 0xe5 0xa5 0xbd"正是"你好"的UTF-8編碼。

第三行輸出印證了之前說的寬字符串在內(nèi)存中以Unicode存儲(chǔ),"0x60 0x4f 0x00 0x00 0x7d 0x59 0x00 0x00"正好是寬字符串L"你好"對(duì)應(yīng)的Unicode。

setlocale(LC_ALL, "zh_CN.UTF-8")設(shè)置locale,程序?qū)⒁訳TF-8解碼寬字符串。調(diào)用mbstowcs()后,可以看到“你好”的UTF-8編碼 "0xe4 0xbd 0xa0 0xe5 0xa5 0xbd 0x00"確實(shí)被轉(zhuǎn)換成了“你好”對(duì)應(yīng)的Unicode "0x60 0x4f 0x00 0x00 0x7d 0x59 0x00 0x00 0x00 0x00 0x00 0x00"。

如果將setlocale(LC_ALL, "zh_CN.UTF-8")換成setlocale(LC_ALL, "en_US.iso88591 ");那么最后一行的輸出也就會(huì)不一樣。

分享到:
標(biāo)簽:UTF
用戶無頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定