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

发布于 2024-11-14 07:15:08 字数 317 浏览 3 评论 0原文

我即将进行大学编程考试,其中一个部分是关于 unicode 的。

我已经彻底检查了这个问题的答案,而我的讲师毫无用处,所以这没有帮助,所以这是你们可能提供帮助的最后手段。

问题是这样的:

字符串“mЖ丽”具有以下 unicode 代码点 U+006DU+0416U+4E3D,答案以十六进制编写,手动编码 将字符串转换为 UTF-8 和 UTF-16。

任何帮助都将不胜感激,因为我正在努力解决这个问题。

I have a university programming exam coming up, and one section is on unicode.

I have checked all over for answers to this, and my lecturer is useless so that’s no help, so this is a last resort for you guys to possibly help.

The question will be something like:

The string 'mЖ丽' has these unicode codepoints U+006D, U+0416 and
U+4E3D, with answers written in hexadecimal, manually encode the
string into UTF-8 and UTF-16.

Any help at all will be greatly appreciated as I am trying to get my head round this.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

oО清风挽发oО 2024-11-21 07:15:08

哇。一方面,我很高兴知道大学课程所教导的现实是字符编码是一项艰苦的工作,但实际上了解 UTF-8 编码规则听起来像是期望很高。 (它会帮助学生通过土耳其测试?)

到目前为止,我所看到的关于将 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

您可以通过记下可以用新表示中允许的位填充多少空间来导出范围:

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

0x4E3E is 100111000111110b。将这些位放入上面的 x 中(从右侧开始,我们将用 0 填充开头处缺失的位):

   1110x100 10111000 10111110

有一个 x 在开头留下的位置,用 0 填充:

   11100100 10111000 10111110

位转十六进制

   0xE4 0xB8 0xBE

Wow. On the one hand I'm thrilled to know that university courses are teaching to the reality that character encodings are hard work, but actually knowing the UTF-8 encoding rules sounds like expecting a lot. (Will it help students pass the Turkey test?)

The clearest description I've seen so far for the rules to encode UCS codepoints to UTF-8 are from the utf-8(7) manpage on many Linux systems:

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.

It might be easier to remember a 'compressed' version of the chart:

Initial bytes starts of mangled codepoints start with a 1, and add padding 1+0. Subsequent bytes start 10.

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

You can derive the ranges by taking note of how much space you can fill with the bits allowed in the new representation:

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

I know I could remember the rules to derive the chart easier than the chart itself. Here's hoping you're good at remembering rules too. :)

Update

Once you have built the chart above, you can convert input Unicode codepoints to UTF-8 by finding their range, converting from hexadecimal to binary, inserting the bits according to the rules above, then converting back to hex:

U+4E3E

This fits in the 0x00000800 - 0x0000FFFF range (0x4E3E < 0xFFFF), so the representation will be of the form:

   1110xxxx 10xxxxxx 10xxxxxx

