Linux Unicode编程

Unicode不仅是编程工具,还是政治和经济工具。 不包含世界语言支持的应用程序通常只能由读写ASCII支持的语言的个人使用。 这使基于ASCII的计算机技术无法为世界上大多数人所接受。 Unicode允许程序利用世界上任何字符集,因此支持任何语言。

Unicode允许程序员提供普通人可以其母语使用的软件。 消除了学习外语的先决条件,并且更容易实现计算机技术的社会和金钱利益。 不难想象,如果用户不得不学习Urdu以使用Internet浏览器,那么在美国将看到很少的计算机使用。 网络永远不会发生。

Linux在很大程度上致力于Unicode。 对Unicode的支持被嵌入到内核和代码开发库中。 在大多数情况下,它是使用程序中的一些简单命令自动合并到代码中的。

所有现代字符集的基础是1968年以ANSIX3.4出版的美国信息交换标准码(ASCII)。 值得注意的例外是在ASCII之前定义的IBM EBCDIC(扩展的二进制编码的十进制信息代码)。 ASCII是一种编码字符集(CCS),换句话说,是从整数到字符表示的映射。 ASCII CCS允许以八位(2、0或1值的底数)字段或字节(2 ^ 8 = 256)表示256个字符。 这是一个高度受限的CCS,不允许代表许多不同语言(如中文和日语),科学符号,甚至古代文字(符文和象形文字)和音乐的所有字符。 更改字节的大小以允许对更大的字符集进行编码将是有用的,但完全不切实际。 所有计算机都基于八位字节。 该解决方案是一种字符编码方案(CES),它可以使用固定长度或可变长度的多字节序列表示大于256的数字。 然后,这些值通过CCS映射到它们表示的字符。

Unicode定义

Unicode通常用作一个通用术语,指的是两个字节的字符编码方案。 Unicode CCS 3.1正式称为ISO 10646-1通用多八位位组编码字符集(UCS)。 Unicode 3.1添加了44,946个新的编码字符。 Unicode 3.0中已有49,194个现有字符,现在总数为94,140个。

Unicode CCS利用128个三维组的三维编码空间。 每个组具有256个二维平面。 每个平面由256个一维行组成,每行有256个像元。 单元在此编码空间编码一个字符,或者该单元被声明为未使用。 这种编码概念称为UCS-4; 四个八位位组用于表示每个字符,指定组,平面,行和单元格。

第一个平面(00组的平面00)是基本多语言平面(BMP)。 BMP定义了字母,音节和表意文字中常用的字符,以及各种符号和数字。 随后的平面用于尚未发明的其他字符或其他编码实体。 需要全面的语言来应对世界上所有的语言; 具体来说,一些东亚语言大约有64,000个字符。

BMP用作标识为ISO 10646的UCS-2形式的两个八位字节的编码字符集。ISO10646 USC-2通常称为Unicode(并且与Unicode相同)。 与所有UCS平面一样,此BMP包含256行,每行256个单元,并且仅通过BMP中的行和单元八位位组在一个单元处对字符进行编码。 这允许将16位编码字符用于编写最重要的商业语言。 USC-2不需要代码页切换,代码扩展或代码状态。 USC-2是一种将Unicode集成到软件中的简单方法,但是仅支持Unicode BMP受到限制。

为了用8位字节表示超过2 ^ 8 = 256个字符的字符编码系统(CCS),需要一个字符编码方案(CES)。

Unicode转换

在UNIX中,最常用的CES是UTF-8。 它允许完全支持整个Unicode,所有页面和平面,并且仍将正确读取标准ASCII。 UTF-8的替代方案是:UCS-4,UTF-16,UTF-7,5,UTF-7,SCSU,HTML和JAVA。

Unicode转换格式(UTF)是CES,它们通过在多字节代码中映射值来支持Unicode的使用。 本文将研究最流行的格式UTF-8 CCS。

UTF-8

UTF-8转换格式正成为交换国际文本信息的主要方法,因为它可以支持世界上所有的语言并且兼容ASCII。 UTF-8使用可变宽度编码。 编号为0到0x7f(127)的字符将自己编码为一个字节,较大的字符值将编码为2到6个字节。

表1. UTF-8编码

0x00000000-0x0000007F: 0 xxxxxxx
0x00000080-0x000007FF: 110 xxxxx 10 xxxxxx
0x00000800-0x0000FFFF: 1110 xxxx 10 xxxxxx 10 xxxxxx
0x00010000-0x001FFFFF: 11110 xxx 10 xxxxxx 10 xxxxxx 10 xxxxxx
0x00200000-0x03FFFFFF: 111110 xx 10 xxxxxx 10 xxxxxx 10 xxxxxx 10 xxxxxx
0x04000000-0x7FFFFFFF: 1111110 x 10 xxxxxx 10 xxxxxx 10 xxxxxx 10 xxxxxx 10 xxxxxx

