web-dev-qa-db-ja.com

C ++でUnicodeを使用する方法

次のような非常に単純なプログラムを想定しています。

  • 名前を尋ねます。
  • 名前を変数に格納します。
  • 可変コンテンツを画面に表示します。

とても簡単なので、最初に学びます。

しかし、問題は、日本語の文字を使用して名前を入力すると、同じことをする方法がわからないことです。

したがって、C++でこれを行う方法を知っている場合は、例を示してください(コンパイルしてテストできます)。

ありがとう。


user362981:ご協力ありがとうございます。あなたが書いたコードを問題なくコンパイルしたところ、コンソールウィンドウが表示され、日本語の文字を入力できません(IMEを使用)。また、コード内の単語( "hello")を日本語の文字を含むものに変更しても、これらの文字は表示されません。

Svisstack:また、ご協力いただきありがとうございます。しかし、コードをコンパイルすると、次のエラーが発生します。

warning: deprecated conversion from string constant to 'wchar_t*'
error: too few arguments to function 'int swprintf(wchar_t*, const wchar_t*, ...)'
error: at this point in file
warning: deprecated conversion from string constant to 'wchar_t*'
28
Dox

あなたはワイドキャラクターについて多くの答えを得るでしょう。ワイド文字、特にwchar_tはUnicodeとは異なります。 unsigned charと同じように、それらを使用して(いくつかの落とし穴があります)Unicodeを格納できます。 wchar_tはシステムに非常に依存しています。 nicode Standard、バージョン5.2、第5章: を引用するには

wchar_tワイド文字タイプでは、ANSI/ISO Cは固定幅のワイド文字を含めることができます。 ANSI/ISO Cは、ワイド文字セットのセマンティクスを特定の実装に任せますが、移植可能なC実行セットからの文字が、ゼロ拡張によって同等のワイド文字に対応する必要があります。

そしてそれ

wchar_tの幅はコンパイラー固有であり、8ビットまで小さくすることができます。したがって、CまたはC++コンパイラ全体で移植可能にする必要があるプログラムは、Unicodeテキストの格納にwchar_tを使用しないでください。 wchar_tタイプは、コンパイラによって定義されたワイド文字(一部のコンパイラではUnicode文字)を格納するためのものです。

つまり、実装が定義されています。次に2つの実装を示します。Linuxでは、wchar_tは4バイト幅で、UTF-32エンコーディングでテキストを表します(現在のロケールに関係なく)。 (システムに応じてBEまたはLEのどちらかネイティブです。)ただし、Windowsは2バイト幅のwchar_tを持ち、UTF-16コードユニットを表します。全然違う。

より良い方法:ロケールについて知る必要があります。たとえば、UTF-8(Unicode)を使用する環境設定があるであるため、次のプログラムはUnicodeを使用します。

#include <iostream>

int main()
{
    setlocale(LC_ALL, "");
    std::cout << "What's your name? ";
    std::string name;
    std::getline(std::cin, name);
    std::cout << "Hello there, " << name << "." << std::endl;
    return 0;
}

...

$ ./uni_test
What's your name? 佐藤 幹夫
Hello there, 佐藤 幹夫.
$ echo $LANG
en_US.UTF-8

しかし、Unicodeについては何もありません。文字を読み取るだけで、UTF-8として提供されます環境がそのように設定されているため。簡単に言うと、「一体、チェコ人です。ISO-8859-2を使用しましょう」:突然、プログラムはISO-8859-2に入力を取得しますが、それは単に逆流しているだけなので、問題ではありません。 、プログラムは引き続き正しく実行されます。

さて、その例が私の名前を読み取ってからそれをXMLファイルに書き込もうとして、上に愚かに<?xml version="1.0" encoding="UTF-8" ?>と書いた場合、端末がUTF-8であったときに正しくなりますが、間違っています。端末がISO-8859-2だったとき。後者の場合、XMLファイルにシリアル化する前に変換する必要があります。 (または、ISO-8859-2をXMLファイルのエンコーディングとして記述します。)

多くのPOSIXシステムでは、ユーザーにいくつかの利点を提供するため、現在のロケールは通常UTF-8ですが、これは保証されていません。 UTF-8をstdoutに出力するだけで通常は正しくなりますが、常にそうとは限りません。 ISO-8859-2を使用しているとしましょう。ISO-8859-1の "è"(0xE8)を無意識に端末に出力すると、 "č"(0xE8)が表示されます。同様に、UTF-8 "è"(0xC3 0xA8)を出力すると、(ISO-8859-2) "è"(0xC3 0xA8)が表示されます。不正な文字のこのバーフィングは Mojibake と呼ばれています。