0x4E3E is 100111000111110b. Drop the bits into the x above (start from the right, we'll fill in missing bits at the start with 0):

   1110x100 10111000 10111110

There is an x spot left over at the start, fill it in with 0:

   11100100 10111000 10111110

Convert from bits to hex:

   0xE4 0xB8 0xBE
溺ぐ爱和你が 2024-11-21 07:15:08

维基百科上对 UTF-8UTF-16 很好:

示例字符串的过程:

UTF-8

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

1 字节 UTF-8 = 0xxxxxxxbin = 7 位 = 0-7Fhex

2、3 和 4 字节 UTF-8 的初始字节以 2 开头、 3 或 4 个 1 位,后跟 0 位。后续字节始终以两位模式 10 开头,留下 6 位用于数据:

2 字节 UTF-8 = 110xxxxx 10xxxxxxbin = 5+6(11) 位 = 80-7FF十六进制
3 字节 UTF-8 = 1110xxxx 10xxxxxx 10xxxxxxbin = 4+6+6(16) 位 = 800-FFFFhex
4 字节 UTF-8 = 11110xxx 10xxxxxx 10xxxxxx 10xxxxxxbin = 3+6+6+6(21) 位 = 10000-10FFFFhex

超过 10FFFFhex 的 Unicode 代码点未定义。

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

U+006D = 1101101bin = 01101101bin = 6D十六进制
U+0416 = 10000 010110bin = 11010000 10010110bin = D0 96十六进制
U+4E3D = 0100 111000 111101bin = 11100100 10111000 10111101bin = E4 B8 BD十六进制

最终字节序列:

6D D0 96 E4 B8 BD

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

6D D0 96 E4 B8 BD 00

UTF-16

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

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

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

  1. 从代码点中减去 10000hex
  2. 将结果表示为 20 位二进制。
  3. 使用模式 110110xxxxxxxxxx 110111xxxxxxxxxxbin 将高 10 位和低 10 位编码为两个 16 位字。

使用您的代码点:

U+006D = 006D十六进制
U+0416 = 0416十六进制
U+4E3D = 4E3D十六进制

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

大端 = FE FF 00 6D 04 16 4E 3D
小端 = FF FE 6D 00 16 04 3D 4E

以 null 结尾,U+0000 = 0000hex

大端 = FE FF 00 6D 04 16 4E 3D 00 00
小端 = FF FE 6D 00 16 04 3D 4E 00 00

由于您的讲师没有给出需要 4 字节 UTF-16 的代码点,因此这里有一个示例:

U+1F031 = 1F031十六进制 - 10000十六进制 = F031十六进制 = 0000111100 0000110001bin =
1101100000111100 1101110000110001bin = D83C DC31十六进制

The descriptions on Wikipedia for UTF-8 and UTF-16 are good:

Procedures for your example string:

UTF-8

UTF-8 uses up to 4 bytes to represent Unicode codepoints. For the 1-byte case, use the following pattern:

1-byte UTF-8 = 0xxxxxxxbin = 7 bits = 0-7Fhex

The initial byte of 2-, 3- and 4-byte UTF-8 start with 2, 3 or 4 one bits, followed by a zero bit. Follow on bytes always start with the two-bit pattern 10, leaving 6 bits for data:

2-byte UTF-8 = 110xxxxx 10xxxxxxbin = 5+6(11) bits = 80-7FFhex
3-byte UTF-8 = 1110xxxx 10xxxxxx 10xxxxxxbin = 4+6+6(16) bits = 800-FFFFhex
4-byte UTF-8 = 11110xxx 10xxxxxx 10xxxxxx 10xxxxxxbin = 3+6+6+6(21) bits = 10000-10FFFFhex

Unicode codepoints are undefined beyond 10FFFFhex.

Your codepoints are U+006D, U+0416 and U+4E3D requiring 1-, 2- and 3-byte UTF-8 sequences, respectively. Convert to binary and assign the bits:

U+006D = 1101101bin = 01101101bin = 6Dhex
U+0416 = 10000 010110bin = 11010000 10010110bin = D0 96hex
U+4E3D = 0100 111000 111101bin = 11100100 10111000 10111101bin = E4 B8 BDhex

Final byte sequence:

6D D0 96 E4 B8 BD

or if nul-terminated strings are desired:

6D D0 96 E4 B8 BD 00

UTF-16

UTF-16 uses 2 or 4 bytes to represent Unicode codepoints. Algorithm:

U+0000 to U+D7FF uses 2-byte 0000hex to D7FFhex
U+D800 to U+DFFF are invalid codepoints reserved for 4-byte UTF-16
U+E000 to U+FFFF uses 2-byte E000hex to FFFFhex

U+10000 to U+10FFFF uses 4-byte UTF-16 encoded as follows:

  1. Subtract 10000hex from the codepoint.
  2. Express result as 20-bit binary.
  3. Use the pattern 110110xxxxxxxxxx 110111xxxxxxxxxxbin to encode the upper- and lower- 10 bits into two 16-bit words.

Using your codepoints:

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

Now, we have one more issue. Some machines store the two bytes of a 16-bit word least significant byte first (so-called little-endian machines) and some store most significant byte first (big-endian machines). UTF-16 uses the codepoint U+FEFF (called the byte order mark or BOM) to help a machine determine if a byte stream contains big- or little-endian UTF-16:

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

With nul-termination, U+0000 = 0000hex:

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

Since your instructor didn't give a codepoint that required 4-byte UTF-16, here's one example:

U+1F031 = 1F031hex - 10000hex = F031hex = 0000111100 0000110001bin =
1101100000111100 1101110000110001bin = D83C DC31hex

撩人痒 2024-11-21 07:15:08

以下程序将完成必要的工作。对于您的目的来说,它可能不够“手动”,但至少您可以检查您的工作。

#!/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";

The following program will do the necessary work. It may not be "manual" enough for your purposes, but at a minimum you can check your work.

#!/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";
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文