8

JNIを介してC++DLLを使用するJavaアプリケーションがあります。DLLのメソッドのいくつかは文字列引数を取り、それらのいくつかは文字列を含むオブジェクトも返します。

現在、DLLはUnicodeをサポートしていないため、文字列の処理はかなり簡単です。

  • JavaはString.getBytes()を呼び出し、結果の配列をDLLに渡します。DLLはデータをchar*として処理します。
  • DLLはNewStringUTF()を使用して、constchar*からjstringを作成します。

現在、UnicodeをサポートするようにDLLを変更し、TCHAR型(UNICODEが定義されている場合はWindowsのWCHARデータ型を使用)の使用に切り替えています。DLLの変更は順調に進んでいますが、コードのJNI部分を変更する方法がわかりません。

私が今考えることができる唯一のことはこれです:

  • JavaはString.getBytes(String charsetName)を呼び出し、結果の配列をDLLに渡します。DLLはデータをwchar_t*として扱います。
  • DLLは文字列を作成しなくなりましたが、代わりに生の文字列データとともにjbyteArraysを渡します。Javaは、String(byte [] bytes、String charsetName)コンストラクターを使用して、実際にStringを作成します。

このメソッドの唯一の問題は、使用する文字セット名がわからないことです。WCHARは2バイトの長さなので、UTF-16であると確信していますが、Java側には3つの可能性があります。UTF-16、UTF-16BE、およびUTF-16LE。バイトオーダーが何であるかを教えてくれるドキュメントは見つかりませんでしたが、いくつかの簡単なテストからおそらく理解できます。

もっと良い方法はありますか?可能であれば、DLL内でjstringオブジェクトを作成し続けたいと思います。そうすれば、これらのメソッドの使用法を変更する必要がなくなります。ただし、NewStringJNIメソッドは文字セット識別子を取りません。

4

2 に答える 2

7

この回答は、WCHARSのバイト順序が保証されていないことを示唆しています...

WindowsをWideCharToMultiByte使用しているため、WCHARをUTF-8に変換してから、既存のJNIコードを使用することができます。

パラメータでバッファオーバーランが発生する可能性があるため、WideCharToMultiByteの使用には注意が必要です。lpMultiByteStrこれを回避するには、関数を2回呼び出す必要があります。最初はゼロにlpMultiByteStr設定し、ゼロに設定します。これにより、書き込みを試行せずに、必要なバッファーの長さが返されます。長さが決まったら、必要なサイズのバッファーを割り当てて、関数を再度呼び出すことができます。NULLcbMultiBytelpMultiByteStr

コード例:

int utf8_length;

wchar_t* utf16 = ...;

utf8_length = WideCharToMultiByte(
  CP_UTF8,           // Convert to UTF-8
  0,                 // No special character conversions required 
                     // (UTF-16 and UTF-8 support the same characters)
  utf16,             // UTF-16 string to convert
  -1,                // utf16 is NULL terminated (if not, use length)
  NULL,              // Determining correct output buffer size
  0,                 // Determining correct output buffer size
  NULL,              // Must be NULL for CP_UTF8
  NULL);             // Must be NULL for CP_UTF8

if (utf8_length == 0) {
  // Error - call GetLastError for details
}

char* utf8 = ...; // Allocate space for UTF-8 string

utf8_length = WideCharToMultiByte(
  CP_UTF8,           // Convert to UTF-8
  0,                 // No special character conversions required 
                     // (UTF-16 and UTF-8 support the same characters)
  utf16,             // UTF-16 string to convert
  -1,                // utf16 is NULL terminated (if not, use length)
  utf8,              // UTF-8 output buffer
  utf8_length,       // UTF-8 output buffer size
  NULL,              // Must be NULL for CP_UTF8
  NULL);             // Must be NULL for CP_UTF8

if (utf8_length == 0) {
  // Error - call GetLastError for details
}
于 2009-05-15T19:35:54.617 に答える
2

バイト順マークに関するちょっとしたよくある質問を見つけました。また、そのFAQから:

UTF-16とUTF-32は、それぞれ2バイトと4バイトの長さのコードユニットを使用します。これらのUTFには、BE、LE、およびマークなしの3つのサブフレーバーがあります。BEフォームはビッグエンディアンバイトシリアル化(最上位バイトが最初)を使用し、LEフォームはリトルエンディアンバイトシリアル化(最下位バイトが最初)を使用し、マークされていないフォームはデフォルトでビッグエンディアンバイトシリアル化を使用しますが、バイトオーダーが含まれる場合があります使用される実際のバイトシリアル化を示すために、先頭にマークを付けます。

Java側では、UTF-16がこのBOMを見つけて、エンコーディングを適切に処理しようとすると思います。私たちは皆、どれほど危険な仮定があり得るかを知っています...

コメントのために編集:

MicrosoftはUTF16リトルエンディアンを使用しています。JavaUTF-16はBOMを解釈しようとします。BOMがない場合、デフォルトでUTF-16BEになります。BEおよびLEバリアントはBOMを無視します。

于 2009-05-15T19:37:08.623 に答える