3

当面の課題

Windows で UTF-8 エンコードされた XML からファイル名を解析しています。そのファイル名を変更できない関数に渡す必要があります。内部的には_fsopen()、Unicode 文字列をサポートしないものを使用します。

現在のアプローチ

私の現在のアプローチは、ファイル名がそのエンコーディングで表現可能であることを期待して、ファイル名をユーザーの文字セットに変換することです。次にboost::locale::conv::from_utf()、UTF-8 から変換するために使用boost::locale::util::get_system_locale()し、現在のロケールの名前を取得するために使用しています。

人生は素晴らしい?

コードページWindows-1252を使用しているドイツのシステムを使用しているため、 de_DE.windows-1252get_system_locale()正しく生成されます。ウムラウトを含むファイル名でアプローチをテストすると、すべてが期待どおりに機能します。

問題

システム ロケールを、コード ページWindows-1251を使用するウクライナ語に切り替えたことを確認するためです。ファイル名にキリル文字を使用すると、私のアプローチは失敗します。その理由は、get_system_locale()現在は正しくないde_DE.windows-1252が生成されるためです。

一方GetACP()、ドイツ語ロケールでは 1252、ウクライナ語ロケールでは 1251 が正しく生成されます。また、この小さなテスト プログラムが期待どおりに動作するため、Boost.Locale が特定のロケールに変換できることも知っています。

#include <boost/locale.hpp>
#include <iostream>
#include <string>
#include <windows.h>

int main()
{
    std::cout << "Codepage: " << GetACP() << std::endl;
    std::cout << "Boost.Locale: " << boost::locale::util::get_system_locale() << std::endl;

    namespace blc = boost::locale::conv;
    // Cyrillic small letter zhe -> \xe6 (ш on 1251, æ on 1252)
    std::string const test1251 = blc::from_utf(std::string("\xd0\xb6"), "windows-1251");
    std::cout << "1251: " << static_cast<int>(test1251.front()) << std::endl;
    // Latin small letter sharp s -> \xdf (Я on 1251, ß on 1252)
    auto const test1252 = blc::from_utf(std::string("\xc3\x9f"), "windows-1252");
    std::cout << "1252: " << static_cast<int>(test1252.front()) << std::endl;

}

質問

  • Boost.Locale がサポートする形式でユーザー ロケールの名前を照会するにはどうすればよいですか? 使用するとGerman_Germany.1252std::locale("").name()が生成され、使用すると例外が発生します。boost::locale::conv::invalid_charset_error

  • システム ロケールがde_DE.windows-1252 のままである可​​能性はありますが、ローカル管理者として変更していると思われますか? 同様に、私のアカウントの言語は英語ですが、システム言語はドイツ語です。(ログイン画面はログインするまでドイツ語です)

  • 短いファイル名を使用する必要がありますか? ただし、確実に機能していないようです。

細字

  • コンパイラはMSVC18
  • Boost はバージョン 1.56.0、バックエンドはおそらく winapi
  • システムは Win7、システム言語はドイツ語、ユーザー言語は英語
4

2 に答える 2

2

ANSI は推奨されていないため、気にしないでください。

Windows は UTF16 を使用するため、 を使用して UTF8 から UTF16 に変換する必要がありますMultiByteToWideChar。この変換は安全です。

std::wstring getU16(const std::string &str)
{
    if (str.empty()) return std::wstring();
    int sz = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), 0, 0);
    std::wstring res(sz, 0);
    MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &res[0], sz);
    return res;
}

次に、_wfsopen(提供したリンクから) を使用して、UTF16 名のファイルを開きます。

int main()
{
    //UTF8 source:
    std::string filename_u8;

    //This line works in VS2015 only
    //For older version comment out the next line, obtain UTF8 from another source
    filename_u8 = u8"c:\\test\\__ελληνικά.txt";

    //convert to UTF16
    std::wstring filename_utf16 = getU16(filename_u8);

    FILE *file = NULL;
    _wfopen_s(&file, filename_utf16.c_str(), L"w");
    if (file)
    {
        //Add BOM, optional...

        //Write the file name in to file, for testing...
        fwrite(filename_u8.data(), 1, filename_u8.length(), file);

        fclose(file);
    }
    else
    {
        cout << "access denined, or folder doesn't exits...
    }

    return 0;
}


編集、UTF8 から ANSI を取得、使用GetACP()

std::wstring string_to_wstring(const std::string &str, int codepage)
{
    if (str.empty()) return std::wstring();
    int sz = MultiByteToWideChar(codepage, 0, &str[0], (int)str.size(), 0, 0);
    std::wstring res(sz, 0);
    MultiByteToWideChar(codepage, 0, &str[0], (int)str.size(), &res[0], sz);
    return res;
}

std::string wstring_to_string(const std::wstring &wstr, int codepage)
{
    if (wstr.empty()) return std::string();
    int sz = WideCharToMultiByte(codepage, 0, &wstr[0], (int)wstr.size(), 0, 0, 0, 0);
    std::string res(sz, 0);
    WideCharToMultiByte(codepage, 0, &wstr[0], (int)wstr.size(), &res[0], sz, 0, 0);
    return res;
}

std::string get_ansi_from_utf8(const std::string &utf8, int codepage)
{
    std::wstring utf16 = string_to_wstring(utf8, CP_UTF8);
    std::string ansi = wstring_to_string(utf16, codepage);
    return ansi;
}
于 2016-06-30T00:29:14.447 に答える