手动将unicode代码点转换为UTF-8和UTF-16

时间:2011-06-04 23:41:25

标签: unicode utf-8 utf-16

我有一个大学编程考试,一个部分是unicode。

我已经检查了所有的答案,我的讲师没用,所以没有帮助,所以这是你们可能帮助的最后手段。

问题将是:

  

字符串'mЖ丽'有这些unicode代码点U+006DU+0416和   U+4E3D,以十六进制编写的答案,手动编码   字符串为UTF-8和UTF-16。

任何帮助都将非常感激,因为我试图了解这一点。

3 个答案:

答案 0 :(得分:47)

哇。一方面,我很高兴知道大学课程正在教导角色编码是一项艰苦的工作,但实际上知道UTF-8编码规则听起来像是期待很多。 (它会帮助学生pass the Turkey test吗?)

到目前为止,我所看到的将UCS代码点编码为UTF-8的规则的最清晰描述来自许多Linux系统上的utf-8(7)联机帮助页:

Encoding
   The following byte sequences are used to represent a
   character.  The sequence to be used depends on the UCS code
   number of the character:

   0x00000000 - 0x0000007F:
       0xxxxxxx

   0x00000080 - 0x000007FF:
       110xxxxx 10xxxxxx

   0x00000800 - 0x0000FFFF:
       1110xxxx 10xxxxxx 10xxxxxx

   0x00010000 - 0x001FFFFF:
       11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

   [... removed obsolete five and six byte forms ...]

   The xxx bit positions are filled with the bits of the
   character code number in binary representation.  Only the
   shortest possible multibyte sequence which can represent the
   code number of the character can be used.

   The UCS code values 0xd800–0xdfff (UTF-16 surrogates) as well
   as 0xfffe and 0xffff (UCS noncharacters) should not appear in
   conforming UTF-8 streams.

记住图表的“压缩”版本可能更容易:

错位代码点的初始字节开始以1开头,并添加填充1+0。后续字节开始10

0x80      5 bits, one byte
0x800     4 bits, two bytes
0x10000   3 bits, three bytes

您可以通过记录可以用新表示中允许的位填充多少 space 来派生范围:

2**(5+1*6) == 2048       == 0x800
2**(4+2*6) == 65536      == 0x10000
2**(3+3*6) == 2097152    == 0x200000

我知道可以记住导出图表的规则比图表本身更容易。这里希望你也擅长记住规则。 :)

<强>更新

一旦你构建了上面的图表,你可以通过查找它们的范围,从十六进制转换为二进制,根据上面的规则插入位,然后转换回十六进制,将输入的Unicode代码点转换为UTF-8:

U+4E3E

这符合0x00000800 - 0x0000FFFF范围(0x4E3E < 0xFFFF),因此表示形式为:

   1110xxxx 10xxxxxx 10xxxxxx

0x4E3E100111000111110b。将这些位丢弃到上面的x(从右侧开始,我们将在0开头填写缺失的位):

   1110x100 10111000 10111110

一开始就留下x点,用0填写:

   11100100 10111000 10111110

bits to hex转换:

   0xE4 0xB8 0xBE

答案 1 :(得分:37)

维基百科对UTF-8UTF-16的描述很好:

示例字符串的过程:

UTF-8

UTF-8最多使用4个字节来表示Unicode代码点。对于1字节的情况,请使用以下模式:

  

1字节UTF-8 = 0xxxxxxx bin = 7位= 0-7F hex

2字节,3字节和4字节UTF-8的初始字节以2位,3位或4位开始,后跟零位。跟随字节始终以两位模式10开头,为数据留下6位:

  

2字节UTF-8 = 110xxxxx 10xxxxxx bin = 5 + 6(11)位= 80-7FF hex
  3字节UTF-8 = 1110xxxx 10xxxxxx 10xxxxxx bin = 4 + 6 + 6(16)位= 800-FFFF hex
  4字节UTF-8 = 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx bin = 3 + 6 + 6 + 6(21)位= 10000-10FFFF hex

     

Unicode代码点未定义超出10FFFF hex

您的代码点分别是U + 006D,U + 0416和U + 4E3D,分别需要1,2和3字节的UTF-8序列。转换为二进制并分配位:

  

U + 006D = 1101101 bin = 0 1101101 bin = 6D hex
  U + 0416 = 10000 010110 bin = 110 10000 10 010110 bin = D0 96 hex
  U + 4E3D = 0100 111000 111101 bin = 1110 0100 10 111000 10 111101 bin = E4 B8 BD hex

最终字节序列:

  

6D D0 96 E4 B8 BD

或者如果需要以空字符结尾的字符串:

  

6D D0 96 E4 B8 BD 00

UTF-16

UTF-16使用2或4个字节来表示Unicode代码点。算法:

  

U + 0000到U + D7FF使用2字节0000 hex 到D7FF hex
  U + D800到U + DFFF是为4字节UTF-16保留的无效代码点   U + E000到U + FFFF使用2字节E000 hex 到FFFF hex

     

U + 10000到U + 10FFFF使用4字节UTF-16编码,如下所示:

     
      
  1. 从代码点减去10000 hex
  2.   
  3. 将结果显示为20位二进制文​​件。
  4.   
  5. 使用模式110110xxxxxxxxxx 110111xxxxxxxxxx bin 将高10位和低10位编码为两个16位字。
  6.   

使用您的代码点:

  

U + 006D = 006D hex
  U + 0416 = 0416 hex
  U + 4E3D = 4E3D hex

现在,我们还有一个问题。有些机器首先存储16位字最低有效字节的两个字节(所谓的小端机器),有些机器存储最重要的字节(大端机器)。 UTF-16使用代码点U + FEFF(称为字节顺序标记或BOM)来帮助机器确定字节流是否包含大端或小端UTF-16:

  

big-endian = FE FF 00 6D 04 16 4E 3D
  little-endian = FF FE 6D 00 16 04 3D 4E

使用nul-termination,U + 0000 = 0000 hex

  

big-endian = FE FF 00 6D 04 16 4E 3D 00 00
  little-endian = FF FE 6D 00 16 04 3D 4E 00 00

由于你的教师没有提供需要4字节UTF-16的代码点,这里有一个例子:

  

U + 1F031 = 1F031 hex - 10000 hex = F031 hex = 0000111100 0000110001 bin =
   110110 0000111100 110111 0000110001 bin = D83C DC31 hex

答案 2 :(得分:4)

以下程序将执行必要的工作。对于您的目的,它可能不是“手动”,但至少您可以检查您的工作。

#!/usr/bin/perl

use 5.012;
use strict;
use utf8;
use autodie;
use warnings;
use warnings    qw< FATAL utf8 >;
no warnings     qw< uninitialized >;
use open        qw< :std :utf8 >;
use charnames   qw< :full >;
use feature     qw< unicode_strings >;

use Encode              qw< encode decode >;
use Unicode::Normalize  qw< NFD NFC >;

my ($x) = "mЖ丽";

open(U8,">:encoding(utf8)","/tmp/utf8-out");
print U8 $x;
close(U8);
open(U16,">:encoding(utf16)","/tmp/utf16-out");
print U16 $x;
close(U16);
system("od -t x1 /tmp/utf8-out");
my $u8 = encode("utf-8",$x);
print "utf-8: 0x".unpack("H*",$u8)."\n";

system("od -t x1 /tmp/utf16-out");
my $u16 = encode("utf-16",$x);
print "utf-16: 0x".unpack("H*",$u16)."\n";
相关问题