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


16

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

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

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

U + 1F615 Iは、Unicode文字のための唯一のエンコーディングが可能と思ったので表現は私に混乱して5進数字は、5x4 = 20ビットを必要とするのに対し、8、16、24または32ビットを必要としました。

このシンボルは、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 Consortiumリストでシンボルを検索するとき、この退屈な方法で手動で変換することなく、そのコードを直接使用できるようにしたいと思います。すなわち

  • いくつかのWebページでシンボルを見つける
  • Webブラウザーのクリップボードにコピーする
  • bashに貼り付けて16進ダンプをエコーし​​、REALコードを検出します。

この20ビットコードを使用して、32ビットコードが何であるかを判断できますか?

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

回答:


20

UTF-8Unicodeの可変長エンコーディングです。ASCIIのスーパーセットになるように設計されています。参照してくださいウィキペディアの符号化の詳細のために。\x00 \x01 \xF6 \x15だろうUCS-4BEか、UTF-32BEエンコードします。

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

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

後者は、POSIX標準の次のバージョンに含まれます。

AFAIK、その構文は、スタンドアロンのGNU printfユーティリティ(printfGNUシェルのユーティリティとは対照的に)によって2000年に導入さ、2003年に最初にecho/ printf/ $'...'builtinsに、2004 にksh93に、2010年にbashに(適切に動作してませんが) 2014年まで)が、明らかに他の言語に触発されました。zsh

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

$'\uXXXX'そして、$'\UXXXXXXXX'によってサポートされているzshbashksh93mkshとFreeBSD sh、GNU printf、GNU echo

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

文字列の解析時または展開時に、現在のロケールのエンコーディングの文字に展開されるものもあります。ロケールに関係なく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'
}

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

必要なもの:

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

または:

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

(複合コマンドまたは関数内ではない)。


逆に、UTF-8エンコーディングからUnicodeコードポイントに到達するには、この別の質問またはその質問を参照してください。

$ 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

2
\U1F615別の有効な16進数が続く場合は、エスケープシーケンスの一部と見なされることに注意してください。:それは関係なく、それはそれに続いているものの動作させるためには、長い間、正確に8桁の数字であることを十分に先行ゼロを持たなければならない\U0001F615
kasperd

@kasperd、ありがとう。はい、注目に値します。それを答えに含めました。
ステファンシャゼル

7

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

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

0x01F615そこに16進値があり、32ビットを埋めるために追加の先行0が埋め込まれていることがわかります。

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

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


6

あなたの頭の中や紙の上でそれを行う素敵な方法:

  1. それが何バイトになるかを計算します。U+ 0080の下の値は1バイト、U + 0800の下の値は2バイト、U + 10000の下の値は3バイト、4バイトです。あなたの場合、4バイト。

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

  3. 最後から始めて、8進数のシーケンスを取得するために、一度に2つの8進数を剥ぎ取ります037 030 025

  4. 予想されるバイト数よりも8進数の値が少ない場合は、先頭に余分な0を追加します000 037 030 025

  5. 最初を除いて、追加し0200てget:を取得します000 0237 0230 0225

  6. 最初に、0300予想される長さが2の0340場合、3の0360場合、または4の場合、追加します360 0237 0230 0225

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.