MindでUnicodeの文字列を扱う方法（2024.01.14改訂）

# はじめに

　Mind Version 8 のパッケージの srclib\ 配下にはMindでUnicode文字を扱うためのソースファイルが格納されています。
　しかしマニュアルが非公開であったため、ここでの投稿をマニュアルとさせていただきます。
 
　　　注：以下の解説で言う UCS2 は現在では UTF-16 と
　　　　　言われることが多いようです。厳密には両者は違う
　　　　　のですがここでは同じものと理解してください。
　　　　　（2024.01.14追記）

　　　注：Mind 8 のパッケージは時期によって srclib\
　　　　　内部が空のままリリースしてしまったことがあり、
　　　　　応急措置としてMind掲示板のデータライブラリに
　　　　　アップしましたので、そのようなパッケージの方
　　　　　はお手数ですが以下からダウンロードをお願いし

  　　　　Mind会議室
  　　　　https://www.scripts-lab.co.jp/support/public/html/mind.cgi?mainframe

　　　　　→会議室：データライブラリ
　　　　　　30番「Mind8 srclibフォルダのアップデート(1/16日再度）」


## 対象読者

　Mind Version 8 をお使いのユーザさんでUnicode扱いなど高度なプログラムを組まれる方。

## 概要
　
　Mindに限らないことですが、プログラムでUnicodeの文字を扱う場合に注意することがあります。それは、データ交換用（あるいは通信用）のUnicodeとプログラム内部処理用のUnicodeの使い分けです。

　データ交換用としてはUTF-8が定番として使われています。HTMLファイルの文字コードもUnicodeと言えばこれになっているようです。
　一方で、プログラム内部処理としてUTF-8がそのまま使われるかと言うと、プログラム言語によってケース・バイ・ケースかと思いますが、Mindのプログラム内部ではネイティブな文字コード（SJISまたはEUC）に変換してしまうか、あるいは内部処理専用のUnicodeで扱うことを勧めます。

　次のような理由です。
　UTF-8は１文字を表すのに１バイト～３バイトの範囲で可変であり、プログラムではこのままの形態で文字列処理をすることが困難です。
　一方、UCS2（２バイト）などの固定長の形式は文字列処理が容易です。
　このようなことから、Mindで作成するプログラムは、その「外」では広く標準化されたUTF-8形式を用い、プログラム内部では文字列操作が容易なネイティブ文字コードにするか、あるいは内部もUnicodeで行くのであればUCS2を用いることを勧めます。

　DOSあるいはWindowsで使われてきたshift_jis、Unix系で使われてきたeuc-jpでは、１文字の長さが１バイトか２バイトのいずれかに決まっていて比較的単純のため、データ交換とプログラム内部処理で形式を使い分けることはなかったのですが、Unicodeでは前記の通り使い分けが生ずることがあり、この点プログラムを組む場合には十分留意する必要があります。
　たとえば「今回は文字コードはUnicodeにしましょう」といった場合、それだけではまだ情報不足で、「そのUnicodeの形式は何を使うのか」まで詰めておく必要があります。


■Unicode用の変換ライブラリ

　さて、Mind Version 8 にはUnicodeの文字列を扱うためのライブラリが
付属しています。ただ、ソースファイル形態でのライブラリなので、アプリ
ケーション用のソースプログラム内から「"～～.src"を　コンパイル」の
ようにインクルードして使う形となります。
　以下がこのライブラリのソースです。

```
　pmind/srclib/
　　　　unidecode.src    (Unicode→SJISまたはEUCへの変換)
　　　　uniencode.src    (SJISまたはEUC→Unicodeへの変換)
```

■Unicode用の変換ツール（実行可能ファイル）

　以下の２つのソースファイルはこのライブラリを使ったメインの付いた実行可能なツールです。UTF-8とSJISとの相互変換ツールが必要なことが多いため添付しています。

　pmind/srclib/

　（Windows用）
　　utf82sjis.src
　　sjis2utf8.src

　（Linux用）
　　utf82euc.src
　　euc2utf8.src

　上記はコンパイルしてから（fileリンクで）使ってください。
　使い方は単純起動するとUsageを表示してきます。


■Unicode用の変換テーブルファイル

　このほか、作成したアプリケーションの実行時には文字変換用のテーブルが必要になります。上記ソースと同じディレクトリに格納されています。

```
　pmind/srclib/

　（Windows向け）    (ファイルサイズは各128KB固定です)
　　　　sjis-uni-encode.bin     (SJIS→Unicode変換用のテーブル)
　　　　uni-sjis-decode.bin     (Unicode→SJIS変換用のテーブル)

　（Linux向け）    (ファイルサイズは各128KB固定です)
　　　　euc-uni-encode.bin      (EUC→Unicode変換用のテーブル)
　　　　uni-euc-decode.bin      (Unicode→EUC変換用のテーブル)
```

　作成したプログラムを配布する場合、そのパッケージに上記のテーブルファイルを必ず添付してください。（実行環境に対応した２ファイルだけで構いません）

　さらには、もしアプリケーションがデコードだけすれば良いのなら、

　　　*-decode.bin

の１ファイルのみ、エンコードだけすれば良いのなら

　　　*-encode.bin

の１ファイルのみの添付で構いません。


■アプリケーションのソースでインクルードする方法
　アプリケーションのソースコードの頭で以下のいずれか、あるいは両方を記述します。（パスは例です）

```
"../srclib/unidecode.src"を　コンパイル。
"../srclib/uniencode.src"を　コンパイル。

・・・・・・・・・・・
・・・・・・・・・・・
・・プログラム本体・・
・・・・・・・・・・・
・・・・・・・・・・・
```


■ライブラリの初期化（変換テーブルの読み込み）
　最初にエンコード用のテーブルとでコード用のテーブルをそれぞれロードします。
　(プログラムの処理が１方向の変換であれば片方だけでもかまいません)

```
（プログラム例）

　　※Native→Unicode変換用

　　　　Unicodeエンコードテーブルを読み出しし　偽？
　　　　　　　　ならば　重大エラー
                つぎに

　　※Unicode→Native変換用

　　　　Unicodeデコードテーブルを読み出しし　偽？
　　　　　　　　ならば　重大エラー
                つぎに
```


■外部から来たUTF-8をそのまま使う場合
　文字コード変換せず、UTF-8のままプログラム処理する方法も無くはありません。
　まず、文字列実体変数への「入れる」ですが、変数の長さが十分あって尻切れにならないことが分かっていれば代入はできます。
　変数を単に参照することと「等しい文字列」でUTF-8同士を比較することもできます。
　その他の文字列操作は、一見すると動作はしますが誤動作することがあるので注意します。たとえば「左端文字」「右端文字」「取り出し」「切り出し」などてす。（ネイティブコードだと思って漢字介入が行われるため）
　一方で、「一バイト検索」「バイト列切り出し」などのバイナリ処理用の文字列操作語は使えます。つまりバイナリデータだと思えば良いのです。

　うっかりしがちなのが、HTMLファイルの内容からタグの位置を見つけて何かするケースでたとえば

```
　'<'を　一文字検索し　位置に　入れ
　位置で　・・を　分断し　・・
```

のように書きたくなりますが誤動作します。「一文字検索」や「分断」ははネイティブコードを前提とした漢字コード介入が行われるからです。ここは、

```
　'<'を　一バイト検索し　位置に　入れ
　位置で　・・を　バイト列分断し　・・
```

のようにバイナリデータを扱う感覚で行えば大丈夫です。


■外部から来たUTF-8をネイティブコードに変換する
　ネイティブコードに変換してしまえば従来通りの文字列操作語が使え、プログラムは簡単になります。この方法のデメリットは多国語に対応できないことです。UTF-8データの中身が日本語と英語だけだとわかっているケースでのみ使えます。

　unidecode.src 内にある変換語を使ってネイティブコードに変換します。
　「ＵＴＦ８→ＥＵＣ変換」「ＵＴＦ８→ＳＪＩＳ変換」がありますが、
もしこれから作るプログラムをWindows／Liunxどちらでもソース修正せずに動作させたいなら、上記の等価語である「ＵＴＦ８→Native変換」を使うと良いでしょう。

　処理後の文字列を再び外部に出力するときにはこの逆の変換をおこないます。uniencode.src 内にある変換語を使います。
　「ＥＵＣ→ＵＴＦ８変換」「ＳＪＩＳ→ＵＴＦ８変換」がありますが、
もしこれから作るプログラムをWindows／Liunxどちらでもソース修正せずに動作させたいなら、上記の等価語である「Native→ＵＴＦ８変換」を使うと良いでしょう。


■外部から来たUTF-8をUCS2に変換する
　内部処理用のUnicodeの一つの形態であるUCS2に変換します。そのあとの文字列操作はすべてUnicode向けの処理単語を使う面倒さはありますが、多国語であっても対応できる大きなメリットがあります。
　内部コードへの変換は unidecode.src 内の「ＵＴＦ８→ＵＣＳ２変換」を使います。
　処理後の文字列を再び外部に出力するときには uniencode.src 内にある「ＵＣＳ２→ＵＴＦ８変換」を使います。

　UCS2の文字はすべて(ASCII相当語も含め)１６ビット長固定であることに注意します。
　たとえば、"abc" のUCS2データは、

```
  61 62 63
```

ではありません。UCS2では、

```
  0061 0062 0063
```

となります。（１６ビットのメモリ上でのバイト並びですが下位バイト、上位バイトの順になっています）

■ソースコード中へのUnicode文字記入

　Mindのソースコード内にUnicodeの文字列定数を書きたいことがあるかも知れません。コンパイラ自体には「日本語の文字列定数を」Unicode変換する機能はありませんが、ASCII文字をUnicodeの16bit長形式で生成させることはできます。以下のように記述します。（Windows API の呼び出しなどで暫定的に使えます）

（Littleエンディアン）（Windows API の呼び出しはこちら）

```
  "(uL+)abc"を　・・・し
```

上記は、

```
  0061 0062 0063
```

のような文字列定数として評価されます。バイト並びとしては、

```
  61 00 62 00 63 00
```

です。

（Bigエンディアン）

```
  "(uB+)abc"を　・・・し
```

上記もまた、

```
  0061 0062 0063
```

のような文字列定数として評価されますが、バイト並びとしては、

```
  00 61 00 62 00 63
```

となり、バイト並びに関して (uL+) と (uB+) は逆になります。

　　注：Windows API でワイド文字を扱う場合はLittle
  　　　　エンディアンです

　なおソースコード中へのUnicode文字の記入ですが、Windows APIなどに渡す場合にNULL終端してC、C++ に文字列を渡す必要があるものは以下のように書いてください。

```
  "(uL+)abc&00&"を　・・・し
```

　上記のように、&00& にてNULLの埋め込みができます。このとき、NULL文字もまた16bit長なので 00 00 の２バイトが埋め込まれます。

■UCS2形式の文字列操作をおこなう処理単語群

　UCS2形式になったUnicodeの文字列をMindで処理するには以下の単語を使います。（Ｕ系の文字列操作と呼んでいます）

```
　Ｕ入れ
　Ｕ一文字追加　Ｕ一文字左側追加
　Ｕ追加　Ｕ左側追加
　Ｕ一文字削除　Ｕ一文字右側削除
　Ｕ一文字切り出し　Ｕ一文字右側切り出し
　Ｕ切り出し　Ｕ右側切り出し
　Ｕ分断
　Ｕ削除　Ｕ右側削除
　Ｕ文字数
　Ｕ左端文字　Ｕ右端文字
　Ｕ指定位置の文字　Ｕ右側指定位置の文字
　Ｕ取り出し　Ｕ右側取り出し
　Ｕ一文字検索
　Ｕ検索
```

　上記の補足です。
　「Ｕ入れ」でなくて「入れ」でもちろん代入はできるのですが「Ｕ入れ」を使うことで実体変数の長さ以上の文字列を代入したようとしたときの末尾カットがきれいな場所でおこなわれます。
　参照は単に変数名を書くだけで良いのでＵ系単語はありません。


■変換単語一覧

　　　　　　　　　　[unidecode.src管轄]

　Unicodeデコードテーブルを読み出し
　（・　→　[ErrorMessage]、真偽）
　　　　
    成功したときは真のみが、失敗したときはエラーメッセージと
    偽が積まれてリターンします。スタック数がアンバランスなの
    で注意。

　ＵＴＦ８→ＵＣＳ２変換
　（文字列、［文字列実体情報］　→　文字列）

　一文字ＵＣＳ２→Native変換
　（文字　→　文字）
　　　　
　　　「一文字ＵＣＳ２→ＥＵＣ変換」または
　  　「一文字ＵＣＳ２→ＳＪＩＳ変換」でも可

　ＵＣＳ２→Native変換
　（文字列、［文字列実体情報］　→　文字列）
　　　「ＵＣＳ２→ＥＵＣ変換」または
    　「ＵＣＳ２→ＳＪＩＳ変換」でも可

　ＵＴＦ８→Native変換
　（文字列　→　文字列）
　　　「ＵＴＦ８→ＥＵＣ変換」または
　  　「ＵＴＦ８→ＳＪＩＳ変換」でも可

　　　　　　　　　　[uniencode.src管轄]

　Unicodeエンコードテーブルを読み出し
　（・　→　[ErrorMessage]、真偽）
　　　　成功したときは真のみが、失敗したときはエラーメッ
    　　セージと偽が積まれてリターンします。スタック数が
      　アンバランスなので注意。

　　一文字Native→ＵＣＳ２変換
　　（文字　→　文字）
　　　「一文字ＥＵＣ→ＵＣＳ２変換」または
　  　「一文字ＳＪＩＳ→ＵＣＳ２変換」でも可

　　Native→ＵＣＳ２変換
　　（文字列、［文字列実体情報］　→　文字列）
　　　　「ＥＵＣ→ＵＣＳ２変換」または
　  　　「ＳＪＩＳ→ＵＣＳ２変換」でも可

　　ＵＣＳ２→ＵＴＦ８変換
　　（文字列、［文字列実体情報］　→　文字列）

　　Native→ＵＴＦ８変換
　　（文字列、［文字列実体情報］　→　文字列）
　　　　「ＥＵＣ→ＵＴＦ８変換」「ＳＪＩＳ→ＵＴＦ８変換」でも可


■変換先の文字列実体変数を明記する方法
　たとえば「Native→ＵＴＦ８変換」のスタック仕様は以下のようになっています。

```
　　（文字列、［文字列実体情報］　→　文字列）

```

上記仕様の場合、以下のいずれかの方法で呼び出すことができます。

```
　（A）
　　　・・・を　Native→ＵＴＦ８変換し

　（B）
　　　UTF-8バッファは　文字列実体　長さ　４０９６。

　　　・・・を　UTF-8バッファをつかい　Native→ＵＴＦ８変換し
```

　上記(A)は変換先のバッファを明示していません。この場合、ライブラリ内部の実体変数「＿暗黙のEUC→UTF8バッファ」が使われ、そこを指す文字列情報が返されることになります。（バッファそのものはローカルシンボルのため外からは見えません）

　一方、(B)は変換先のバッファ（文字列実体変数）を明示する方法です。

　このことと関連しますが、UTF-8記述のHTMLファイルを一行読み出ししながらネイティブコードに変換する場合、ファイルからの一行読み出しのバッファ、あるいはUnicodeの変換バッファの長さに注意してください。まれに、改行コードを含まない非常に長い行がHTMLの中にあるケースがあるためです。
　
 Mind 8 の暗黙の読み出し行バッファは4KBです。また本ライブラリの暗黙の変数バッファは8KBです。より長い行が想定される場合は、以下のようにバッファ指定の読み出しや変換をおこなってください。

```
　（ファイルの読み出し）
　　　○○ファイルから　△△バッファをつかい　一行読み出し

　（変換）
　　　・・・・を　□□１バッファをつかい　ＵＴＦ８→ＵＣＳ２変換し
　　　（さらに）　□□２バッファをつかい　ＵＣＳ２→Native変換し
```

　ソースファイルの中にコメントでも書いていますが、「ＵＴＦ８→Native変換」は内部で２つの変換バッファを使っている関係で、この単語ををバッファ明示で呼び出すことができません。上記では２段に分けて変換することで変換バッファを明示しています。


※　以上　※