10 xxxxxx字节是一个连续字节,其xxxxxx位位置以二进制表示形式填充了字符代码编号的位。 使用了可以表示代码的最短的多字节序列。

UTF-8编码示例

Unicode字符版权符号字符0xA9 = 1010 1001以UTF-8编码为: 11000010 10101001 = 0xC2 0xA9

并且“不等于”符号字符0x2260 = 0010 0010 0110 0000编码为: 11100010 10001001 10100000 = 0xE2 0x89 0xA0

通过取出continuation byte值可以看到原始值:
[1110]0010 [10]001001 [10]100000 0010 001001 100000 0010 0010 0110 0000 = 0x2260

第一个字节定义了要跟随的八位字节数,或者如果它等于或小于7F,则为ASCII等效值。 以10 xxxxxx开头的每个八位位组可确保不会将一个字节误认为ASCII值。

UTF支持

在Linux下开始使用UTF-8之前,请确保发行版具有glibc 2.2和XFree86 4.0或更高版本。 较早的版本缺少UTF-8语言环境支持和ISO10646-1 X11字体。

在UTF-8之前,Linux用户使用了各种不同的语言特定的ASCII扩展,例如欧洲的ISO 8859-1或ISO 8859-2,希腊的ISO 8859-7和俄罗斯的KOI-8 / ISO 8859-5 / CP1251(西里尔文)。 这使得数据交换成为问题,需要针对这些编码之间的差异对应用软件进行编程。 支持不完整,交流未经测试。 主要的Linux发行商和应用程序开发人员正在努力使Unicode(主要是UTF-8格式)成为Linux的标准格式。

为了将文件标识为Unicode,Microsoft建议所有Unicode文件都应以字符ZERO WIDTH NOBREAK SPACE(U + FEFF)开头。 这充当“签名”或“字节序标记(BOM)”,以标识文件中使用的编码和字节序。 但是,Linux / UNIX不使用BOM表,因为这会破坏现有的ASCII文件语法约定。 在POSIX系统上,选定的语言环境标识了进程的所有输入和输出文件中期望的编码。

有两种向Linux应用程序添加UTF-8支持的方法。 首先,数据以UTF-8格式存储在任何地方,这仅导致很少的软件更改(被动)。 或者,使用标准C库函数(已转换)将已读取的UTF-8数据转换为宽字符数组。 与函数wcsrtombs()一起输出时,字符串将转换回UTF-8:

清单1. wcsrtombs()
#include <wchar.h> 
size_t wcsrtombs (char *dest, const wchar_t **src, size_t len, mbstate_t *ps);

选择的方法取决于应用程序的性质。 大多数应用程序可以被动运行。 这就是为什么在UNIX中普遍使用UTF-8的原因。 catecho程序无需修改。 字节流只是一个字节流,并且不对其进行任何处理。 在UTF-8下ASCII字符和控制代码不变。

对于通过计数字节来计数字符的程序,需要进行一些小的更改。 在UTF-8应用程序中,不计算任何连续字节。 如果已选择UTF-8语言环境,则C库strlen(s)函数需要替换为mbstowcs()函数:

清单2. mbstowcs()函数
#include <stdlib.h>
size_t mbstowcs(wchar_t *pwcs, const char *s, size_t n);

strlen的常见用法是估计显示宽度。 中文和其他表意字符将占据两个列的位置。 wcwidth()函数用于测试每个字符的显示宽度:

清单3. wcwidth()函数
#include <wchar.h> 
int wcwidth(wchar_t wc);

C对Unicode的支持

正式地,从GNU glibc 2.2开始,类型wchar_t旨在仅用于32位ISO 10646值,而与当前使用的语言环境无关。 通过ISO C99要求的__STDC_ISO_10646__宏的定义将其通知应用程序。 定义__STDC_ISO_10646__表示wchar_t是Unicode。 精确值是yyyymmL形式的十进制常数。 例如,使用:

清单4.指示wchar_t是Unicode
#define __STDC_ISO_10646__  200104L

表示wchar_t类型的值是ISO / IEC 10646以及指定年份和月份的所有修订和技术勘误所定义的字符的编码表示形式。

如本示例所示,它将被利用,该示例使用宏来确定在ISO C99可移植代码中编写双引号的方法:

清单5.确定写双引号的方法
#if __STDC_ISO_10646__  
   printf("%lc", 0x201c);  
#else  
   putchar('"');  
#fi

语言环境

激活UTF-8的正确方法是POSIX语言环境机制。 语言环境是一种配置设置,其中包含有关特定于区域性的软件行为约定的信息。 这包括字符编码,日期/时间符号,排序规则和度量系统。 语言环境的名称通常由ISO 639-1语言,ISO 3166-1国家/地区代码以及可选的编码名称和其他限定符组成。 您可以使用locale -a命令获取系统上安装的所有语言环境的列表(通常在/ usr / lib / locale /中)。

