web-dev-qa-db-ja.com

Visual C ++ 2008でUTF-8文字列リテラルを作成する方法

VC++ 2003では、ソースファイルをUTF-8として保存でき、すべての文字列がそのまま使用されました。つまり、次のコードは、文字列をそのままコンソールに出力します。ソースファイルがUTF-8として保存された場合、出力はUTF-8になります。

printf("Chinese (Traditional)");
printf("中国語 (繁体)");
printf("중국어 (번체)");
printf("Chinês (Tradicional)");

UTF-8 BOMでUTF-8形式でファイルを保存しました。ただし、VC2008でコンパイルすると次の結果になります。

warning C4566: character represented by universal-character-name '\uC911' 
cannot be represented in the current code page (932)
warning C4566: character represented by universal-character-name '\uAD6D' 
cannot be represented in the current code page (932)
etc.

これらの警告を引き起こす文字は破損しています。ロケールに適合するもの(この場合は932 =日本語)は、ロケールエンコーディング、つまりShift-JISに変換されます。

VC++ 2008でこれをコンパイルする方法を見つけることができません。ソースファイルで使用するロケールは関係ないことに注意してください。 「何をしているのかわかっているので、f $%## ngで文字列リテラルを変更しないでください」というロケールはないようです。特に、無用なUTF-8疑似ロケールは機能しません。

#pragma setlocale(".65001") 
=> error C2175: '.65001' : invalid locale

「C」も:

#pragma setlocale("C") 
=> see warnings above (in particular locale is still 932)

VC2008では、すべての文字が指定された(またはデフォルトの)ロケールに強制され、そのロケールはUTF-8にできません。 「\ xbf\x11 ...」のようなエスケープ文字列を使用するようにファイルを変更したくないのは、同じソースがgccを使用してコンパイルされ、UTF-8ファイルを非常にうまく処理できるからです。

ソースファイルのコンパイルで文字列リテラルをそのままにすることを指定する方法はありますか?

別の言い方をすれば、ソースファイルのコンパイル時にVC2003との下位互換性を指定するために使用できるコンパイルフラグは何ですか。つまり、文字列リテラルを変更せず、そのままバイト単位で使用します。

更新

提案をありがとう、しかし私はwcharを避けたい。このアプリはUTF-8の文字列のみを処理するため、wcharを使用するには、すべての文字列をUTF-8に変換する必要がありますが、これは不要なはずです。入力、出力、内部処理はすべてUTF-8で行われます。これは、Linux上およびVC2003でコンパイルされた場合と同様に正常に機能するシンプルなアプリです。 VC2008で同じアプリをコンパイルして、機能させることができます。

これを実現するには、VC2008をローカルマシンのロケール(日本語、932)に変換しないようにする必要があります。 VC2008にはVC2003との下位互換性が必要です。文字列がそのまま、本質的にcharの不透明な配列、またはUTF-8として使用されることを示すロケールまたはコンパイラ設定が必要です。私はVC2003とgccで動けなくなるかもしれませんが、VC2008はこの例ではあまりにも賢くしようとしているようです。

61
brofield

更新:

これを行う方法は保証されていないと判断しました。以下に示すソリューションは英語版VC2003で機能しますが、日本語版VC2003(または日本語OS)でコンパイルすると失敗します。いずれにせよ、仕事に頼ることはできません。すべてをL ""文字列として宣言しても機能しなかったことに注意してください(以下で説明するように、gccでは苦痛です)。

代わりに、弾丸を噛んですべてのテキストをデータファイルに移動し、そこから読み込むだけでよいと考えています。現在、テキストをINI SimpleIni (クロスプラットフォームINIファイルライブラリ)を介してファイルに保存し、アクセスしています。テキストはプログラム外です。

元の:

エヴァンだけが問題を理解しているように見えたので、私は自分でこれに答えています。 Unicodeの概要とwchar_tの使用方法に関する回答は、この問題には関係ありません。これは、国際化に関する問題でも、Unicodeの文字エンコーディングの誤解でもないためです。しかし、あなたが助けようとする試みに感謝します。

問題は、さまざまなプラットフォームとコンパイラーでクロスコンパイルする必要があるソースファイルがあることです。プログラムはUTF-8処理を行います。他のエンコーディングは気にしません。現在gccとvc2003で動作するようなUTF-8の文字列リテラルを持ちたいです。 VC2008でそれを行うにはどうすればよいですか? (つまり、後方互換性のあるソリューション)。

これは私が見つけたものです:

gcc(v4.3.2 20081105):

  • 文字列リテラルはそのまま使用されます(生の文字列)
  • uTF-8エンコードされたソースファイルをサポート
  • ソースファイルにはUTF-8 BOMを含めることはできません

vc2003:

  • 文字列リテラルはそのまま使用されます(生の文字列)
  • uTF-8エンコードされたソースファイルをサポート
  • ソースファイルにはUTF-8 BOMが含まれる場合と含まれない場合があります(重要ではありません)

vc2005 +:

  • 文字列リテラルはコンパイラによって処理されます(生の文字列はありません)
  • 文字列リテラルは、指定されたロケールに再エンコードされます
  • UTF-8はターゲットロケールとしてサポートされていません
  • ソースファイルにはUTF-8 BOMが必要です

したがって、簡単な答えは、この特定の目的のために、VC2005 +は壊れており、下位互換性のあるコンパイルパスを提供しないということです。コンパイルされたプログラムにUnicode文字列を取得する唯一の方法は、UTF-8 + BOM + wcharを使用することです。つまり、使用時にすべての文字列をUTF-8に戻す必要があります。

WcharをUTF-8に変換する単純なクロスプラットフォームの方法はありません。たとえば、wcharはどのサイズとエンコーディングですか? Windowsでは、UTF-16。他のプラットフォームでは?それは異なります。詳細については、 ICUプロジェクト を参照してください。

最終的に、次のようなソースを持つvc2005 +以外のすべてのコンパイラで変換コストを回避することにしました。

#if defined(_MSC_VER) && _MSC_VER > 1310
// Visual C++ 2005 and later require the source files in UTF-8, and all strings 
// to be encoded as wchar_t otherwise the strings will be converted into the 
// local multibyte encoding and cause errors. To use a wchar_t as UTF-8, these 
// strings then need to be convert back to UTF-8. This function is just a rough 
// example of how to do this.
# define utf8(str)  ConvertToUTF8(L##str)
const char * ConvertToUTF8(const wchar_t * pStr) {
    static char szBuf[1024];
    WideCharToMultiByte(CP_UTF8, 0, pStr, -1, szBuf, sizeof(szBuf), NULL, NULL);
    return szBuf;
}
#else
// Visual C++ 2003 and gcc will use the string literals as is, so the files 
// should be saved as UTF-8. gcc requires the files to not have a UTF-8 BOM.
# define utf8(str)  str
#endif

このコードは単純化された例にすぎないことに注意してください。実稼働環境では、さまざまな方法(スレッドセーフ、エラーチェック、バッファサイズチェックなど)でクリーンアップする必要があります。

これは次のコードのように使用されます。正常にコンパイルされ、gcc、vc2003、およびvc2008のテストで正しく動作します。

std::string mText;
mText = utf8("Chinese (Traditional)");
mText = utf8("中国語 (繁体)");
mText = utf8("중국어 (번체)");
mText = utf8("Chinês (Tradicional)");
33
brofield

ブロフィールド、

私はまったく同じ問題を抱えていて、ソース文字列をワイド文字に変換して戻す必要のないソリューションにつまずいた:ソースファイルをUTF-8 without署名として保存すると、VC2008はそのままにしておく。署名を落とすことがわかったとき、とてもうまくいきました。総括する:

Unicode(署名なしのUTF-8)-コードページ65001、VC2008でc4566警告をスローせず、VCがエンコードを混乱させることはありませんが、コードページ65001(UTF-8 With署名)はc4566をスローします(あなたが見つけたように)。

これで手遅れにならないことを願っていますが、VC2008アプリを高速化して回避策を削除できる可能性があります。

16
echo

おそらく、ワイド文字列を使用し、必要に応じてUTF-8に変換することをお勧めします。あなたが言ったようにあなたの最善の策は、文字列に16進エスケープを使用することだと思います。コードポイント\uC911、これを行うことができます。

const char *str = "\xEC\xA4\x91";

これはうまくいくと思いますが、あまり読みやすいものではないので、これを行う場合はコメントしてください。

16
Evan Teran

ファイル/詳細保存オプション/エンコード: "Unicode(UTF-8 署名なし)-コードページ65001"

14
Vladius

