マルチバイト文字とワイド文字

アバター
MoNoQLoREATOR
記事: 284
登録日時: 14年前
住所: 東京

マルチバイト文字とワイド文字

投稿記事 by MoNoQLoREATOR » 12年前

マルチバイト文字とワイド文字、これはC/C++プログラマーにとって永遠のテーマである…!ゴゴゴゴゴゴゴゴゴゴ
というわけで、現在関数やクラスを、A/Wのどちらが指定されても大丈夫なものに書き換えています。
現在はこんな感じです。(クリックと同時にダウンロードが始まります)

できるだけコンパイラの種類に依存しないものを目指そうと考えていたのですが…
まず
#pragma once
を使用している時点でアウトなのではないか!?と気づいたわけです。
これは、実装しているコンパイラが多いだけで、C/C++で規格されているわけではないという話を聞いたことがあります。
そして、
_w
で始まる標準関数(らしきもの)はもしかして規格外ですか?そんな気がします。
そもそもWin32APIを取り扱うライブラリは標準ライブラリではないのだからそれを使用している時点でアウトなのでは!?
などと、どんどん疑心暗鬼に…。

また、プロジェクト単位であらかじめどちらの文字セットを使用するかを設定しておく方式ではなく、「どちらの文字セットで指定しても良い」というものを目指していたのですが、はたしてこの方針でうまくやっていけるのか?という不安が湧いてきました。

ところで、(A系とW系、一つに絞るなら)A系のAPIには制限があるからW系を採用するべきだと以前YuOさんから教えて頂いたのですが、この制限とは?文字数?しかし文字セットの違いによって文字数制限があったりなかったりするというのはどうも腑に落ちません。表現できる文字に制限があるという意味なら、可変長のAよりも固定長のWの方が制限されるはずですし…。

もうVisualStudio限定にしてしまいましょうかね…。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前

Re: マルチバイト文字とワイド文字

投稿記事 by softya(ソフト屋) » 12年前

文字というのは文字種です。
文字の種類はUNICODEが多いので、UNICODEで書いてしまうとマルチバイト文字でちゃんと表示されない可能性があるのです。

ISLe
記事: 2650
登録日時: 14年前

Re: マルチバイト文字とワイド文字

投稿記事 by ISLe » 12年前

A/Wを単純に文字コードと結び付けてしまうのは危険ですよね。
例えばUTF-8はマルチバイト文字ですよ。

文字セットの違いで表現できる文字に制限があるというのは、マルチバイト文字かワイド文字かの違いではなく、コードページの話ですよね。
文字コードと割り当てられているイメージの対応表の問題かと。
なので、Aで表示できるがWで表示できない文字、Wで表示できるがAで表示できない文字、両方出てきます。

(追記)
A/Wの切り替えに対応するだけならTCHARを使えば良いのでは?
char→wchar_t,wchar_t→charの変換とか考えてます?
最後に編集したユーザー ISLe on 2013年5月21日(火) 17:28 [ 編集 1 回目 ]

YuO
記事: 947
登録日時: 14年前

Re: マルチバイト文字とワイド文字

投稿記事 by YuO » 12年前

MoNoQLoREATOR さんが書きました:#pragma once
を使用している時点でアウトなのではないか!?と気づいたわけです。
これは、実装しているコンパイラが多いだけで、C/C++で規格されているわけではないという話を聞いたことがあります。
#pragmaで規格にあるのは,
  • STDC FP_CONTRACT
  • STDC FENV_ACCESS
  • STDC CX_LIMITED_RANGE
のみです。
MoNoQLoREATOR さんが書きました:_w
で始まる標準関数(らしきもの)はもしかして規格外ですか?そんな気がします。
Visual C++における_から始まる関数は,原則的に非標準です。
# 全部を調べたわけでは無いので,「原則的に」。
MoNoQLoREATOR さんが書きました:ところで、(A系とW系、一つに絞るなら)A系のAPIには制限があるからW系を採用するべきだと以前YuOさんから教えて頂いたのですが、この制限とは?文字数?しかし文字セットの違いによって文字数制限があったりなかったりするというのはどうも腑に落ちません。表現できる文字に制限があるという意味なら、可変長のAよりも固定長のWの方が制限されるはずですし…。
エンコーディングで表現可能な文字の集合 (!= 文字集合,符号化文字集合) の違いです。
Windowsにおいて,A系は現在のコードページによって定義されるエンコーディングが使われます。
それに対して,W系はUTF-16がエンコーディングとして使われます。

