UNICODEとエンディアンと仕様を整理する。

# ひげを剃る。そしてインド人を右に。
エンディアンはインディアンではない。。。

# 概要

https://qiita.com/heeroo_ymsw/items/c6e15d5f9246b4e842eb


こちらの記事見ていて、少し脱線はするが、。UNICODEの自動判定に疑問を持った。仕様で定義されており、自動判別する必要は無いのではないか、と。

``UNICODE`` 系エンコードの中には ``UTF-8``、 ``UTF-16``、 ``UTF-32``、 といった ``エンディアン`` 情報を表記に含んでいないものがある。
それらははたして ``ビッグ エンディアン`` ？ ``リトル エンディアン`` ？ ~~それとも ``マスター アジア`` ？~~

※ ``UTF-8`` にはエンディアン問題が無いため、以下にはほぼ登場しません。

# 結論
色々と実装依存なものの。
**UNICODEの仕様では、デフォルトは ``ビッグ エンディアン`` と明記されている。**

私は定義されていると思っていたものの、``リトル エンディアン`` のMSはてっきり仕様に則っているものだとばかり。。。orz=3。おのれ、まいくろそふとー・・・・。

## 補足
ただし、時代的背景を加味すると。いまでこそPCサーバーが当たり前だのクラッカーだけど。少し前までは ``ホスト`` だの ``メインフレーム`` だのでサーバーで用いられるアーキテクチャが異なっており

* **サーバーでは ``ビッグ エンディアン``**
* **PCでは ``リトル エンディアン``**

と言う形で二分化していたところもある様子には、まぁ解らないでもない。 MacintoshもCPUが ``Motorola系`` や ``PowerPC`` などで ``ビッグ エンディアン`` がメインのみたいだった様だけど、普及率的にもソフト開発が盛んではなかったろうし、あまり意識することは多くなかったろうしね。

また、元々計算機は文字とCPUが密接に関係して進化してきた。

なので、PCは ``Wintel`` によって ``リトルエンディアン`` で長らく進行していたため、内部UNICODEも``リトル エンディアン`` なのはあまり不自然な話でも無いだろう。デスクトップPC筆頭Wintel系とサーバー UNIX系とで、方針は分かれるべくして分かれたと考えることも出来る可能。

https://ja.wikipedia.org/wiki/%E3%83%90%E3%82%A4%E3%83%88_(%E6%83%85%E5%A0%B1)

> 本来バイトとは、8ビット固定ではなく、扱う文字種や、あるいはワードサイズをいくつかに分割することによって決められる、1文字を表現する単位で、古くは直接的に「字」とか「キャラクタ」とも呼ばれていた単位に由来するものである（→「キャラクタマシン」および「ワードマシン」を参照）。歴史的には5ビットから12ビットまで存在したと言われる。DEC PDP-10、NEC ACOS-6など、初期のコンピュータの多くでは6ビット、また7ビットや9ビットとして扱うコンピュータもあった。


# 比較
## リトル エンディアン (非準拠陣営)
恐らくIntel CPUが ``リトル エンディアン`` だったこともあり、Wintel系では ``リトル エンディアン`` で進んだ様な印象がある。

### Windows
* Unicode のサポート | Microsoft Docs  
  https://docs.microsoft.com/ja-jp/cpp/text/support-for-unicode?view=msvc-160

  >  16LE (リトルエンディアン用) を使用してエンコードされたワイド文字は、Windows のネイティブ文字形式です。

### .NET (CLR) 

※ 内部文字コードは ``UTF-16`` ではなく ``UCS2`` です。で、 ``リトル エンディアン`` です。

貴様味噌ですが。

https://qiita.com/libraplanet/items/d596da9d3548a37100ee

```text:(出力結果抜粋)
Encoding.GetEncoding("utf-16") => UnicodeEncoding, 1200, 1200, FF FE (2), Unicode, utf-16, utf-16, utf-16
Encoding.GetEncoding("utf-16be") => UnicodeEncoding, 1201, 1200, FE FF (2), Unicode (Big-Endian), utf-16BE, utf-16BE, utf-16BE
Encoding.GetEncoding("utf-16le") => UnicodeEncoding, 1200, 1200, FF FE (2), Unicode, utf-16, utf-16, utf-16
Encoding.GetEncoding("utf-32") => UTF32Encoding, 12000, 1200, FF FE 00 00 (4), Unicode (UTF-32), utf-32, utf-32, utf-32
Encoding.GetEncoding("utf-32be") => UTF32Encoding, 12001, 1200, 00 00 FE FF (4), Unicode (UTF-32 ビッグ エンディアン), utf-32BE, utf-32BE, utf-32BE
Encoding.GetEncoding("utf-32le") => UTF32Encoding, 12000, 1200, FF FE 00 00 (4), Unicode (UTF-32), utf-32, utf-32, utf-32
```

