WindowsXP+VB2005で開発をしています。
System.Text.Encoding.GetEncoding("Shift_JIS").GetByteCount(TEXT)
でTEXTのバイト長を求めていたのですが、
中国語に対応するため、TEXTに "你好"とか"时间"といった文字が入ってくることになりました。
上記の方法で求めても、当然ながら正確な値が戻ってきません。
一文字ずつ全/半角のチェックを行ってバイト長を求める事はできますが、
上記のように一度に求める方法はないでしょうか。
ご教授のほどよろしくお願いいたします。
※TEXTは全角/半角、中国語(簡体)/日本語が混在します。
申し訳ございません。
文字化けさせてしまいました。
"你好" > 中国語のこんにちは(ニイハオ)
"时间" > 中国語の時間
です。
よろしくお願いします。
ご存じの通り、Shift_JIS では簡体字は表現できません。
表現できない以上、Shift_JIS におけるバイト長というものは存在しません。
一体どういう基準での「バイト長」が必要なのでしょうか?
UTF-8? UTF-16? GB2312?
// DB の文字セットが Shift_JIS ってことになってたらどうしようもないですね。
早速の回答ありがとうございます。
勉強不足で申し訳ございません。
TextBoxで入力制限をかけたいのですが、今まで日本語で運用していた関係上、文字数でなく、バイト数。中国語も2バイトとして扱い(算出し)たいのですが。
そうなるとやはり、一文字ずつチェックするしかないのでしょうか?
> 中国語も2バイトとして扱い(算出し)たいのですが。
Hongliang さんも書かれていますが、その扱い方と言うのは、
どの「文字集合」と「文字コード」で算出した場合でしょうか?
また日本語に限ってみても、JIS-2004 の対応を考えねばなりません。
たとえば:
"a" の文字は、Shift_JIS/UTF-8/Big-5/GB2312 いずれも 1 バイト。
"β" の文字は、Shift_JIS/UTF-8/Big-5/GB2312 いずれも 2 バイト。
"好" の文字は、UTF-8 で 3 バイト、Shift_JIS/Big-5/GB2312 で 2 バイト。
ChrW(&H4F60)、いわゆる ニイハオ の {イ尓} は、
UTF-8 で 3 バイト、Big-5/GB2312 で 2 バイト、Shift_JIS では該当文字無し。
ChrW(&H3351)、いわゆる {リットル} は、
UTF-8 で 3 バイト、Shift_JIS で 2 バイト、Big-5/GB2312 で該当文字無し。
ホッケという魚を表す {魚+花} は、サロゲートペアが入って
UTF-8 で 4 バイト、Shift_JIS/Big-5/GB2312 で該当文字無し。
———という事になっています。
> 今まで日本語で運用していた関係上、
そのデータを、その後どのように使うのかを考慮してください。
入力されたデータを Shift_JIS で扱う処理系が控えているなら、
{イ尓}などの Shift_JIS の文字集合に無い文字は、弾かねばならないでしょう。
また、そのデータを UTF-8 や UTF-16 などで扱うのであれば、
日本語は 2 バイトでは無く、3 バイトになるはずです。
> そうなるとやはり、一文字ずつチェックするしかないのでしょうか?
扱えない文字を調べるためには、System.Text.Encoding.GetEncoding の引数に、
EncoderFallback/DecoderFallback を指定すれば OK です。
書き間違い。 m(_ _;)m
> また、そのデータを UTF-8 や UTF-16 などで扱うのであれば、
> 日本語は 2 バイトでは無く、3 バイトになるはずです。
すみません、UTF-8 で 3 バイト、UTF-16 で 2 バイトの間違いです。
(サロゲートペアは除く)
追加発言が遅くなり申し訳ございません。
たとえば印刷で漢字5文字分の領域を確保してあるとします。
印刷が漢字5文字分なので入力も漢字5文字分の入力制限をかけたいのですが、
日本語だけなら
System.Text.Encoding.GetEncoding("Shift_JIS").GetByteCount(TEXT)
でよかったのですが、中国語が混在する時の文字数入力制限はどうすればいいでしょうか。
見た目の問題といいましょうか、中国語の漢字も2バイトに扱ってくれるようなクラスまたは関数はないでしょうか。
ということを、お伺いしたかったのですが。
稚拙な説明で申し訳ございません。
データ保存等の問題はクリアしていますので問題ございません。
> 扱えない文字を調べるためには、System.Text.Encoding.GetEncoding の引数に、
> EncoderFallback/DecoderFallback を指定すれば OK です。
ありがとうございます。
頂いた回答はすごく勉強になりました。
文字の表示時の幅と,文字を特定エンコーディングで表現した場合のバイト数を混同していませんか。
・印刷時の文字の幅が問題なのであれば,Graphics.MeasureStringなどを使って幅を調べればよい
MSDN: Graphics.MeasureString メソッド (String, Font) (System.Drawing)
http://msdn.microsoft.com/ja-jp/library/6xe5hazb.aspx
・文字数が問題なのであれば,StringInfo.LengthInTextElementsを使って調べればよい
MSDN: StringInfo.LengthInTextElements プロパティ (System.Globalization)
http://msdn.microsoft.com/ja-jp/library/system.globalization.stringinfo.lengthintextelements.aspx
ということだと思いますが。
う〜ん。
理解していただけないみたいですね。
文字数でも、文字幅でもないです。
印刷する時は、その枠内に決められた大きさで、文字を収めるのが大切なので、
"123ABC" の時は 6(半角6文字だから)
"65漢字a"の時は 7(半角3文字+全角2文字だから)
という日本語の感覚で、中国語のときも
"时间12a"の時は 7(半角3文字+漢字2文字だから)
と、返ってくるようなクラスまたは関数を知りたかったのですが。
>印刷する時は、その枠内に決められた大きさで、文字を収める
文字幅でしょ?
> 印刷する時は、その枠内に決められた大きさで、文字を収めるのが大切なので、
つまり、その枠サイズこそが「文字列を納めるのに必要な幅」という事になりますよね。
どのように印刷しているのかにもよりますが、文字を収めきれない場合には
(1) 入りきらない部分は、文字の一部欠けさせ、領域部分だけに描画する。
(2) 入りきらない部分は、文字単位で切り落として、領域部分だけで描画する。
(3) 入りきらない場合には、「...」などの省略記号を併用して描画する。
(4) 領域外に溢れさせて描画する。
(5) 入りきれない場合は、文節単位で次行あるいは次頁に送り出して描画を続ける。
(6) 入りきれない場合は、文字単位で次行あるいは次頁に送り出して描画を続ける。
などなど、それぞれ状況に応じて幾つかの描画方法があるかと思います。
そして .NET の描画処理においては、TextRenderer.DrawText に
TextFormatFlags のビットフラグを指定する事で、限られた領域内に、
どのように描画するかを決定する事ができるようになっています。
一方、何らかの帳票ツールなどを使って印刷する場合には、ツール側に
収まりきれない部分の印刷方式を指定する機能(AutoSize や WordWarp など)が
装備されていないかをチェックした方が早いかも知れません。
> という日本語の感覚で、中国語のときも
まず、その考え方自体から改める必要があるかと思います。
日本語を出力するなら、日本語の感覚で良いですし、
中国語を出力するなら、中国語の感覚で良いですし、
ギリシャ語であれば、ギリシャ語の感覚で良いでしょう。
ただし今回のように、多言語対応のアプリとする場合には、
必ずしも以前と同じ考え方が通用するとは限りません。
たとえば、ギリシャ語の α、β、γ などの文字を例に出すと、これらは
日本語圏(Shift_JIS, EUC-JPなど)においては全角幅で表現される事が多いですが、
ギリシャ語圏(ISO 8859-7, Windows-1253など)では半角幅で表現されるべきものでしょう。
それゆえ Unicode コンソーシアムの EastAsianWidth.txt においては、
これらの文字の「文字幅プロパティ」には、半角でも全角でもなく、
"文脈依存"を表す Ambiguous(曖昧な文字幅) が与えられています。
それに Unicode には、「ゼロ幅文字」と呼ばれる文字も幾つか存在します。
また、改行文字、水平タブ文字、垂直タブ文字、著作権記号(マルC)、
ロゴ(マルR)、トレードマーク(TM)などのように、文字幅を単純には
決定し難い文字も存在するかと思います。それゆえに、印刷枠のサイズに拘るなら、
> "123ABC" の時は 6(半角6文字だから)
> "65漢字a"の時は 7(半角3文字+全角2文字だから)
> という日本語の感覚で、中国語のときも
といった曖昧な仕様のままではいられません。
今回のように複数言語にきちんと対応させるともなれば、それぞれの文字を
どのように扱うのかを、きちんと見つめなおす必要があるかと思います。
(そもそも出力に使うフォントが、多言語対応であるかという問題もありますしね)
(案1) どのフォントが選択されても対応できるよう、収まりきる文字幅を、
TextRenderer.MeasureText メソッドなどを用いて動的に算出する。
(案2) 文字数を調整するのではなく、TextFormatFlags などを用いて
描画処理側でクリッピング処理を行うことで対応する。
(案3) 特定の固定幅フォントに固定し、それぞれの非日本語圏の文字が、
いわゆる全角/半角のどちらに当たるのかを調べた後、それぞれの文字を
正規表現もしくは文字コード範囲にて調査し、全角/半角の判定を行う。
(案4) 使える文字種を制限し、CJK の漢字圏で扱われない文字の入力を
あらかじめ拒否しておくか、またはそれらの想定外の文字が現れた場合に、
一律で別の文字(たとえば、長さゼロの文字列)に置換して描画することで対応する。
色々とご教授頂きありがとうございました。
(案3) で対応しようと思います。
これに懲りず、またよろしくお願い致します。