ここで,Windowsが持っている文字集合 (兼符号化文字集合) はUnicodeであるため,W系のAPIでは全ての文字を表現することが出来ます。
それに対して,A系のAPIは,必ずUnicodeの部分集合である文字集合しか取り扱うことが出来ません。
# 部分集合は自身を含みます。

ちなみに,Unicodeは一文字を表現するのに複数のコードポイントを必要とすることがある,可変長前提の文字集合です。
探せばCOMBINEDなんたら,という名前の文字がたくさん見つかりますし,異体字のための仕組みもあります。
この結合文字のせいで,正規化という話が出てきて,NFDでファイル名を扱うMac OS Xから濁点入りファイルをメールに添付して送ると文字化けする原因になったりします。
# が (U+304C) と,が (U+304B U+3099)など。単体の濁点゛はU+309B。←プレビューしたらNFCに正規化されたっぽいので,記事上は結合用濁点が「か」に結合してしまっているかもしれません。
ISLe さんが書きました:A/Wを単純に文字コードと結び付けてしまうのは危険ですよね。
文字コードというか,エンコーディングとそのままダイレクトに結びつきますよ。
ISLe さんが書きました:文字セットの違いで表現できる文字に制限があるというのは、マルチバイト文字かワイド文字かの違いではなく、コードページの話ですよね。
文字コードと割り当てられているイメージの対応表の問題かと。
なので、Aで表示できるがWで表示できない文字、Wで表示できるがAで表示できない文字、両方出てきます。
コードページはエンコーディングにダイレクトに結びつきます。
Code Pages Supported by Windowsなどを見ても,明らかに符号化文字集合の話ではなく,特定のエンコーディングにおける符号位置を示しています。
さらに,先ほども書きましたが,Windowsが持つ文字集合はUnicodeで,Unicodeのコードポイントをそのまま使って符号化文字集合にしています。
CP932であれば,文字集合や符号化文字集合はUnicodeで,エンコーディングがWindows-31jになります。
よって,A系のAPIで利用できるがW系のAPIで利用できない文字というのは原理上ありえません。

もちろん,Cの標準規格上の話として,wchar_tは「現在のロケールにおける」拡張文字集合のすべての文字を一意に表せる型であって,UCS-2とは限らないというのはありますが,それはまた別の話です。
# というか,wchar_tでUTF-16を使うのは定義に反するような……。

アバター
h2so5
副管理人
記事: 2212
登録日時: 14年前

Re: マルチバイト文字とワイド文字

投稿記事 by h2so5 » 12年前

文字集合(コードページ)と符号化方式(エンコーディング)の関係は少々ややこしいですよね。

お店に商品を注文することを例えに出してみると、文字集合というのは店の品揃えです。
日本語の文字しか使えないものもあれば(Shift_JIS)、様々な文字列を扱える店(Unicode)もあります。
一方、符号化方式は梱包の方法です。
大きな商品を小分けにして送ることもあれば(Shift_JIS, UTF-8)、大きなダンボールで丸ごと送ることもあります(UTF-32)。

注)例えなので正確ではないです。イメージとしてそんな感じだと思ってください。
最後に編集したユーザー h2so5 on 2013年5月21日(火) 21:11 [ 編集 1 回目 ]

ISLe
記事: 2650
登録日時: 14年前

Re: マルチバイト文字とワイド文字

投稿記事 by ISLe » 12年前

VisualStudio限定にするかどうかって書いてあるのでウィンドウズのエンコーディング前提ではないのかなとか思ったんですよね。
表現・表示できるかどうかではなくて、処理できるかどうかで考えてしまいました。

追記したようにTCHAR使えば何も考えなくて良い話なんですかね。
だとしたら何を悩んでいるのか分かりませんが。