``UTF-16LE`` の実態が ``UTF-16`` っぽい・・・
Javaの ``UTF-16`` 、 ``UTF-32`` とは エンディアンが逆なので注意が必要。

* JavaとC＃のUTF-16エンコーディング - Javaer101  
  https://www.javaer101.com/ja/article/4296274.html

  > Javaでは、getBytes("UTF-16")オプションのバイト順マーク付きのビッグエンディアン表現を返します。C＃はSystem.Text.Encoding.Unicode.GetBytesリトルエンディアン表現を返します。ここからコードを確認することはできませんが、変換を正確に指定する必要があると思います。


### notepad
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72257/c667fbb3-846d-0e26-0799-5dfefcd98f11.png)

|名称|BOM|エンディアン|
|:--|:--|:--|
|Unicode|あり|リトルエンディアン |
|Unicode big endian|あり|ビッグエンディアン|
|UTF-8|あり|n/a|
``Unicode`` ≒ ``UTF-16`` で ``リトル エンディアン`` だとか。
と言うかさ。 ``Unicode`` は ``文字コード (文字集合)`` だけど ``エンコード`` じゃないんだから、曖昧な表現は、いい加減やめてほしい。。。
※ただし、BOMがついているので悪くなくもない

### iconv
何故かiconvはこっち。
x86_64 (Hyper-v上のUbuntu 20.04)と、ARM (Raspberry Pi3 + CentOS 7.8-2003) で同じ結果。

```sh:console
$ echo hoge | iconv -t UTF-16 | od -tx1
0000000 ff fe 68 00 6f 00 67 00 65 00 0a 00
0000014

$ echo hoge | iconv -t UTF-16LE | od -tx1
0000000 68 00 6f 00 67 00 65 00 0a 00
0000012

$ echo hoge | iconv -t UTF-16BE | od -tx1
0000000 00 68 00 6f 00 67 00 65 00 0a
0000012

$ echo hoge | od -tx1
0000000 68 6f 67 65 0a
0000005

$ echo hoge | iconv -t UTF-8 | od -tx1
0000000 68 6f 67 65 0a
0000005
```
### (おまけ) Intel系 CPU
(引用なし)


## ビッグ エンディアン (準拠陣営)
ラインナップを見ていると、サーバー系のプラットフォームでは ``ビッグ エンディアン`` が台頭していた印象がある。

### UNICODE 12.0.0 (仕様)
そもそも仕様で定義されているんだから、頑張ってそれに合わせる必要が、変革する必要があるよなぁ。。。。

* UTF-16 - Wikipedia #符号化  
  https://ja.wikipedia.org/wiki/UTF-16#%E7%AC%A6%E5%8F%B7%E5%8C%96

  > UTF-16の場合はBOMでエンディアンを明示するか、上層のプロトコルで指定されておらずBOMも付与しない場合はビッグエンディアンにするよう決められている[1]。


* The Unicode Standard Version 12.0  
  https://www.unicode.org/versions/Unicode12.0.0/ch03.pdf

  > The UTF-16 encoding scheme may or may not begin with a BOM. However,
when there is no BOM, and in the absence of a higher-level protocol, the byte order of the UTF-16 encoding scheme is big-endian.

* UTF-16 - Wikipedia #利用  
  https://ja.wikipedia.org/wiki/UTF-16#%E5%88%A9%E7%94%A8

  > UTF-16符号化フォームは、WindowsやJava（J2SE 5.0以上）で、内部表現に使われている。Windowsの内部表現では16ビット符号なし整数を符号単位とするUTF-16符号化フォームとして扱い、ファイルなどではBOMありのUTF-16符号化スキーム（リトルエンディアン）が主である。

### RFC 2781 (仕様)