如果未预安装UTF-8语言环境,则可以使用localedef命令生成它。 要为特定用户生成并激活德语UTF-8语言环境,请使用以下语句:

清单6.为特定用户生成一个语言环境
localedef -v -c -i de_DE -f UTF-8 $HOME/local/locale/de_DE.UTF-8
export LOCPATH=$HOME/local/locale
export LANG=de_DE.UTF-8

有时为所有用户添加UTF-8语言环境很有用。 可以使用以下说明通过root用户进行:

清单7.为所有用户生成一个语言环境
localedef -v -c -i de_DE -f UTF-8 /usr/share/locale/de_DE.UTF-8

要使其成为每个用户的默认语言环境,请在/ etc / profile文件中添加以下行:

清单8.为所有用户设置默认语言环境
export LANG=de_DE.UTF-8

处理多字节字符代码序列的函数的行为取决于当前语言环境的LC_CTYPE类别;例如, 它确定与语言环境相关的多字节编码。 值LANG = de_DE(德语)将导致输出按照ISO 8859-1进行格式化。 值LANG = de_DE.UTF-8会将输出格式化为UTF-8。 语言环境设置将导致printf%ls格式说明符调用wcsrtombs()函数,以将宽字符参数字符串转换为与语言环境相关的多字节编码。 语言环境中的国家/地区标识符,例如:LC_CTYPE = en_GB(英国的英语)和LC_CTYPE = en_AU(澳大利亚的英语),仅在LC_MONETARY类别中货币名称和打印货币金额的规则有所不同。

将环境变量LANG设置为首选语言环境的名称。 当C程序执行setlocale()函数时:

清单9. setlocale()函数
#include <stdio.h>
#include <locale.h>
//char *setlocale(int category, const char *locale);
int main()
{
  if (!setlocale(LC_CTYPE, "")) 
  {
    fprintf(stderr, "Locale not specified. Check LANG, LC_CTYPE, LC_ALL.
");
    return 1;
  }

该库将按该顺序测试环境变量LC_ALL,LC_CTYPE和LANG。 其中第一个具有值的值将确定要为LC_CTYPE类别加载哪些语言环境数据。 语言环境数据分为不同的类别。 LC_CTYPE值定义字符编码,LC_COLLATE定义排序顺序。 LANG环境变量用于为所有类别设置默认语言环境,但是LC_ *变量可用于覆盖各个类别。

您可以使用locale charmap命令查询当前语言环境中的字符编码locale charmap 。 如果您在LC_CTYPE类别中成功选择了UTF-8语言环境,则应该显示UTF-8。 命令locale -m提供了一个列表,其中列出了所有已安装字符编码的名称。

如果您仅使用C库多字节函数在内部使用的外部字符编码和wchar_t编码之间进行所有转换,则C库将根据LC_CTYPE来使用正确的编码。 该程序甚至不必显式编码为当前的多字节编码。

如果要求应用程序特别了解UTF-8(或其他)转换方法并且不使用libc多字节函数,则应用程序必须确定是否激活UTF-8模式。 具有<langinfo.h>库头的X / Open兼容系统可以使用以下代码:

清单10.检测当前语言环境是否使用UTF-8编码
BOOL utf8_mode = FALSE;

if( !  strcmp(nl_langinfo(CODESET), "UTF-8")
   utf8_mode = TRUE;

检测当前语言环境是否使用UTF-8编码。 必须首先调用setlocale(LC_CTYPE, "")函数以根据环境变量设置语言环境。 nl_langinfo(CODESET)函数也是locale charmap命令调用以查找由当前语言环境指定的编码名称的函数。

可以使用的另一种方法是查询区域设置环境变量:

清单11.查询语言环境环境变量
char *s;
BOOL utf8_mode = FALSE;

if ((s = getenv("LC_ALL")) || (s = getenv("LC_CTYPE")) || (s = getenv ("LANG"))) 

{
   if (strstr(s, "UTF-8"))
      utf8_mode = TRUE;
}

此测试假定UTF-8语言环境名称中的值“ UTF-8”(并非始终为true),因此应使用nl_langinfo()方法。

摘要

为了支持世界上的语言,需要使用8位字节字符编码方案(CES)的超过2 ^ 8 = 256个ASCII字符(使用无符号字节的扩展版本)的字符编码系统(CCS)。 Unicode作为具有CCS的CCS来进行,它具有128个3维组的4维编码空间,具有94,140个定义的字符值,许多CES方法都支持该方法,在Linux中,更流行的是Unicode转换格式UTF-8。


翻译自: https://www.ibm.com/developerworks/java/library/l-linuni/index.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值