Notes C API探訪: NLS_translate(関数)(その1)
Notes/Dominoで使用されている文字列はLMBCS(Lotus Multi-Bytes Character Set)でフォーマットされています。それをC++プログラムで使用する場合、ShiftJISのようなマルチバイト文字セット(std::string)か、UTF16のようなワイド文字セット(std::wstring)で扱えるようにします。その相互変換を可能にするのが、OSTranslate関数もしくはNLS_translate関数です。
OSTranslate
OSTranslate関数はLMBCSと指定文字セットとの間で文字列の変換をします。
#include <osmisc.h>
/* Character Set Translation Routines */
WORD LNPUBLIC OSTranslate(
WORD TranslateMode,
const char far *In,
WORD InLength,
char far *Out,
WORD OutLength);
TranslateModeは、後述するモード定数を指定します。
Inは、変換元の文字列です。
InLengthは、変換元文字列のバイト長です。
Outは、変換先文字列の格納先メモリです。
OutLengthは、変換した文字列のバイト長を格納する変数へのポインタです。
#include <osmisc.h>
/* Charsets used with OSTranslate */
#define OS_TRANSLATE_NATIVE_TO_LMBCS 0 /* Translate platform-specific to LMBCS */
#define OS_TRANSLATE_LMBCS_TO_NATIVE 1 /* Translate LMBCS to platform-specific */
#define OS_TRANSLATE_LOWER_TO_UPPER 3 /* current int'l case table */
#define OS_TRANSLATE_UPPER_TO_LOWER 4 /* current int'l case table */
#define OS_TRANSLATE_UNACCENT 5 /* int'l unaccenting table */
#ifdef DOS
#define OS_TRANSLATE_OSNATIVE_TO_LMBCS 7 /* Used in DOS (codepage) */
#define OS_TRANSLATE_LMBCS_TO_OSNATIVE 8 /* Used in DOS */
#elif defined (OS2)
#define OS_TRANSLATE_OSNATIVE_TO_LMBCS OS_TRANSLATE_NATIVE_TO_LMBCS
#define OS_TRANSLATE_LMBCS_TO_OSNATIVE OS_TRANSLATE_LMBCS_TO_NATIVE
#else
#define OS_TRANSLATE_OSNATIVE_TO_LMBCS OS_TRANSLATE_NATIVE_TO_LMBCS
#define OS_TRANSLATE_LMBCS_TO_OSNATIVE OS_TRANSLATE_LMBCS_TO_NATIVE
#endif
#if defined(DOS) || defined(OS2)
#define OS_TRANSLATE_LMBCS_TO_ASCII 13
#else
#define OS_TRANSLATE_LMBCS_TO_ASCII 11
#endif
#define OS_TRANSLATE_LMBCS_TO_UNICODE 20
#define OS_TRANSLATE_LMBCS_TO_UTF8 22
#define OS_TRANSLATE_UNICODE_TO_LMBCS 23
#define OS_TRANSLATE_UTF8_TO_LMBCS 24
変換できる文字セットはLMBCSとプラットフォームネイティブ、ASCII、UNICODE(UTF-16)、UTF-8です。ご覧のように変換元、または変換先のいずれかに必ずLMBCSがあるので、例えばUTF-16とUTF-8の間で変換する場合でも一度LMBCSにする必要があります。
NLS_translate
NLS_translate関数も、OSTranslate関数と同じように文字セット間で文字列を変換します。
#include <nls.h>
NLS_STATUS LNPUBLIC NLS_translate (
BYTE far * pString,
WORD Len,
BYTE far * pStringTarget,
WORD far * pSize,
WORD ControlFlags,
NLS_PINFO pInfo);
pStringは、変換元の文字列です。
Lenは、その文字列のバイト長です。
pStringTargetは、変換した文字列を格納するメモリ領域です。
pSizeは、そのバイト長を格納する変数へのポインタを指定します。
ControlFlagsは、変換の組合せやNULLの扱い方などビットの組合せによるフラグを指定します(後述)。
pInfoは、文字セット情報を指定します。
個人的な意見ですが、これが変換元の文字セットなのか、変換先の文字セットなのかはリファレンスだけでは判別が付かないのではと思っています。元と先はControlFlagsで決まってしまうので、pInfoの存在理由がわかりません。経験上、変換先を指定していますが。
#include <nls.h>
/********************************************************************
** FLAGS
*/
/* NLS_translate */
#define NLS_NONULLTERMINATE 0x1
#define NLS_NULLTERMINATE 0x2
#define NLS_STRIPUNKNOWN 0x4
#define NLS_TARGETISLMBCS 0x8
#define NLS_SOURCEISLMBCS 0x10
#define NLS_TARGETISUNICODE 0x20
#define NLS_SOURCEISUNICODE 0x40
#define NLS_TARGETISPLATFORM 0x80
#define NLS_SOURCEISPLATFORM 0x100
ControlFlagsで設定するビットスイッチは、上記NLS_translate専用定数で指定します。この中には、「NLS_SOURCEISxxx」で始まる変換元文字セットと「NLS_TARGETISxxx」で始まる変換先文字セットがあり、「xxx」にはそれぞれLMBCS、UNICODE、PLATFORMの3種類が選択可能で、結果的に6つの変換パターンが設定できます。
OSTranslateとの違いは、OSTranslateは変換元と変換先のどちらかが必ずLMBCSになるのに対し、NLS_translateではLMBCSを経由せずに変換する方法を選択できる点にあります。一方で、NLS_translateには大文字-小文字間やUTF-8との変換などはできません。
変換先文字列を格納するバイト数の計算
他の言語はわかりませんが、日本語に限っていえば、LMBCSはASCII文字は変換する必要がなく、全角文字は0x10+シフトJISコード、半角カナは0x10+0x10+半角カナ文字コードとなります。これを元に計算すると、LMBCSからUTF-16に変換すると、バイト長は2/3〜2倍、UTF-16からLMBCSに変換すると0.5〜1.5倍になることがわかります。
ヘッダーファイルには、LMBCSとの変換の際に想定される、最大レートが定義されています。
#include <nls.h>
#define NLS_MAXRATIO_XLATE_FROM_LMBCS 4
#define NLS_MAXRATIO_XLATE_TO_LMBCS 3
これにより、LMBCSからの変換では4倍になる文字セットが、LMBCSへの変換では3倍になる文字セットが想定されていることがわかります。
なお、この想定レートは、OSTranslate、NLS_translate双方で利用可能です。
UNICODE(UTF-16)をC++側のデフォルトにする
プログラムや用途によって、どの文字セットとLMBCSを相互変換するか、いわゆる「C++側のデフォルト文字セット」を決めると思います。
では、汎用的なライブラリを構築する立場に立ってみた場合、どの文字セットを選択するのが適切なのでしょうか。私個人の考えとしては、Unicode(UTF-16) が適当なのではないかと考えています。
最大の理由は、ワイド文字セットである点です。
ワイド文字セットの利点は、1文字当たり一律2バイトであることです。これは文字数計算などを単純計算で済むようにしてくれます。また、C++言語においては、wchar_t、std::wstringで扱えるようになります。またOSとしてもUnicode対応が標準となりつつあり、マルチプラットフォームで利用可能です。
OS、プラットフォーム間での移植を気にしなくていいのなら、ネイティブの文字セットでもいいですが、Notes/Dominoでプログラムを開発するならば、いつでも別OSへの移植をできるように、Unicodeを標準としておくことは、決して損にならないと思います。
まとめ
Notesデータベースに収まっているLMBCS文字列を取得したり、プログラム側からNotes/DominoにLMBCS文字列を書き込みにいくことは、十中八九避けて通れないでしょう。早い段階でLMBCS相互変換ロジックを確立させて、後々楽にプログラムできるように、変換関数を使いこなせるようにしておきましょう。