* Unicode#文字符号化方式 - Wikipedia  
  https://ja.wikipedia.org/wiki/Unicode#%E6%96%87%E5%AD%97%E7%AC%A6%E5%8F%B7%E5%8C%96%E6%96%B9%E5%BC%8F

  > RFC 2781 ではBOMが付いていないUTF-16文書はビッグエンディアンとして解釈することになっている。Windowsのメモ帳で作成した「Unicodeテキスト」はBOMが付与されるようになっている。ビッグエンディアンの符号化方式をUTF-16BE、リトルエンディアンの符号化方式をUTF-16LEとして区別することもある

* RFC 2781 - UTF-16, an encoding of ISO 10646  
  https://tools.ietf.org/html/rfc2781

> 4.3 Interpreting text labelled as UTF-16
> 
> Text labelled with the "UTF-16" charset might be serialized in either big-endian or little-endian order. If the first two octets of the text is 0xFE followed by 0xFF, then the text can be interpreted as being big-endian. If the first two octets of the text is 0xFF followed by 0xFE, then the text can be interpreted as being little-endian. If the first two octets of the text is not 0xFE followed by 0xFF, and is not 0xFF followed by 0xFE, then the text SHOULD be interpreted as being big-endian.


* RFC 2781 - UTF-16, an encoding of ISO 10646 日本語訳  
  https://tex2e.github.io/rfc-translater/html/rfc2781.html

  > 4.3解釈テキストはUTF-16としてラベル
  > 「UTF-16」の文字セットで標識したテキストは、ビッグエンディアンかリトルエンディアン順のいずれかでシリアル化される可能性があります。テキストの最初の2つのオクテットが0xFEのが0xFFが続いている場合は、テキストはビッグエンディアンであると解釈することができます。テキストの最初の2つのオクテットが0xFFが0xFEのが続いている場合は、テキストはリトルエンディアンであると解釈することができます。テキストの最初の2つのオクテットが0xFEのが0xFFが続かない、とは0xFFが0xFEのが続いていない場合、テキストはビッグエンディアンであるとして解釈されるべきです。


### Java VM
※ 内部文字コードは ``UTF-16`` ではなく ``UCS2`` です。で、 ``ビッグ エンディアン`` です。

* Charset (Java Platform SE 7 )  
　https://docs.oracle.com/javase/jp/7/api/java/nio/charset/Charset.html

  > デコードの際、UTF-16 文字セットは入力ストリームの最初のバイト順マークを解釈してストリームのバイト順を決定するが、バイト順マークがない場合はビッグエンディアンバイト順を使用する。エンコードの際は、ビッグエンディアンバイト順を使用し、ビッグエンディアンバイト順マークを書き込む。

多分、Javaが ``ビッグ エンディアン`` なのは ``SPARC`` に関係してる気がする。。。

### nkf
* Ubuntu Manpage: nkf - ネットワーク用漢字コード変換フィルタ  
  http://manpages.ubuntu.com/manpages/xenial/ja/man1/nkf.1.html

```text:option&#x20;(抜粋)
-w16 -w16B0
UTF16 コードを出力する。 (Big Endian / BOM 無し)
```


### TCP/IP
・UTF-16 - Wikipedia #利用
  https://ja.wikipedia.org/wiki/UTF-16
  > UTF-16符号化フォームは、WindowsやJava（J2SE 5.0以上）で、内部表現に使われている。Windowsの内部表現では16ビット符号なし整数を符号単位とするUTF-16符号化フォームとして扱い、ファイルなどではBOMありのUTF-16符号化スキーム（リトルエンディアン）が主である。
>
> TCP/IPネットワークでは、プロトコルヘッダやMIME等の手段で文字符号化スキームを指定しない場合はビッグエンディアンに決められている。

・Windows ソケット : バイトの順序付け | Microsoft Docs
  https://docs.microsoft.com/ja-jp/cpp/mfc/windows-sockets-byte-ordering?view=msvc-160
> 異なるコンピューターアーキテクチャでは、異なるバイトオーダーを使用してデータを格納する場合があります。 たとえば、Intel ベースのコンピューターでは、Macintosh (Motorola) コンピューターの逆の順序でデータが保存されます。 "リトルエンディアン" と呼ばれる Intel のバイトオーダーも、ネットワークの標準の "ビッグエンディアン" の順序と逆になります。

### (おまけ) Motorola系 CPU
* ホストバイトオーダー ‐ 通信用語の基礎知識  
  https://www.wdic.org/w/TECH/%E3%83%9B%E3%82%B9%E3%83%88%E3%83%90%E3%82%A4%E3%83%88%E3%82%AA%E3%83%BC%E3%83%80%E3%83%BC

  > 680×0、PowerPCやSuperHなど多くは、最上位バイトが先に配置されるビッグエンディアンである。

