web-dev-qa-db-ja.com

U + xxxxxコードで指定された絵文字をutf-8に変換する方法は?

絵文字はU + xxxxxの形式を使用して指定されているようです
ここで、各xは16進数です。

たとえば、U + 1F615は、「混乱した顔」の 公式のUnicodeコンソーシアムコード です????

私はよく混乱するので、このシンボルには強い親和性があります。

U + 1F615の表現は私にとって混乱を招きます数字には5x4 = 20ビットが必要です。

私はこのシンボルがbashの完全に異なる16進数文字列で表されているように見えることを発見しました:

$echo -n ???? | hexdump
0000000 f0 9f 98 95                                    
0000004

$echo -e "\xf0\x9f\x98\x95"
????

$PS1=$'\xf0\x9f\x98\x95  >'
????  >

U + 1F615\ x00\x01\xF6\x15

これら2つのエンコーディングの関係がわかりませんか?

公式のUnicodeコンソーシアムのリスト でシンボルを検索するとき、手間のかかる方法で手動で変換することなく、そのコードを直接使用できるようにしたいと思います。つまり.

  • 一部のWebページでシンボルを見つける
  • それをWebブラウザーのクリップボードにコピーする
  • それをbashに貼り付けて、hexdumpを介してエコーし、REALコードを見つけます。

この20ビットコードを使用して、32ビットコードを特定できますか?

これら2つの数値の間に関係はありますか?

16
Alex Ryan

UTF-8は、Unicodeの可変長encodingです。 ASCIIのスーパーセットになるように設計されています。エンコーディングの詳細については Wikipedia を参照してください。 \x00 \x01 \xF6 \x15は、UCS-4BEまたはUTF-32BEエンコーディングになります。

UnicodeコードポイントからUTF-8エンコーディングを取得するには、ロケールのcharmapがUTF-8(locale charmapの出力を参照)であると想定すると、次のようになります。

$ printf '\U1F615\n'
????
$ echo -e '\U1F615'
????
$ confused_face=$'\U1F615'

後者 POSIX標準の次のバージョンに含まれる予定

AFAIK、その構文は2000年にスタンドアロンGNU printfユーティリティ(GNU Shell)のprintfユーティリティではなく)によって導入され、echoになりました/ printf/$'...' builtins first by zsh in 20 、ksh93 in 2004、bash in 2010(ただし 2014年まで適切に機能しない )ですが、明らかに刺激を受けました他の言語で。

ksh93は、printf '\x1f615\n'およびprintf '\u{1f615}\n'としてもサポートしています。

$'\uXXXX'および$'\UXXXXXXXX'は、zshbashksh93mkshおよびFreeBSD sh、GNU printf、GNU = echo

すべての桁が必要なものもあります(\U0001F615ではなく\U1F615のように)。ただし、POSIXではより少ない桁数が許可されるため、将来のバージョンで変更される可能性があります。いずれの場合でも、\UXXXXXXXX\U0001F615FOXであったため、\U1F615FOXの後に$'\U001F615F'OXのように16進数字が続く場合は、すべての数字が必要です。

文字列の解析時または文字列の展開時に現在のロケールのエンコーディングの文字に展開されるものもあれば、ロケールに関係なくUTF-8でのみ展開されるものもあります。文字が現在のロケールのエンコーディングで使用できない場合、動作はシェルによって異なります。

したがって、移植性を最大限に高めるには、UTF-8ロケールでのみ使用し、すべての数字を使用し、$'...'で使用するのが最適です。

printf '%s\n' $'\U0001F615'

ご了承ください:

LC_ALL=C.UTF-8; printf '%s\n' $'\U0001F615'

または:

{
  LC_ALL=C.UTF-8
  printf '%s\n' $'\U0001F615'
}

$'\U0001F615'が割り当てられる前にLC_ALL解析されるため、すべてのシェル(bashを含む)では機能しません。 (また、システムがC.UTF-8と呼ばれるロケールを持つことは保証されないことにも注意してください)

あなたは必要になるでしょう:

LC_ALL=C.UTF-8; eval "confused_face=$'\U0001F615'"

または:

LC_ALL=C.UTF-8
printf '%s\n' $'\U0001F615'

(複合コマンドまたは関数内ではありません)。


逆の場合は、UTF-8エンコーディングからUnicodeコードポイントに変換するために、 この他の質問 または その1つ を参照してください。

$ unicode ???? 
U+1F615 CONFUSED FACE
UTF-8: f0 9f 98 95  UTF-16BE: d83dde15  Decimal: 😕
????
Category: So (Symbol, Other)
Bidi: ON (Other Neutrals)

$ Perl -CA -le 'printf "%x\n", ord shift' ????
1f615
20

UTF-32(ビッグエンディアン)からUTF-8に変換する方法を次に示します

$ confused=$(echo -ne "\x0\x01\xF6\x15" | iconv -f UTF-32BE -t UTF-8)     
$ echo $confused 
????

16進数値0x01F615があり、先頭に0を追加して32ビットを埋めています。

TF-8 のWikipediaページは、UnicodeコードポイントからそのUTF-8表現への変換を非常に明確に説明しています。しかし、シェルスクリプトでそれを自分でやろうとするのは、最善の考えではないかもしれません。

TF-32 は固定幅であり、コードポイントとUTF-32表現の間の対応は簡単です-値は同じです。

7
Mat

頭の中や紙の上でそれを行うための良い方法:

  1. それが何バイトになるかを考えてください:U + 0080の下の値は1バイト、それ以外の場合U + 0800は2バイト、それ以外の場合はU + 10000は3バイト、それ以外は4バイトです。あなたの場合、4バイト。

  2. 16進数を8進数に変換します:0373025

  3. 最後から始めて、一度に2桁の8進数字を剥がして、一連の8進値を取得します:037030025

  4. 予想されるバイト数よりも少ない8進数値がある場合は、先頭に0を追加します:000037030025

  5. 1つ目以外の場合は、0200を追加して取得します:000023702300225

  6. 最初に、予想される長さが2の場合は0300を追加し、3の場合は0340を、4の場合は0360を追加してください:360023702300225

次に、8進エスケープの文字列として書き込みます:\360\237\230\225。必要に応じて、オプションで16進数に戻します。