(追記)
よく考えたらA/Wで分かれているのって文字列入出力関係だけでしたっけ。
どうやら勘違いしてたようです。
そしてMoNoQLoREATORさんが何に悩んでいるのかさっぱり分かりません。
最後に編集したユーザー ISLe on 2013年5月21日(火) 23:26 [ 編集 4 回目 ]

アバター
MoNoQLoREATOR
記事: 284
登録日時: 14年前
住所: 東京

Re: マルチバイト文字とワイド文字

投稿記事 by MoNoQLoREATOR » 12年前

とりあえず知識不足だということがわかったので勉強してきます。

とりあえず、プロジェクト全体でどんな文字セットが設定されているかに関わらず、文字列を指定すべき場所にマルチバイト文字を指定しようがワイド文字を指定しようが構わないというものを作ろうと思っていたのですが…これはあまり意味がないですね。
1つのプロジェクト中でマルチバイト文字とワイド文字を無闇やたらに入り乱れさせる利点も必要性もないわけですし、もっと言ってしまえば、「通常はワイド文字を使用し、ファイル等に保存する時だけマルチバイト文字を使う」というのが最もよい使い分け方なのでは?
マルチバイト文字に表現できない文字もワイド文字なら表現できてその逆が無いというのであれば積極的にワイド文字を使用してゆくべきだと思いますし、ファイル等に保存する際マルチバイト文字を使用すれば要領削減が期待できます。

多くの場所で、とりあえず「マルチバイト文字セットを使用する」という設定にしておきなさいという解説を聞きますが、あれは何故なのでしょうか?
ちなみにHALでもマルチバイト文字セットを使用する設定にしろと言われました。
DXライブラリの公式サイトにもそんなことが書かれていた記憶があります。

アバター
MoNoQLoREATOR
記事: 284
登録日時: 14年前
住所: 東京

Re: マルチバイト文字とワイド文字

投稿記事 by MoNoQLoREATOR » 12年前

>>MoNoQLoREATOR
などと意味不明な供述をしており…
とりあえずいろんなものがゴッチャゴチャになってますね。
そもそもVisualStudioの設定が「マルチバイト文字セットを使用する」と「Unicode文字セットを使用する」の2択であることに違和感が…。
単純に可変長ですか?固定長ですか?って訊いてるわけではないということですかね。
とりあえず調べてきてから発言する必要がありますね私は。

ISLe
記事: 2650
登録日時: 14年前

Re: マルチバイト文字とワイド文字

投稿記事 by ISLe » 12年前

MoNoQLoREATOR さんが書きました:多くの場所で、とりあえず「マルチバイト文字セットを使用する」という設定にしておきなさいという解説を聞きますが、あれは何故なのでしょうか?
単に
"文字列"
と書いた場合、それはcharの配列なので型が違いますとコンパイラに怒られるからです。
文字=charだと思っているひとはすべからくコンパイルエラーに面食らうことになります。

YuO
記事: 947
登録日時: 14年前

Re: マルチバイト文字とワイド文字

投稿記事 by YuO » 12年前

MoNoQLoREATOR さんが書きました:とりあえず、プロジェクト全体でどんな文字セットが設定されているかに関わらず、文字列を指定すべき場所にマルチバイト文字を指定しようがワイド文字を指定しようが構わないというものを作ろうと思っていたのですが…これはあまり意味がないですね。
C++ならばLP(C)STRとLP(C)WSTRでオーバーロードします。
Win32 API形式ならばLP(C)STRをとるA系APIとLP(C)WSTRをとるW系APIを用意し,#ifdef UNICODEで切り分けます。さらに,使う側はTEXTマクロやLP(C)TSTRを積極的に使います。

