0

次のコードを使用して、テキスト エディター (メモ帳 ++) で作成したテキスト (.xml) ファイルから読み取り、そこから読み取った UTF-8 テキストを UTF-16 に変換して、Windows API 関数が使用できるようにします。 、次にこの UTF-16 でエンコードされたテキストを 2 番目のファイルに書き戻します。

私の問題は、出力ファイルを Notepad++ で開いたときに、テキスト エディターにどのエンコーディングを使用するように要求しても、期待どおりの結果が得られないことです。ファイル内のほぼすべての文字の前にヌル文字があります。出力ファイルに UTF-16 を書き込んでいるときに何か間違ったことをしたか、Notepad++ が 1 バイト文字として読み取っていると思います。

何かアイデアはありますか?コードは次のとおりです。

#define UNICODE

// includes...

int main( int argc, char * argv[] )
{
    FILE * pzInFile,
         * pzOutFile;

    try
    {
        char   sUtf8[8192];
        char * pcDst = sUtf8;

        wchar_t wsUtf16[8192];

        _wfopen_s( & pzInFile, L"../config-sample.xml", L"r" );
        _wfopen_s( & pzOutFile, L"../config-sample2.xml", L"w+" );

        if( pzInFile && pzOutFile )
        {
            size_t uiRead;

            while( uiRead = fread_s( pcDst, sizeof( sUtf8 ), 1, 1, pzInFile ) )
            {
                pcDst += uiRead;
            }

            size_t uiLen = pcDst - sUtf8;

            sUtf8[uiLen] = 0;

            MultiByteToWideChar( CP_UTF8, 0, sUtf8, 8192, wsUtf16, 8192 ); // UTF-8 to UTF-16

            fwrite( wsUtf16, 1, uiLen, pzOutFile );
        }
        else
        {
            throw L"Failed to open file";
        }
    }
    catch( const wchar_t * pwsMsg )
    {
        ::MessageBox( NULL, pwsMsg, L"Error", MB_OK | MB_TOPMOST | MB_SETFOREGROUND );
    }

    if( pzInFile )
    {
        fclose( pzInFile );
        pzInFile = 0;
    }
    if( pzOutFile )
    {
        fclose( pzOutFile );
        pzOutFile = 0;
    }

    return 0;
}
4

4 に答える 4

3

いくつかのエラーを修正するためにコードを修正しました。Notepad++ は BOM の有無にかかわらず出力ファイルを正しく表示していたので、エンコーディングの検出ルーチンは健全に見えます。

主な問題は次のとおりです。

  • 出力ファイルは (少なくとも) バイナリ モードで開く必要があります。そうしないと、2 バイトの UTF-16 文字が0AWindows で に変換されます0D 0A
  • ファイルの読み取りを単純化して、1 回の読み取りでバッファーをいっぱいにし、null 終端を追加することを考慮しました。
  • MultibyteToWideChar変換された文字数を返します-1。これは null で終了するため、入力バッファー サイズに使用しました。
  • fwrite変換された文字列の正しいバイト数で、変換された文字 (null を差し引いたもの) を書き込む必要があります。文字は UTF-16 で 2 バイトです。

ファイルの破損に関しては、もともとfwriteUTF-8文字列の長さに等しいバイトを書き込んでいました。それは1〜3文字であるため、元の出力ファイルの長さは39バイトでした... UTF-16ファイルでは不可能なので、おそらくNotepad ++のエンコーディングヒューリスティックはUTF-16を検出しませんでした. サンプルデータを提供しなかったので、いくつか作成しましたが、メモ帳++はまだUTF-16を検出しているため、ヒューリスティックはデータに応じて推測することもできます.

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>

int main()
{
    FILE* pzInFile;
    FILE* pzOutFile;

    try
    {
        char    sUtf8[8192];
        wchar_t wsUtf16[8192];

        _wfopen_s(& pzInFile, L"config-sample.xml", L"r");
        _wfopen_s(& pzOutFile, L"config-sample2.xml", L"w+b");

        if(pzInFile && pzOutFile)
        {
            size_t uiRead = fread_s(sUtf8, sizeof(sUtf8), 1, sizeof(sUtf8) - 1, pzInFile);
            sUtf8[uiRead] = 0;

            int wlen = MultiByteToWideChar(CP_UTF8, 0, sUtf8, -1, wsUtf16, 8192);   // UTF-8 to UTF-16

            fwrite(wsUtf16, 1, (wlen-1) * sizeof(wchar_t), pzOutFile);
        }
        else
        {
            throw L"Failed to open file";
        }
    }
    catch(const wchar_t* pwsMsg)
    {
        ::MessageBox(NULL, pwsMsg, L"Error", MB_OK | MB_TOPMOST | MB_SETFOREGROUND);
    }

    if(pzInFile)
    {
        fclose(pzInFile);
        pzInFile = 0;
    }
    if(pzOutFile)
    {
        fclose(pzOutFile);
        pzOutFile = 0;
    }

    return 0;
}

私のデータファイルには以下が含まれていました:

<data>αßΓπΣσµτΦΘΩδ</data>

以下は、入力ファイルと出力ファイルの 16 進ダンプです。入力ファイルをテキストモードで開いたままにしたため、入力時に0D0ACR-LF の組み合わせが に変換された0Aため、バイナリモードの出力ファイルでは改行のみが終了したことに注意してください。これは、Windows のテキストとバイナリの問題を説明するために残しました。おそらく、両方をバイナリ モードで開く必要があります。

input:  3C646174613ECEB1C39FCE93CF80CEA3CF83C2B5CF84CEA6CE98CEA9CEB43C2F646174613E0D0A0D0A
output: 3C0064006100740061003E00B103DF009303C003A303C303B500C403A6039803A903B4033C002F0064006100740061003E000A000A00
于 2013-09-08T00:22:09.687 に答える
0

基本的な問題は、UTF-8 として読み取られるファイルに UTF-16 を書き込んでいるということです。したがって、すべての文字の前に null 文字があります。UTF-16 を UTF-8 に変換して書き戻したいだけです。

于 2013-09-07T23:28:13.700 に答える