ソースファイルのVisual C++(2005+)COMPILER標準動作は次のとおりです。

  • CP1252(この例では、西ヨーロッパのコードページ):
    • "Ä"C4 00
    • 'Ä'C4
    • L"Ä"00C4 0000
    • L'Ä'00C4
  • BOMなしのUTF-8:
    • "Ä"C3 84 00(= UTF-8)
    • 'Ä'→警告:複数文字の定数
    • "Ω"E2 84 A6 00(= UTF-8、予想どおり)
    • L"A"00C3 0084 0000 (違う!)
    • L'Ä'→警告:複数文字の定数
    • L"Ω"00E2 0084 00A6 0000 (違う!)
  • BOM付きUTF-8:
    • "Ä"C4 00(= CP1252、UTF-8なし)、
    • 'Ä'C4
    • "Ω"→エラー:CP1252に変換できません!
    • L"Ä"00C4 0000(正しい)
    • L'Ä'00C4
    • L"Ω"2126 0000(正しい)

ご覧のとおり、CコンパイラはCP1252と同じ方法でBOMなしでUTF-8ファイルを処理します。その結果、コンパイラーはコンパイル済みの出力にUTF-8とUTF-16の文字列を混在させることはできません!したがって、1つのソースコードファイルを決定する必要があります。

  • either BOMでUTF-8を使用し、UTF-16文字列のみを生成します(つまり、常にLプレフィックスを使用)、
  • または BOMなしのUTF-8およびUTF-8文字列のみを生成します(つまり、Lプレフィックスを使用しないでください)。
  • 7ビットASCII文字は含まれず、Lプレフィックスの有無にかかわらず使用できます

独立して、EDITORはBOMなしのUTF-8ファイルをUTF-8ファイルとして自動検出できます。

8
Henrik Haftmann

この非常に素晴らしいブログへのコメントから
「Visual StudioでのCおよびC++の文字列の内部表現としてUTF-8を使用する」
=> http://www.nubaria.com/en/blog/?p=289

#pragma execution_character_set("utf-8") 

Visual Studio 2008 SP1、および次の修正プログラムが必要です。

http://support.Microsoft.com/kb/98026 ....

6
Alexander Jung

これはどう?文字列をUTF-8エンコードファイルに保存してから、ASCIIエンコードC++ソースファイルに前処理します。16進エスケープを使用して、文字列内にUTF-8エンコードを保持します。

"中国語 (繁体)"

に変換されます

"\xE4\xB8\xAD\xE5\x9B\xBD\xE8\xAA\x9E (\xE7\xB9\x81\xE4\xBD\x93)"

もちろん、これは人間には読めません。その目的は、コンパイラの問題を回避することだけです。

C++プリプロセッサを使用して、変換されたヘッダーファイルの文字列を参照するか、このトリックを使用してコンパイルする前にUTF-8ソース全体をASCIIに変換できます。

4

Char_traits :: widen()を使用すると、ネイティブエンコードからの移植可能な変換が簡単になります。

#include <locale>
#include <string>
#include <vector>

/////////////////////////////////////////////////////////
// NativeToUtf16 - Convert a string from the native 
//                 encoding to Unicode UTF-16
// Parameters:
//   sNative (in): Input String
// Returns:        Converted string
/////////////////////////////////////////////////////////
std::wstring NativeToUtf16(const std::string &sNative)
{
  std::locale locNative;

  // The UTF-16 will never be longer than the input string
  std::vector<wchar_t> vUtf16(1+sNative.length());

  // convert
  std::use_facet< std::ctype<wchar_t> >(locNative).widen(
        sNative.c_str(), 
        sNative.c_str()+sNative.length(), 
        &vUtf16[0]);

  return std::wstring(vUtf16.begin(), vUtf16.end());
}

理論的には、UTF-16からUTF-8への戻りの旅も同様に簡単であるはずですが、UTF-8ロケールが私のシステム(Win7上のVC10 Express)で正しく機能しないことがわかりました。

したがって、RFC 3629に基づいた単純なコンバーターを作成しました。

/////////////////////////////////////////////////////////
// Utf16ToUtf8 -   Convert a character from UTF-16 
//                 encoding to UTF-8.
//                 NB: Does not handle Surrogate pairs.
//                     Does not test for badly formed 
//                     UTF-16
// Parameters:
//   chUtf16 (in): Input char
// Returns:        UTF-8 version as a string
/////////////////////////////////////////////////////////
std::string Utf16ToUtf8(wchar_t chUtf16)
{
    // From RFC 3629
    // 0000 0000-0000 007F   0xxxxxxx
    // 0000 0080-0000 07FF   110xxxxx 10xxxxxx
    // 0000 0800-0000 FFFF   1110xxxx 10xxxxxx 10xxxxxx

    // max output length is 3 bytes (plus one for Nul)
    unsigned char szUtf8[4] = "";

    if (chUtf16 < 0x80)
    {
        szUtf8[0] = static_cast<unsigned char>(chUtf16);
    }
    else if (chUtf16 < 0x7FF)
    {
        szUtf8[0] = static_cast<unsigned char>(0xC0 | ((chUtf16>>6)&0x1F));
        szUtf8[1] = static_cast<unsigned char>(0x80 | (chUtf16&0x3F));
    }
    else
    {
        szUtf8[0] = static_cast<unsigned char>(0xE0 | ((chUtf16>>12)&0xF));
        szUtf8[1] = static_cast<unsigned char>(0x80 | ((chUtf16>>6)&0x3F));
        szUtf8[2] = static_cast<unsigned char>(0x80 | (chUtf16&0x3F));
    }

    return reinterpret_cast<char *>(szUtf8);
}