ライブラリのユーザーがプロジェクトでUNICODEが定義されているか否かを気にせずに使えるように,というのであれば上記の方法になります。
MoNoQLoREATOR さんが書きました:1つのプロジェクト中でマルチバイト文字とワイド文字を無闇やたらに入り乱れさせる利点も必要性もないわけですし、もっと言ってしまえば、「通常はワイド文字を使用し、ファイル等に保存する時だけマルチバイト文字を使う」というのが最もよい使い分け方なのでは?
マルチバイト文字に表現できない文字もワイド文字なら表現できてその逆が無いというのであれば積極的にワイド文字を使用してゆくべきだと思いますし、ファイル等に保存する際マルチバイト文字を使用すれば要領削減が期待できます。
Windows 95系のOSで動かす場合を除けば,今時A系のAPIを使う利点はありません。
ただし,入力がユーザーデフォルトCPで,出力もユーザーデフォルトCPの場合,わざわざ内部でコード変換する必要性に欠けるため,char *とA系APIを使う,という選択肢もあるでしょう。
また,wchar_t *を受け取らないライブラリもたくさんあります。そのため,char *側で処理を行うこともありえます。
MoNoQLoREATOR さんが書きました:多くの場所で、とりあえず「マルチバイト文字セットを使用する」という設定にしておきなさいという解説を聞きますが、あれは何故なのでしょうか?
ちなみにHALでもマルチバイト文字セットを使用する設定にしろと言われました。
DXライブラリの公式サイトにもそんなことが書かれていた記憶があります。
ワイド文字列に馴染みが無いからでは。
wprintfやstd::wcoutのような標準のワイド文字列I/Oではsetlocale等のロケール設定が必要になりますし,fopenやstd::fstreamのファイル名にはchar *しか使えません。
さらに,fwprintfやstd::fstreamの文字列の入出力は,char *に変換して出力され,char *からwchar_t *に変換されて読み取られます。
つまり,ファイルに対する文字列の入出力は基本的にchar系としての入出力しかできません。
ロケールは処理系依存なので,初心者に教えるには難しい物になります。
中には,Open Watcomの初期(現在は不明)のようにロケールがまともに実装されておらず,日本語が使い物にならなかったこともありました。
なので,入門書にワイド文字(列)について書かれていることは皆無です。

解説で「なぜ」を説明できないなら,本来はその解説は当てにならないと言うべきところです。
LPTSTR/LPCTSTR/TCHAR各型やTEXTマクロを使うだけの話なので,こちらを説明するのが本筋ですから。
# LPTSTRを盛大に誤解して解説している本を見たこともあります (2000年ころだったかと) が……。

DXライブラリは……なんででしょうね。
DxDirectX.hの一部にTCHAR *ではなくCHAR *のみの関数 (TCHAR *がコメントアウトされている) があるのですが,
DxLib.hが読み込むライブラリを決定する時にはUNICODEの有無でライブラリを切り替えています。
これはこれでリスクがある方法ではありますが……。
ソースを見ても,問題になるとは思えないのですが……。
MoNoQLoREATOR さんが書きました:そもそもVisualStudioの設定が「マルチバイト文字セットを使用する」と「Unicode文字セットを使用する」の2択であることに違和感が…。
単純に可変長ですか?固定長ですか?って訊いてるわけではないということですかね。
UNICODEと_UNICODEを定義「しない(=マルチバイト文字セットを使う=A系APIを使う)」のか,「する(=Unicode文字セットを使う=W系APIを使う)」のか,の選択です。
つまり,/D:UNICODE /D:_UNICODEコンパイラオプションの設定の問題です。
ISLe さんが書きました:よく考えたらA/Wで分かれているのって文字列入出力関係だけでしたっけ。
基本的には,引数に文字列があるものだけです。
実際には,SendMessageGetMessageなどもA/Wの区別があります。
ただ,これらは対象メッセージと対象ウィンドウによって変換処理が入る場合のあるものだったりもするため (IsWindowUnicode参照),分離しているいるのだと思います。
# とはいえ,GetMessageA/Wはなぜだろう,と思わなくも無いです。SendMessageはWM_TEXTなどがありますが……。

naohiro19
記事: 256
登録日時: 14年前

Re: マルチバイト文字とワイド文字

投稿記事 by naohiro19 » 12年前

Win32APIではマルチバイト用(MessageBoxA)とワイド文字列用(MessageBoxW)の2つが用意してありますね。
「Unicode文字列を使用する」がプロジェクトで選択されているならWがついたほうを呼び出します。またマルチバイトはAがついたほうを呼び出します。