
[编辑] 编码方式
Unicode的编码方式与ISO 10646的通用字符集(Universal Character Set,UCS)概念相对应,目前实际应用的Unicode版本对应于UCS-2,使用16位的编码空间。也就是每个字符占用2个字节。这样理论上一共最多可以表示216即65536个字符。基本满足各种语言的使用。实际上目前版本的Unicode尚未填充满这16位编码,保留了大量空间作为特殊使用或将来扩展。
上述16位Unicode字符构成基本多文种平面(Basic Multilingual Plane,简称BMP)。最新(但未实际广泛使用)的Unicode版本定义了16个辅助平面,两者合起来至少需要占据21位的编码空间,比3字节略少。但事实上辅助平面字符仍然占用4字节编码空间,与UCS-4保持一致。未来版本会扩充到ISO 10646-1实现级别3,即涵盖UCS-4的所有字符。UCS-4是一个更大的尚未填充完全的31位字符集,加上恒为0的首位,共需占据32位,即4字节。理论上最多能表示231个字符,完全可以涵盖一切语言所用的符号。
BMP字符的Unicode编码表示为U+hhhh,其中每个h 代表一个十六进制数位。与UCS-2编码完全相同。对应的4字节UCS-4编码后两个字节一致,前两个字节的所有位均为0。
关于Unicode和ISO 10646及UCS的详细关系 ,请参看通用字符集。
[编辑] 实现方式
Unicode的实现方式不同于编码方式。一个字符的Unicode编码是确定的。但是在实际传输过程中,由于不同系统平台的设计不一定一致,以及出于节省空间的目的,对Unicode编码的实现方式有所不同。Unicode的实现方式称为Unicode转换格式(Unicode Translation Format,简称为UTF)。
例如,如果一个仅包含基本7位ASCII字符的Unicode文件,如果每个字符都使用2字节的原Unicode编码传输,其第一字节的8位始终为0。这就造成了比较大的浪费。对于这种情况,可以使用UTF-8编码,这是一种变长编码,它将基本7位ASCII字符仍用7位编码表示,占用一个字节(首位补0)。而遇到与其他Unicode字符混合的情况,将按一定算法转换,每个字符使用1-3个字节编码,并利用首位为0或1进行识别。这样对以7位ASCII字符为主的西文文档就大大节省了编码长度(具体方案参见UTF-8)。类似的,对未来会出现的需要4个字节的辅助平面字符和其他UCS-4扩充字符,2字节编码的UTF-16也需要通过一定的算法进行转换。
再如,如果直接使用与Unicode编码一致(仅限于BMP字符)的UTF-16编码,由于每个字符占用了两个字节,在Macintosh (Mac)机和PC机上,对字节顺序的理解是不一致的。这时同一字节流可能会被解释为不同内容,如某字符为十六进制编码4E59,按两个字节拆分为4E和59,在Mac上读取时是从低字节开始,那么在Mac OS会认为此4E59编码为594E,找到的字符为“奎”,而在Windows上从高字节开始读取,则编码为U+4E59的字符为“乙”。就是说在Windows下以UTF-16编码保存一个字符“乙”,在Mac OS里打开会显示成“奎”。此类情况说明UTF-16的编码顺序若不加以人为定义就可能发生混淆,于是在UTF-16编码实现方式中使用了大端序(Big-Endian, 简写为UTF-16 BE)、小端序(Little-Endian,简写为UTF-16 LE)的概念,以及可附加的位元组顺序记号解决方案,目前在PC机上的Windows系统和Linux系统对于UTF-16编码默认使用UTF-16 LE。(具体方案参见UTF-16)
此外Unicode的实现方式还包括UTF-7、Punycode、CESU-8、SCSU、UTF-32等,这些实现方式有些仅在一定的国家和地区使用,有些则属于未来的规划方式。目前通用的实现方式是UTF-16小尾序(LE)、UTF-16大尾序(BE)和UTF-8。在微软公司Windows XP *** 作系统附带的记事本(Notepad)中,“另存为”对话框可以选择的四种编码方式除去非Unicode编码的ANSI(对于英文系统即ASCII编码,中文系统则为GB2312或Big5编码) 外,其余三种为“Unicode”(对应UTF-16 LE)、“Unicode big endian”(对应UTF-16 BE)和“UTF-8”。
目前辅助平面的工作主要集中在第二和第三平面的中日韩统一表意文字中,因此包括GBK、GB18030、Big5等简体中文、繁体中文、日文、韩文以及越南喃字的各种编码与Unicode的协调性被重点关注。考虑到Unicode最终要涵盖所有的字符,从某种意义而言,这些编码方式也可视作Unicode的出现于其之前的既成事实的实现方式,如同ASCII及其扩展Latin-1一样,后两者的字符在16位Unicode编码空间中的编码第一字节各位全为0,第二字节编码与原编码完全一致。但上述东亚语言编码与Unicode编码的对应关系要复杂得多。
function unescape($str) {$str = rawurldecode($str)
preg_match_all("/(?:%u.{4})|.{4}|\d+|.+/U",$str,$r)
$ar = $r[0]
//print_r($ar)
foreach($ar as $k=>$v) {
if(substr($v,0,2) == "%u"){
$ar[$k] = iconv("UCS-2BE","UTF-8",pack("H4",substr($v,-4)))
}
elseif(substr($v,0,3) == ""){
$ar[$k] = iconv("UCS-2BE","UTF-8",pack("H4",substr($v,3,-1)))
}
elseif(substr($v,0,2) == "") {
$ar[$k] = iconv("UCS-2BE","UTF-8",pack("n",substr($v,2,-1)))
}
}
return join("",$ar)
}
echo unescape("紫星蓝")
今天有用户反馈,表单系统用户提交的数据中文会乱码。测试发现问题出在 iconv 转换上。
iconv('UCS-2', 'GBK', '中文')
Google 搜索发现,原因是 Linux 服务器上 UCS-2 编码方式与 Winodws 不一致。
于是,我改成 iconv('UCS-2BE', 'GBK', '中文') 试试,中文正常了
以下是有关两个平台 UCS-2 编码的潜规则:
1, UCS-2 不等于 UTF-16。 UTF-16 每个字节使用 ASCII 字符范围编码,而 UCS-2 对每个字节的编码可以超出 ASCII 字符范围。UCS-2 和 UTF-16 对每个字符至多占两个字节,但是他们的编码是不一样的。
2, 对于 UCS-2, windows 下默认是 UCS-2LE。用 MultibyteToWidechar(或者A2W)生成的是 UCS-2LE 的 unicode。windows记事本可以将文本保存为 UCS-2BE,相当于多了层转换。
3, 对于 UCS-2, linux 下默认是 UCS-2BE。用iconv(指定UCS-2)来转换生成的是 UCS-2BE 的 unicode。如果转换windows平台过来的 UCS-2, 需要指定 UCS-2LE。
4, 鉴于windows和linux等多个平台对 UCS-2 的理解不同(UCS-2LE,UCS-2BE)。MS 主张 unicode 有个引导标志(UCS-2LE FFFE, UCS-2BE FEFF),以表明下面的字符是 unicode 并且判别 big-endian 或 little-endian。 所以从 windows 平台过来的数据发现有这个前缀,不用慌张。
5, linux 的编码输出,比如从文件输出,从 printf 输出,需要控制台做适当的编码匹配(如果编码不匹配,一般和该程序编译时的编码有若干关系),而控制台的转换输入需要查看当前的系统编码。比如控制台当前的编码是 UTF-8, 那么 UTF-8 编码的东西能正确显示,GBK 就不能;同样,当前编码是 GBK, 就能显示 GBK 编码,后来的系统应该更智能的处理好更多的转换了。不过通过 putty 等终端还是需要设置好终端的编码转换以解除乱码的烦恼。
PHP中对汉字进行UNICODE编码和解码的实现
//将内容进行UNICODE编码
function unicode_encode($name)
{
$name = iconv('UTF-8', 'UCS-2', $name)
$len = strlen($name)
$str = ''
for ($i = 0$i <$len - 1$i = $i + 2)
{
$c = $name[$i]
$c2 = $name[$i + 1]
if (ord($c) >0)
{// 两个字节的文字
$str .= '\u'.base_convert(ord($c), 10, 16).base_convert(ord($c2), 10, 16)
}
else
{
$str .= $c2
}
}
return $str
}
$name = 'MY,你大爷的'
$unicode_name=unicode_encode($name)
echo '<h3>'.$unicode_name.'</h3>'
// 将UNICODE编码后的内容进行解码
function unicode_decode($name)
{
// 转换编码,将Unicode编码转换成可以浏览的utf-8编码
$pattern = '/([\w]+)|(\\\u([\w]{4}))/i'
preg_match_all($pattern, $name, $matches)
if (!empty($matches))
{
$name = ''
for ($j = 0$j <count($matches[0])$j++)
{
$str = $matches[0][$j]
if (strpos($str, '\\u') === 0)
{
$code = base_convert(substr($str, 2, 2), 16, 10)
$code2 = base_convert(substr($str, 4), 16, 10)
$c = chr($code).chr($code2)
$c = iconv('UCS-2', 'UTF-8', $c)
$name .= $c
}
else
{
$name .= $str
}
}
}
return $name
}
echo 'MY,\u4f60\u5927\u7237\u7684 ->'.unicode_decode($unicode_name)
docker支持多种graphDriver,包括vfs、devicemapper、overlay、overlay2、aufs等等,其中最常用的就是aufs了,但随着linux内核3.18把overlay纳入其中,overlay的地位变得更重,最近也在自己的虚拟机上用overlay2作为docker存储驱动实验了一番,下面来做一个简单的笔记和总结。 docker默认的存储目录是 /var/lib/docker ,下面我们简单打印一下这个目录: 在这里,我们只关心 image 和 overlay2 就足够了。 做这个实验之前,我们应该先启动一个容器,在这里使用nginx作为实验: 可以看到新启动的nginx容器的id是 86b5733e54c7 ,我们继续往下看。 上面说了,我们只需要关心 /var/lib/docker/image 和 /var/lib/docker/overlay2 ,可以先到 /var/lib/docker/image 打印一下: 我们只能看到overlay2这个目录,想必聪明的你也猜到了,docker会在 /var/lib/docker/image 目录下按每个存储驱动的名字创建一个目录,如这里的 overlay2 。 接下来,使用 tree 命令浏览一下这个目录: 这里的关键地方是 imagedb 和 layerdb 目录,看这个目录名字,很明显就是专门用来存储元数据的地方,那为什么区分image和layer呢?因为在docker中,image是由多个layer组合而成的,换句话就是layer是一个共享的层,可能有多个image会指向某个layer。 那如何才能确认image包含了哪些layer呢?答案就在 imagedb 这个目录中去找。比如上面启动的nginx容器,我们可以先找到这个容器对应的镜像: 可以看到,imageID是 2bcb04bdb83f ,再次记住这个id,我们打印 /var/lib/docker/image/overlay2/imagedb/content/sha256 这个目录: 第一行的 2bcb04bdb83f7c5dc30f0edaca1609a716bda1c7d2244d4f5fbbdfef33da366c 正是记录我们 nginx 镜像元数据的文件,接下来cat一下这个文件,得到一个长长的json: 由于篇幅原因,我只展示最关键的一部分,也就是 rootfs 。可以看到rootfs的diff_ids是一个包含了3个元素的数组,其实这3个元素正是组成 nginx 镜像的3个layerID,从上往下看,就是底层到顶层,也就是说 5dacd731af1b0386ead06c8b... 是image的最底层。既然得到了组成这个image的所有layerID,那么我们就可以带着这些layerID去寻找对应的layer了。 接下来,我们返回到上一层的 layerdb 中,先打印一下这个目录: 在这里我们只管 mounts 和 sha256 两个目录,再打印一下 sha256 目录: 在这里,我们仅仅发现 5dacd731af1b038.. 这个最底层的layer,那么剩余两个layer为什么会没有呢?那是因为docker使用了chainID的方式去保存这些layer,简单来说就是chainID=sha256sum(H(chainID) diffid),也就是 5dacd731af1b038.. 的上一层的sha256 id是: 这个时候,你能看到 166d13b... 这个layer层的目录了吧?依次类推,我们就能找出所有的layerID的组合。 但是上面我们也说了, /var/lib/docker/image/overlay2/layerdb 存的只是元数据,那么真实的rootfs到底存在哪里呢?其中 cache-id 就是我们关键所在了。我们打印一下 /var/lib/docker/image/overlay2/layerdb/sha256/5dacd731af1b0386ead06c8b1feff9f65d9e0bdfec032d2cd0bc03690698feda/cache-id : 没错,这个id就是对应 /var/lib/docker/overlay2/dffe31c1db6055910b3cd49366a2989d9cd2f3460844437b2190de44807095fa 。因此,以此类推,更高一层的layer对应的cache-id也能找到对应的rootfs,当这些rootfs的diff目录通过联合挂载的方式挂载到某个目录,就能完整整个容器需要的rootfs了。欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)