/////////////////////////////////////////////////////////
// Utf16ToUtf8 -   Convert a string from UTF-16 encoding
//                 to UTF-8
// Parameters:
//   sNative (in): Input String
// Returns:        Converted string
/////////////////////////////////////////////////////////
std::string Utf16ToUtf8(const std::wstring &sUtf16)
{
    std::string sUtf8;
    std::wstring::const_iterator itr;

    for (itr=sUtf16.begin(); itr!=sUtf16.end(); ++itr)
        sUtf8 += Utf16ToUtf8(*itr);
    return sUtf8;
}

これはどのプラットフォームでも動作するはずですが、自分のシステムを除いてテストすることができなかったため、バグがある可能性があります。

#include <iostream>
#include <fstream>

int main()
{
    const char szTest[] = "Das tausendschöne Jungfräulein,\n"
                          "Das tausendschöne Herzelein,\n"
                          "Wollte Gott, wollte Gott,\n"
                          "ich wär' heute bei ihr!\n";

    std::wstring sUtf16 = NativeToUtf16(szTest);
    std::string  sUtf8  = Utf16ToUtf8(sUtf16);

    std::ofstream ofs("test.txt");
    if (ofs)
        ofs << sUtf8;
    return 0;
}
3
Michael J

たぶん実験してみてください:

#pragma setlocale(".UTF-8")

または:

#pragma setlocale("english_england.UTF-8")
1

私はパーティーに遅れていることは知っていますが、私は これを広げる が必要だと思います。 Visual C++ 2005以降では、ソースファイルにBOM(バイトオーダーマーク)が含まれておらず、システムロケールが英語でない場合、VCはソースファイルがUnicode。

UTF-8ソースファイルを正しくコンパイルするには、BOMエンコーディングなしでUTF-8で保存する必要があり、システムロケール(非Unicode言語)は英語でなければなりません。

enter image description here

1
raymai97

同様の問題がありました。 UTF-8文字列リテラルは、コンパイル中に現在のシステムコードページに変換されました。objファイルを16進ビューアで開いただけで、すでに壊れています。たとえば、文字ćは1バイトだけでした。

私にとっての解決策は、UTF-8およびBOMなしで保存することでした。それは私がコンパイラをだました方法です。今ではそれが単なる通常のソースであると考えており、文字列を翻訳しません。 .objファイルではćは2バイトになりました。

一部のコメンテーターを無視してください。私はあなたが望むものを理解しています-私も同じことを望みます:UTF-8ソース、UTF-8生成ファイル、UTF-8入力ファイル、翻訳なしの通信回線上のUTF-8。

たぶんこれは役立ちます...

1
Daniel N.

UTF-8ナロー(char)文字列リテラルのコンパイルで同様の問題が発生しました。基本的に、UTF-8 BOMと#pragma execution_character_set("utf-8") [1]の両方、またはBOMとプラグマの両方が必要でした。 [2]。一方を他方なしで使用すると、不正な変換が発生しました。

詳細は https://github.com/jay/compiler_string_test で文書化しました。

[1]:Visual Studio 2012はexecution_character_setをサポートしていません。 Visual Studio 2010および2015は正常に動作し、2008年のパッチでご存じのとおり正常に動作します。

[2]:このスレッドのいくつかのコメントは、BOMもプラグマも使用しないと、マルチバイトのローカルコードページ(日本など)を使用する開発者にとって誤った変換になる可能性があると指摘しています。

0
Jay

UTF-8ソースファイル

  • BOMなし:システムが> 1byte/charコードページ(Shift JISなど)を使用している場合を除き、rawとして扱われます。システムのコードページを任意のシングルバイトに変更する必要があり、その後、リテラル内でUnicode文字を使用し、問題なくコンパイルできるはずです(少なくとも願っています)。
  • With BOM:コンパイル中に文字と文字列リテラルをシステムコードページに変換します。 GetACP()で現在のシステムコードページを確認できます。私の知る限り、システムのコードページを65001(UTF-8)に設定する方法はないため、BOMでUTF-8を直接使用する方法はありません。

