LoginSignup
4
3

MindでUnicodeの文字列を扱う方法(2024.01.14改訂)

Last updated at Posted at 2017-04-09

はじめに

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

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

■Unicode用の変換ライブラリ

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

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

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

 以下の2つのソースファイルはこのライブラリを使ったメインの付いた実行可能なツールです。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変換用のテーブル)

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

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

   *-decode.bin

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

   *-encode.bin

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

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

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

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

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

(プログラム例)

  ※Native→Unicode変換用

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

  ※Unicode→Native変換用

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

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

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

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

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

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

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

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

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

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

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

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

  61 62 63

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

  0061 0062 0063

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

■ソースコード中への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 の2バイトが埋め込まれます。

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

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

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

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

■変換単語一覧

          [unidecode.src管轄]

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

 UTF8→UCS2変換
 (文字列、[文字列実体情報] → 文字列)

 一文字UCS2→Native変換
 (文字 → 文字)
    
   「一文字UCS2→EUC変換」または
   「一文字UCS2→SJIS変換」でも可

 UCS2→Native変換
 (文字列、[文字列実体情報] → 文字列)
   「UCS2→EUC変換」または
 「UCS2→SJIS変換」でも可

 UTF8→Native変換
 (文字列 → 文字列)
   「UTF8→EUC変換」または
   「UTF8→SJIS変換」でも可

          [uniencode.src管轄]

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

  一文字Native→UCS2変換
  (文字 → 文字)
   「一文字EUC→UCS2変換」または
   「一文字SJIS→UCS2変換」でも可

  Native→UCS2変換
  (文字列、[文字列実体情報] → 文字列)
    「EUC→UCS2変換」または
    「SJIS→UCS2変換」でも可

  UCS2→UTF8変換
  (文字列、[文字列実体情報] → 文字列)

  Native→UTF8変換
  (文字列、[文字列実体情報] → 文字列)
    「EUC→UTF8変換」「SJIS→UTF8変換」でも可

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

  (文字列、[文字列実体情報] → 文字列)

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

 (A)
   ・・・を Native→UTF8変換し

 (B)
   UTF-8バッファは 文字列実体 長さ 4096。

   ・・・を UTF-8バッファをつかい Native→UTF8変換し

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

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

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

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

 (変換)
   ・・・・を □□1バッファをつかい UTF8→UCS2変換し
   (さらに) □□2バッファをつかい UCS2→Native変換し

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

※ 以上 ※

4
3
20

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3