多くの場合、データをシャッフルするだけで、それほど問題になりません。これは通常、データをシリアル化する必要がある場合に役立ちます。 (たとえば、多くのインターネットプロトコルはUTF-8またはUTF-16を使用します:ISO-8859-2端末からデータを受け取った場合、またはWindows-1252でエンコードされたテキストファイルを受け取った場合は、変換する必要があります。そうしないと、送信中 文字組み 。)

悲しいことに、これはCとC++の両方でのUnicodeサポートの状態についてです。これらの言語は実際にはシステムに依存せず、特定の方法に縛られることはありません。これには文字セットが含まれます。ただし、Unicodeやその他の文字セットを処理するためのライブラリはたくさんあります。

結局、それほど複雑ではありません。データのエンコード方法を理解し、出力のエンコード方法を理解します。それらが同じでない場合は、変換を行う必要があります。これは、std::coutstd::wcoutのどちらを使用しているかに関係なく適用されます。私の例では、stdinまたはstd::cinおよびstdout/std::coutがUTF-8、ISO-8859-2の場合がありました。

37
Thanatos

選択したOSで汎用のワイド文字サポートを使用して簡単なことを行うことができますが、一般にC++にはUnicodeの組み込みサポートが十分にないため、長期的には [〜#〜] icu [〜#〜]

1
Nick Bastin

Coutをwcout、cinをwcin、stringをwstringに置き換えてみてください。プラットフォームによっては、これが機能する場合があります。

#include <iostream>
#include <string>

int main() {
  std::wstring name;
  std::wcout << L"Enter your name: "; 
  std::wcin >> name;
  std::wcout << L"Hello, " << name << std::endl;
}

他の方法もありますが、これは「最小限の変更」の答えのようなものです。

1
EvanED
#include <stdio.h>
#include <wchar.h>

int main()
{
    wchar_t name[256];

    wprintf(L"Type a name: ");
    wscanf(L"%s", name);

    wprintf(L"Typed name is: %s\n", name);

    return 0;
}
1
Svisstack

前提条件: http://www.joelonsoftware.com/articles/Unicode.html

上記の記事は、ユニコードとは何かを説明する必読ですが、残っている質問はほとんど残っていません。はいUNICODEには、すべての言語のすべての文字に固有のコードポイントがあり、さらに実際のコードとは異なる可能性があるエンコードおよびメモリへの格納が可能です。このようにして、たとえばUTF-8エンコーディングを使用してメモリを節約できます。サポートされている言語が英語のみで、メモリの表現がASCII –これはもちろんエンコーディングを知っている場合)理論的には、エンコーディングがわかっていれば、これらの長いUNICODE文字を好きなように格納して読み返すことができますが、実際の世界は少し異なります。

C++プログラムにUNICODE文字/文字列をどのように格納しますか?どのエンコーディングを使用していますか?答えは、エンコードを使用していないが、ASCII文字をASCII stringに格納するのと同じように、UnicodeコードポイントをUnicode文字列に直接格納することです。 。問題は、UNICODE文字には固定サイズがないため、どの文字サイズを使用する必要があるかということです。簡単な答えは、サポートする最大の文字コードポイント(言語)を保持するのに十分な幅の文字サイズを選択することです。

UNICODE文字に2バイト以上かかるという理論は依然として成り立ち、混乱を招く可能性があります。コードポイントを3バイトまたは4バイトで格納するのではなく、実際にすべてのUnicode文字を表すのはそれではありませんか? Visual C++がユニコードをwchar_tに格納しているのはなぜですか。これは2バイトのみであり、明らかにすべてのUNICODEコードポイントを格納するのに十分ではありません。

Visual C++でUNICODE文字コードポイントを2バイトで格納する理由は、実際にはASCII(=英語)文字を1バイトに格納する理由とまったく同じです。当時は、英語のみなので、1バイトで十分です。現在、ほとんどの国際言語を考えていますが、すべてではないので、2バイトで十分です。はい、確かに、この表現では、3バイトかかるコードポイントを表すことができません。それ以上の人たちですが、まだコンピュータを購入していないので、まだ気にしていません。はい、まだメモリが足りないので、3バイトまたは4バイトを使用していません。追加の0(zero)使用しないときはすべての文字を含むバイト(その言語)。これもASCIIが各文字を1バイトに格納していたのと同じ理由です)なぜ英語を1バイトで表現できるのに2バイト以上で文字を格納し、それらの余分な特殊文字のために余裕があるのですか?