### (おまけ) PowerPC
* ホストバイトオーダー ‐ 通信用語の基礎知識  
  https://www.wdic.org/w/TECH/%E3%83%9B%E3%82%B9%E3%83%88%E3%83%90%E3%82%A4%E3%83%88%E3%82%AA%E3%83%BC%E3%83%80%E3%83%BC

  > 680×0、PowerPCやSuperHなど多くは、最上位バイトが先に配置されるビッグエンディアンである。

PowerPC お前もか。。。

### (おまけ) SuperH
* ホストバイトオーダー ‐ 通信用語の基礎知識  
  https://www.wdic.org/w/TECH/%E3%83%9B%E3%82%B9%E3%83%88%E3%83%90%E3%82%A4%E3%83%88%E3%82%AA%E3%83%BC%E3%83%80%E3%83%BC

  > 680×0、PowerPCやSuperHなど多くは、最上位バイトが先に配置されるビッグエンディアンである。


### (おまけ) SPARC
* SPARC#歴史の概要 - Wikipedia  
  https://ja.wikipedia.org/wiki/SPARC#%E6%AD%B4%E5%8F%B2%E3%81%AE%E6%A6%82%E8%A6%81

  > SPARCは、完全ビッグエンディアンのRISCマイクロプロセッサ命令セットアーキテクチャで...


# 気になりごと
## ① 同じLinuxでもiconvとnkfで結果が異なる。

```sh:console
$ echo hoge | iconv -t UTF-16 | od -tx1
0000000 ff fe 68 00 6f 00 67 00 65 00 0a 00
0000014

# -w16 == UTF16
$ echo hoge | nkf -w16 | od -tx1
0000000 fe ff 00 68 00 6f 00 67 00 65 00 0a
0000014

```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72257/6c443540-15b0-8d77-3bb1-22bfed1d03f0.png)

なんでiconvは ``リトル エンディアン`` なんだろう。:thinking:

# 結局つまりはどういうこと？
* ``UTF-16``、 ``UTF-32`` と言うエンディアン表記のない場合には、 ``ビッグ エンディアン`` でも ``リトル エンディアン`` でも、どっちでも問題はない。
* しかし、BOMや上流のプロトコルや仕様などで決まっていない場合、デフォルトでは ``ビッグ エンディアン`` と仕様で定めている。
* ってことは。``エンディアン`` でのトラブルっていうのは ``ソフトウェア`` のせいではなく、大抵が **人間同士の疎通ミス** が原因。
  * **「なんでエンディアン情報を交換してないんだよ！」** **「なんで仕様を確認してないんだよ！」** って言う話 。
  * ``iconv`` の場合は ``リトル エンディアン`` を名言する仕様が見当たらないので、``iconv`` の不具合と言えなくもないかも。
* イメージつかない人は **``HTTP (80)``** とか **``HTTPS (443)``** とかでイメージすると解りやすいかも。  
(明記して自由にしていいけど、暗黙のデフォルトの解釈も仕様で決められているよね。)

# 明日の未来のために
you get big-endianness!

# 参考 (謝辞)
* バイト (情報) - Wikipedia
  https://ja.wikipedia.org/wiki/%E3%83%90%E3%82%A4%E3%83%88_(%E6%83%85%E5%A0%B1)

* Unicode のサポート | Microsoft Docs
  https://docs.microsoft.com/ja-jp/cpp/text/support-for-unicode?view=msvc-160

* UTF-16 - Wikipedia
  https://ja.wikipedia.org/wiki/UTF-16

* The Unicode Standard Version 12.0
  https://www.unicode.org/versions/Unicode12.0.0/ch03.pdf

* Windows ソケット : バイトの順序付け | Microsoft Docs
  https://docs.microsoft.com/ja-jp/cpp/mfc/windows-sockets-byte-ordering?view=msvc-160

* JavaとC＃のUTF-16エンコーディング - Javaer101
  https://www.javaer101.com/ja/article/4296274.html

* iconv_unicode - マニュアルページ セクション 7: 標準、環境、マクロ、文字セット、その他
  https://docs.oracle.com/cd/E75431_01/html/E73433/iconv-unicode-7.html

* [Linux] 文字コードを変換する
  https://blog.katsubemakito.net/linux/convert-character-encoding

* SPARC - Wikipedia
  https://ja.wikipedia.org/wiki/SPARC