移植性があり、コンパイラに依存しない唯一の方法は、ASCII charsetとエスケープシーケンスを使用することです。コンパイラがUTF-8エンコードファイルを受け入れるという保証はないためです。

0
user206334

だから、物事は変更されます。今、私は解決策を得ました。

まず、英語などのシングルバイトコードページローカルで実行する必要があります。これにより、cl.exeがコードを混乱させないようにできます。

次に、ソースコードをUTF8-NO BOMで保存します。NO-BOMに注意してください。次にcl.exeでコンパイルします。printfwprintなどのC APIを呼び出さないでください。理由はわかりません:)....後で研究するかもしれません...

その後、コンパイルして実行すると、結果が表示されます.....私のメールはluoyonggangで、(Googleの)いくつかの希望を......

wscript:

#! /usr/bin/env python
# encoding: utf-8
# Yonggang Luo

# the following two variables are used by the target "waf dist"
VERSION='0.0.1'
APPNAME='cc_test'

top = '.'

import waflib.Configure

def options(opt):
    opt.load('compiler_c')

def configure(conf):
    conf.load('compiler_c')
    conf.check_lib_msvc('gdi32')
    conf.check_libs_msvc('kernel32 user32')

def build(bld):
    bld.program(
        features = 'c',
        source   = 'chinese-utf8-no-bom.c',
        includes = '. ..',
        cflags   = ['/wd4819'],
        target   = 'myprogram',
        use      = 'KERNEL32 USER32 GDI32')

実行スクリプトrun.bat

rd /s /q build
waf configure build --msvc_version "msvc 6.0"
build\myprogram

rd /s /q build
waf configure build --msvc_version "msvc 9.0"
build\myprogram

rd /s /q build
waf configure build --msvc_version "msvc 10.0"
build\myprogram

ソースコードmain.c:

//encoding : utf8 no-bom
#include <stdio.h>
#include <string.h>

#include <Windows.h>

char* ConvertFromUtf16ToUtf8(const wchar_t *wstr)
{
    int requiredSize = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, 0, 0, 0, 0);
    if(requiredSize > 0)
    {
        char *buffer = malloc(requiredSize + 1);
        buffer[requiredSize] = 0;
        WideCharToMultiByte(CP_UTF8, 0, wstr, -1, buffer, requiredSize, 0, 0);
        return buffer;
    }
    return NULL;
}

wchar_t* ConvertFromUtf8ToUtf16(const char *cstr)
{
    int requiredSize = MultiByteToWideChar(CP_UTF8, 0, cstr, -1, 0, 0);
    if(requiredSize > 0)
    {
        wchar_t *buffer = malloc( (requiredSize + 1) * sizeof(wchar_t) );
        printf("converted size is %d 0x%x\n", requiredSize, buffer);
        buffer[requiredSize] = 0;
        MultiByteToWideChar(CP_UTF8, 0, cstr, -1, buffer, requiredSize);
        printf("Finished\n");
        return buffer;
    }
    printf("Convert failed\n");
    return NULL;
}

void ShowUtf8LiteralString(char const *name, char const *str)
{
    int i = 0;
    wchar_t *name_w = ConvertFromUtf8ToUtf16(name);
    wchar_t *str_w = ConvertFromUtf8ToUtf16(str);

    printf("UTF8 sequence\n");
    for (i = 0; i < strlen(str); ++i)
    {
        printf("%02x ", (unsigned char)str[i]);
    }

    printf("\nUTF16 sequence\n");
    for (i = 0; i < wcslen(str_w); ++i)
    {
        printf("%04x ", str_w[i]);
    }

    //Why not using printf or wprintf? Just because they do not working:)
    MessageBoxW(NULL, str_w, name_w, MB_OK);
    free(name_w);
    free(str_w);

}

int main()
{
    ShowUtf8LiteralString("English english_c", "Chinese (Traditional)");
    ShowUtf8LiteralString("简体 s_chinese_c", "你好世界");
    ShowUtf8LiteralString("繁体 t_chinese_c", "中国語 (繁体)");
    ShowUtf8LiteralString("Korea korea_c", "중국어 (번체)");
    ShowUtf8LiteralString("What? what_c", "Chinês (Tradicional)");
}
0
lygstate

私は同様の問題を抱えていました、解決策は高度な保存オプションを使用してbomなしでUTF 8で保存することでした

0
Dennis