理論的には、2バイトはすべてのUnicodeコードポイントを表すのに十分ではありませんが、今のところ気に入る可能性のあるものを保持するには十分です。真のUNICODE文字列表現では、各文字を4バイトで格納できますが、それらの言語は関係ありません。

私たちが友好的なエイリアンを豊富に見つけ、無数の言語を取り入れて彼らとコミュニケーションしたいと思う今から1000年を想像してみてください。単一のユニコード文字サイズは、すべてのコードポイントに対応するために、おそらく8バイトまで大きくなります。これで、Unicode文字ごとに8バイトを使い始める必要があるという意味ではありません。メモリは限られたリソースであり、必要なものを割り当てます。

UNICODE文字列をCスタイルの文字列として処理できますか?

C++ではASCII文字列は引き続きC++で処理できますが、C関数を適用できるchar *ポインタで文字列を取得することでかなり一般的です。ただし、現在のCスタイルの文字列関数をUNICODE文字列に適用するC文字列を終了する単一のNULLバイトが含まれる可能性があるため、意味がありません。

UNICODE文字列はテキストのプレーンバッファーではなくなりましたが、NULLバイトで終了するシングルバイト文字のストリームよりも複雑になりました。このバッファーは、C内でもポインターによって処理できますが、UNICODE互換の呼び出しまたはCライブラリを必要とします。これらの文字列は、これらの文字列を読み書きし、操作を実行できます。

これは、C++でUNICODE文字列を表す特殊なクラスを使用することで簡単になります。このクラスは、Unicode文字列バッファーの複雑さを処理し、簡単なインターフェイスを提供します。このクラスは、Unicode文字列の各文字が2バイト以上かどうかも決定します。これらは実装の詳細です。今日はwchar_t(2バイト)を使用する可能性がありますが、明日はより多くの(あまり知られていない)言語をサポートするために各文字に4バイトを使用する可能性があります。これが、実装が変更されたときに適切なサイズにマップする固定サイズよりも常にTCHARを使用する方が良い理由です。

UNICODE文字列にインデックスを付けるにはどうすればよいですか?

また、特にCスタイルの文字列の処理では、文字列内のサブ文字列をトラバースまたは検索するためにインデックスを使用することも注目に値します。 ASCII文字列内のこのインデックスは、その文字列内のアイテムの位置に直接対応していますが、UNICODE文字列では意味がなく、避ける必要があります。

NULLバイトを終了する文字列はどうなりますか?

UNICODE文字列はまだNULLバイトで終了していますか?単一のNULLバイトで文字列を終了できますか?これは実装上の問題ですが、NULLバイトは1つのUnicodeコードポイントであり、他のすべてのコードポイントと同様に、他のコードポイントと同じサイズでなければなりません(特にエンコードがない場合)。したがって、Unicode文字列の実装がwchar_tに基づいている場合は、NULL文字も2バイトでなければなりません。すべてのUNICODEコードポイントは、nullバイトまたはその他のいずれであっても、同じサイズで表されます。

Visual C++デバッガーはUNICODEテキストを表示しますか?

はい、テキストバッファがLPWSTRタイプまたはUNICODEをサポートするその他のタイプの場合、Visual Studio 2005以降では、デバッガーウォッチウィンドウに国際テキストを表示できます(提供されているフォントと言語パックはもちろんインストールされています)。

まとめ:

C++は、エンコードを使用してUnicode文字を格納しませんが、文字列内の各文字のUNICODEコードポイントを直接格納します。望ましい言語(大まかに言えば)の最大の文字を保持するのに十分な大きさの文字サイズを選択する必要があり、その文字サイズは固定され、文字列内のすべての文字に使用されます。

現時点では、関心のあるほとんどの言語を表すには2バイトで十分です。これが、コードポイントを表すために2バイトが使用される理由です。将来、それらと通信したい新しいフレンドリースペースコロニーが発見された場合、新しいUnicodeコードピオントを言語に割り当て、より大きな文字サイズを使用してこれらの文字列を格納する必要があります。

0
zar