2

日本語とラテン文字が混在するマルチバイト文字列があります。この文字列の一部を別のメモリ位置にコピーしようとしています。マルチバイト文字列であるため、一部の文字は1バイトを使用し、他の文字は2バイトを使用します。文字列の一部をコピーするときは、「半分」の日本語の文字をコピーしてはいけません。これを適切に実行できるようにするには、マルチバイト文字列の文字の開始位置と終了位置を判別できる必要があります。

たとえば、文字列に[2バイト][2バイト][1バイト]を必要とする3文字が含まれている場合、3ではなく2、4、または5バイトを他の場所にコピーする必要があります。 2番目の文字の半分だけをコピーします。

マルチバイト文字列の文字の開始位置と終了位置を把握するために、Windows API関数のCharNextとCharNextExAを使用しようとしていますが、運がありません。これらの関数を使用すると、一度に1文字ではなく、一度に1バイトずつ文字列をナビゲートします。MSDNによると、CharNextは文字列内の次の文字へのポインタを取得することになっています。

この問題を説明するためのコードを次に示します。

#include <windows.h>
#include <stdio.h>
#include <wchar.h>
#include <string.h>

/* string consisting of six "asian" characters */
wchar_t wcsString[] = L"\u9580\u961c\u9640\u963f\u963b\u9644";

int main() 
{
   // Convert the asian string from wide char to multi-byte.
   LPSTR mbString = new char[1000];
   WideCharToMultiByte( CP_UTF8, 0, wcsString, -1, mbString, 100,  NULL, NULL);

   // Count the number of characters in the string.
   int characterCount = 0;
   LPSTR currentCharacter = mbString;
   while (*currentCharacter)
   {
      characterCount++;

     currentCharacter = CharNextExA(CP_UTF8, currentCharacter, 0);
   }
}

(メモリリークとエラーチェックの失敗は無視してください。)

さて、上記の例では、characterCountが6になると予想します。これは、アジアの文字列の文字数だからです。ただし、mbStringには18文字が含まれているため、代わりにcharacterCountは18になります。

門阜陀阿阻附

それがどのように機能するのかわかりません。CharNextは、文字列内の「é–€é」が日本語文字のエンコードされたバージョンであるか、実際には文字é–€およびéであるかをどのように認識しますか?

いくつかのメモ:

  • すべての開発者がUnicodeについて知っておくべきことについてのJoelsブログ投稿を読みました。私はその中に何かを誤解したかもしれません。
  • 文字数を数えるだけなら、アジアの文字列の文字数を直接数えることができます。私の本当の目標は、マルチバイト文字列の一部を別の場所にコピーすることであることに注意してください。別の場所はマルチバイトのみをサポートし、ワイド文字はサポートしません。
  • MultiByteToWideCharを使用してmbStringの内容をワイド文字に戻すと、正しい文字列(門襲陀阿訓附)が得られます。これは、mbStringに問題がないことを示しています。

編集:明らかに、CharNext関数はUTF-8をサポートしていませんが、Microsoftはそれを文書化するのを忘れていました。私は自分のルーチンを一緒に投げたりコピーしたりしましたが、それは使用せず、改善する必要があります。簡単にクラッシュできると思います。

  LPSTR CharMoveNext(LPSTR szString)
  {
     if (szString == 0 || *szString == 0)
        return 0;

     if ( (szString[0] & 0x80) == 0x00)
        return szString + 1;
     else if ( (szString[0] & 0xE0) == 0xC0)
        return szString + 2;
     else if ( (szString[0] & 0xF0) == 0xE0)
        return szString + 3;
     else if ( (szString[0] & 0xF8) == 0xF0)
        return szString + 4;
     else
        return szString +1;
  }
4

5 に答える 5

4

これは、 Sorting it All Outブログで何が起こっているのかについての本当に良い説明です:CharNextExAは壊れていますか?。つまり、CharNextはUTF8文字列で動作するようには設計されていません。

于 2009-08-05T21:51:45.627 に答える
3

私が判断できる限り(グーグルと実験)、CharNextExAUTF-8では実際には機能せず、短いリード/トレイルバイトペアまたはシングルバイト文字を使用するマルチバイトエンコーディングのみがサポートされています。

UTF-8はかなり規則的なエンコーディングであり、必要な処理を実行するライブラリがたくさんありますが、独自のライブラリを作成するのもかなり簡単です。

有効なシーケンス形式については、ここunicode.org、特に表3-7を参照してください。

const char* NextUtf8( const char* in )
{
    if( in == NULL || *in == '\0' )
        return in;

    unsigned char uc = static_cast<unsigned char>(*in);

    if( uc < 0x80 )
    {
        return in + 1;
    }
    else if( uc < 0xc2 )
    {
         // throw error? invalid lead byte
    }
    else if( uc < 0xe0 )
    {
        // check in[1] for validity( 0x80 .. 0xBF )
        return in + 2;
    }
    else if( uc < 0xe1 )
    {
        // check in[1] for validity( 0xA0 .. 0xBF )
        // check in[2] for validity( 0x80 .. 0xBF )
        return in + 3;
    }
    else // ... etc.
    // ...
}
于 2009-08-05T22:02:46.553 に答える
3

CharNextExAがUTF-8で機能しない場合は、自分で解析できます。上位2ビットに10が含まれる文字をスキップするだけです。このパターンは、UTF-8の定義で確認できます:http://en.wikipedia.org/wiki/Utf-8

LPSTR CharMoveNext(LPSTR szString)
{
    ++szString;
    while ((*szString & 0xc0) == 0x80)
        ++szString;
    return szString;
}
于 2009-08-05T22:06:24.927 に答える
0

これはあなたの質問に対する直接の答えではありませんが、次のチュートリアルが役立つかもしれません、私は確かにそうしました。実際、ここで提供される情報は、マルチバイト文字列を自分で簡単にトラバースできるようにするのに十分です。

完全な文字列チュートリアル

于 2009-08-05T21:47:08.163 に答える
0

コードページに932を使用してみてください。CP_UTF8は実際のコードページではないと思います。また、WideCharToMultibyte()以降でのみ機能する可能性があります。isleadByte()を試すこともできますが、そのためには、ロケールを正しく設定するか、デフォルトのコードページを正しく設定する必要があります。IsDBCSLeadByteEx()を正常に使用しましたが、CP_UTF8を使用したことはありません。

于 2009-08-05T22:07:09.090 に答える