web-dev-qa-db-ja.com

UnicodeコードポイントをUTF-8およびUTF-16に手動で変換する

大学でのプログラミング試験が予定されていますが、1つはユニコードに関するセクションです。

私はこれに対する答えを全面的にチェックしました、そして私の講師は役に立たないので、それは助けにはなりません。

質問は次のようになります。

文字列 'mЖ丽'にはこれらのUnicodeコードポイントがありますU+006DU+0416およびU+4E3Dは、16進数で記述された回答とともに、文字列をUTF-8およびUTF-16に手動でエンコードします。

私はこれを回避しようとしているので、どんな助けでも大歓迎です。

36
RSM

ワオ。一方では、大学のコースが文字エンコーディングが大変な仕事であるという現実を教えていることを知ってワクワクしていますが、実際には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

新しい表現で許可されているビットで埋めることができるspaceの量に注意することで、範囲を導出できます。

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

[〜#〜] i [〜#〜]は、チャート自体よりも簡単にチャートを導き出すためのルールを覚えていることを知っています。ルールを覚えるのが上手であることを願っています。 :)

更新

上記のチャートを作成したら、入力Unicodeコードポイントの範囲を見つけ、16進数からバイナリに変換し、上記の規則に従ってビットを挿入してから16進数に戻すことで、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
49
sarnold

TF-8 および TF-16 のウィキペディアの説明は適切です。

サンプル文字列の手順:

UTF-8

UTF-8は、Unicodeコードポイントを表すために最大4バイトを使用します。 1バイトの場合、次のパターンを使用します。

1バイトUTF-8 = 0xxxxxxx置き場 = 7ビット= 0-7Fhex

2、3、および4バイトUTF-8の初期バイトは、2、3、または4つの1ビットで始まり、その後にゼロビットが続きます。後続バイトは常に2ビットパターン10で始まり、データ用に6ビットを残します。

2バイトUTF-8 = 110xxxxx 10xxxxxx置き場 = 5 + 6(11)ビット= 80-7FFhex
3バイトUTF-8 = 1110xxxx 10xxxxxx 10xxxxxx置き場 = 4 + 6 + 6(16)ビット= 800-FFFFhex
4バイトUTF-8 = 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx置き場 = 3 + 6 + 6 + 6(21)ビット= 10000-10FFFFhex

Unicodeコードポイントは10FFFFを超えると未定義ですhex

コードポイントは、それぞれ1バイト、2バイト、3バイトのUTF-8シーケンスを必要とするU + 006D、U + 0416、U + 4E3Dです。バイナリに変換し、ビットを割り当てます。

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

最終バイトシーケンス:

6D D0 96 E4 B8 BD

またはヌル終端文字列が必要な場合:

6D D0 96 E4 B8 BD 00

UTF-16

UTF-16は、Unicodeコードポイントを表すために2バイトまたは4バイトを使用します。アルゴリズム:

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. 結果を20ビットバイナリとして表現します。
  3. パターン110110xxxxxxxxxx 110111xxxxxxxxxxを使用します置き場 上位10ビットと下位10ビットを2つの16ビットワードにエンコードします。

コードポイントの使用:

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

さて、もう1つ問題があります。 16ビットWordの2バイトを最下位バイトから先に格納するマシン(いわゆるリトルエンディアンマシン)と最上位バイトを先に格納するマシン(ビッグエンディアンマシン)があります。 UTF-16は、コードポイントU + FEFF(バイトオーダーマークまたはBOMと呼ばれる)を使用して、バイトストリームにビッグエンディアンまたはリトルエンディアンのUTF-16が含まれているかどうかをマシンが判断できるようにします。

ビッグエンディアン= FE FF 00 6D 04 16 4E 3D
リトルエンディアン= FF FE 6D 00 16 04 3D 4E

ヌル終端の場合、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 = 1F031hex - 10000hex = F031hex = 0000111100 0000110001置き場 =
1101100000111100 1101110000110001置き場 = D83C DC31hex

39
Mark Tolonen

次のプログラムが必要な作業を行います。目的に合った「マニュアル」ではないかもしれませんが、少なくとも作業内容を確認できます。

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