C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[10]: 文字コードの取得


(過去ログ 96 を表示中)

[トピック内 16 記事 (1 - 16 表示)]  << 0 >>

■57162 / inTopicNo.1)  文字コードの取得
  
□投稿者/ TAKE (16回)-(2011/02/16(Wed) 09:55:20)

分類:[C#] 

日本語文字コードの取得はやはり簡単にはいかず難しいのでしょうか?

(例)
あいうえお → Shift_JIS
あいうえお → utf-8


引用返信 編集キー/
■57163 / inTopicNo.2)  Re[1]: 文字コードの取得
□投稿者/ shu (438回)-(2011/02/16(Wed) 09:58:15)
No57162 (TAKE さん) に返信
> 日本語文字コードの取得はやはり簡単にはいかず難しいのでしょうか?
>
> (例)
> あいうえお → Shift_JIS
> あいうえお → utf-8
>
>
Encoding.GetBytes ?

引用返信 編集キー/
■57165 / inTopicNo.3)  Re[1]: 文字コードの取得
□投稿者/ 魔界の仮面弁士 (2064回)-(2011/02/16(Wed) 10:50:24)
No57162 (TAKE さん) に返信
> あいうえお → Shift_JIS
> あいうえお → utf-8

あいうえお → Shift_JIS → 82,A0,82,A2,82,A4,82,A6,82,A8
あいうえお →   utf-8   → E3,81,82,E3,81,84,E3,81,86,E3,81,88,E3,81,8A

の事だとすれば、shu さんが回答されているように、Encoding.GetBytes を使うだけで OK です。

  byte[] sjis = Encoding.GetEncoding("Shift_JIS").GetBytes("あいうえお");
  byte[] utf8 = Encoding.UTF8.GetBytes("あいうえお");
  Console.WriteLine("sjis:" + BitConverter.ToString(sjis));
  Console.WriteLine("utf8:" + BitConverter.ToString(utf8));



> 日本語文字コードの取得はやはり簡単にはいかず難しいのでしょうか?

「文字コード不明のテキストファイルを読み込む」場合のように、
バイナリからのデコード処理の事を指しているのだとすれば、
ある程度の推測はできますが…確定されないケースもあります。

static void Main()
{
    byte[] bin = { 0xe0, 0xdd, 0xe0, 0xea, };
    foreach (var x in Conjecture(bin))
    {
        Console.WriteLine("codepage={0, 6}, [{1}]", x.Key, x.Value);
    }
    Console.ReadKey();
}

public static IEnumerable<KeyValuePair<int, string>> Conjecture(byte[] bin)
{
    int[] code = {
        0,      // システム既定値
        932,    // Shift_JIS
        1200,   // UTF-16
        1201,   // UTF-16BE
        10001,  // MacJapanse
        20290,  // IBM EBCDIC/日本語カタカナ
        20932,  // JIS 0208-1990 and 0212-1990
        50220,  // ISO-2022-JP
        50221,  // JIS 1バイトカナ含む
        50222,  // JIS 1バイトカナ含む(SO/SI)
        51932,  // EUC-JP
        65000,  // UTF-7
        65001,  // UTF-8
    };

    // いろいろなコードページを試してみて、
    // 変換エラーが発生しなかったものだけを返す
    EncoderFallback ef = new EncoderExceptionFallback();
    DecoderFallback df = new DecoderExceptionFallback();
    foreach (int cp in code)
    {
        string text;
        try
        {
            text = Encoding.GetEncoding(cp, ef, df).GetString(bin);
        }
        catch (DecoderFallbackException ex)
        {
            System.Diagnostics.Debug.WriteLine(cp + ":" + ex.Message);
            text = null;
        }
        if (text != null)
        {
            yield return new KeyValuePair<int, string>(cp, text);
        }
    }
}

引用返信 編集キー/
■57168 / inTopicNo.4)  Re[2]: 文字コードの取得
□投稿者/ TAKE (18回)-(2011/02/16(Wed) 13:29:43)
求めていたのは後述の
「文字コード不明のテキストファイルを読み込む」場合でして、

Encoding.GetEncoding("Shift_JIS").GetBytes("あいうえお");

で、Shift_JISにエンコード後、バイト列になりますが
用が済んで戻す時に本来の文字コードが不明のため
再エンコードできないため、取得できないものかと思ったところです。






引用返信 編集キー/
■57169 / inTopicNo.5)  Re[3]: 文字コードの取得
□投稿者/ Hongliang (758回)-(2011/02/16(Wed) 13:49:33)
> で、Shift_JISにエンコード後、バイト列になりますが
> 用が済んで戻す時に本来の文字コードが不明のため
> 再エンコードできないため、取得できないものかと思ったところです。

戻すって、.NET の String にですか?
それって Shift_JIS でデコード(GetString)するだけの話じゃないですか?
引用返信 編集キー/
■57170 / inTopicNo.6)  Re[3]: 文字コードの取得
□投稿者/ 魔界の仮面弁士 (2065回)-(2011/02/16(Wed) 14:02:31)
No57168 (TAKE さん) に返信
> Encoding.GetEncoding("Shift_JIS").GetBytes("あいうえお");
> で、Shift_JISにエンコード後、バイト列になりますが
> 用が済んで戻す時に本来の文字コードが不明のため
自分でエンコード(符号化)したデータという事でしょうか?
であれば、Encoding 情報もどこかに保持しておく事はできないのでしょうか。

たとえば XML テキストなら <?xml version="1.0" encoding="Shift_JIS"?> を
埋め込んでおくという手法が使えます。

プレーンテキストの場合はそうもいきませんが、たとえばそのファイルを
 ・Shift_JIS
 ・UTF-8(BOM 付)
 ・UTF-16(BOM 付)
のいずれかに限定する事ができるようなケースでは、最初の数バイトを見て判断できます。


> 再エンコードできないため、
デコードするのではなく、
エンコードしなおす、という事ですか?


> 取得できないものかと思ったところです。
とりあえず、先述の方法などをつかって「推測」することはできます。
ただし、複数の解釈が可能なデータの場合が問題になってきますね。

"+MEIwRDBG-" という文字列は、Shift_JIS では 2B,4D,45,49,77,52,44,42,47,2D です。
  "あいう"   という文字列は、  UTF-7   では 2B,4D,45,49,77,52,44,42,47,2D です。

    "ABC"    という文字列は、Shift_JIS では 41,42,43 です。
    "ABC"    という文字列は、  UTF-8   では 41,42,43 または EF,BB,BF,41,42,43 です。

   "珎瑕"    という文字列は、Shift_JIS では E0,DD,E0,EA です。
   "珈琲"    という文字列は、  EUC-JP  では E0,DD,E0,EA です。

   "珈琲"    という文字列は、Shift_JIS では E0,DB,E0,E8 です。
   "獻琥"    という文字列は、  EUC-JP  では E0,DB,E0,E8 です。


人間であれば、復元された文字列の内容を見て成否を判断できますが、
そのようなファジーな処理をプログラムに組み込むことは困難でしょう。

引用返信 編集キー/
■57171 / inTopicNo.7)  Re[3]: 文字コードの取得
□投稿者/ shu (439回)-(2011/02/16(Wed) 14:19:47)
No57168 (TAKE さん) に返信

> 用が済んで戻す時

この意味がよく分かりません。
一連の流れであるなら元の文字列を保存しておけばいいだけでは?

処理終了後、別のアクションで読み直すのなら魔界の仮面弁士さんの書いている通りですね。
バイナリデータで保存している場合、自分以外による読み書きがないなら先頭にコードページ(Encoding.CodePage)でも
書いておくとかどうでしょう?
引用返信 編集キー/
■57172 / inTopicNo.8)  Re[4]: 文字コードの取得
□投稿者/ TAKE (19回)-(2011/02/16(Wed) 14:58:35)
ASP.NETなので
web.configから文字コード情報は取得はできると思いますが
もっと直接的に文字コードが判明できればよいかなっと思いまして。


ちなみに
用というのは
末尾に半角のスペースを加えている処理をしています。
入力文字列+スペース=500バイトになるようにスペースの数を末尾に加えています。


>"+MEIwRDBG-" という文字列は、Shift_JIS では 2B,4D,45,49,77,52,44,42,47,2D です。
> "あいう" という文字列は、 UTF-7 では 2B,4D,45,49,77,52,44,42,47,2D です。

> "ABC" という文字列は、Shift_JIS では 41,42,43 です。
> "ABC" という文字列は、 UTF-8 では 41,42,43 または EF,BB,BF,41,42,43 です。

> "珎瑕" という文字列は、Shift_JIS では E0,DD,E0,EA です。
> "珈琲" という文字列は、 EUC-JP では E0,DD,E0,EA です。

> "珈琲" という文字列は、Shift_JIS では E0,DB,E0,E8 です。
> "獻琥" という文字列は、 EUC-JP では E0,DB,E0,E8 です。

文字コードはダブってしまうんですね・・・。

引用返信 編集キー/
■57174 / inTopicNo.9)  Re[5]: 文字コードの取得
□投稿者/ TAKE (20回)-(2011/02/16(Wed) 15:11:11)
バイト数文字数だけ取得すれば、
デコードする必要なかったですね・・・。
引用返信 編集キー/
■57180 / inTopicNo.10)  Re[6]: 文字コードの取得
□投稿者/ TAKE (22回)-(2011/02/16(Wed) 15:57:00)
文字コードについて勉強になりました。
ありがとうございました。
解決済み
引用返信 編集キー/
■57191 / inTopicNo.11)  Re[6]: 文字コードの取得
□投稿者/ 魔界の仮面弁士 (2067回)-(2011/02/16(Wed) 19:03:00)
No57172 (TAKE さん) に返信
> ASP.NETなので
> web.configから文字コード情報は取得はできると思いますが
データベース等を使っている場合には、DB 側の文字集合も影響する事があります。


■No57174 (TAKE さん) に返信
> バイト数文字数だけ取得すれば、
> デコードする必要なかったですね・・・。
ケースバイケースだとは思いますが、そういう状況もあるかもしれません。


ただし、符号化に使用されるバイト数は一定では無いので注意してください。

「あ」の文字は、Shift_JIS では 2 バイト、UTF-16 では 2 バイト、UTF-8 では 3 バイトです。
「α」の文字は、Shift_JIS では 2 バイト、UTF-16 では 2 バイト、UTF-8 では 2 バイトです。
「A」 の文字は、Shift_JIS では 1 バイト、UTF-16 では 2 バイト、UTF-8 では 1 バイトです。


また、文字の並び順によってバイト数が変わるケースもあります。

  Encoding enc = Encoding.GetEncoding("iso-2022-jp");

  Console.WriteLine(enc.GetByteCount("あaいbうcえ"));     //35バイト
  Console.WriteLine(enc.GetByteCount("あいうえabc"));     //17バイト

解決済み
引用返信 編集キー/
■57202 / inTopicNo.12)  Re[7]: 文字コードの取得
□投稿者/ TAKE (23回)-(2011/02/17(Thu) 10:42:38)
Shift_JIS以外では
全角→2バイト
半角→1バイト
と正確に取得できないようなので
エンコードに使用するのはShift_JIS固定がよいのかもしれませんね。

引用返信 編集キー/
■57203 / inTopicNo.13)  Re[8]: 文字コードの取得
□投稿者/ shu (450回)-(2011/02/17(Thu) 10:53:34)
No57202 (TAKE さん) に返信
> Shift_JIS以外では
> 全角→2バイト
> 半角→1バイト
> と正確に取得できないようなので
> エンコードに使用するのはShift_JIS固定がよいのかもしれませんね。

スレッドを分散して同じようなことを書かれているようなので話がまとまりにくいかと思います。

全角のバイト数、半角のバイト数が固定しないと困る処理をされているのでしょうか?
もし文字幅計測とかであれば、Graphics.MeasureStringなどを使用して実際に計測したほうがよいかと思います。
シフトJISの範囲の文字しか使用しないのであれば、考えられている方法でもよいかと思います。
引用返信 編集キー/
■57206 / inTopicNo.14)  Re[8]: 文字コードの取得
□投稿者/ 魔界の仮面弁士 (2068回)-(2011/02/17(Thu) 11:42:57)
No57202 (TAKE さん) に返信
> Shift_JIS以外では
> 全角→2バイト
> 半角→1バイト
> と正確に取得できないようなので

Shift_JIS だとバイト数で簡易判定できますが、Unicode においては、
EastAsianWidth.txt という定義があります。
http://ja.wikipedia.org/wiki/%E6%9D%B1%E3%82%A2%E3%82%B8%E3%82%A2%E3%81%AE%E6%96%87%E5%AD%97%E5%B9%85
http://unicode.org/Public/UNIDATA/EastAsianWidth.txt
http://www.unicode.org/reports/tr11/

ただし印刷幅などの話であれば、使用するフォントによって幅が異なりますので、
shu さんが書かれているように、実際に測定して判断する必要があるでしょう。


> エンコードに使用するのはShift_JIS固定がよいのかもしれませんね。
文字コードはそれで良いとして、文字集合については大丈夫ですか?

たとえば「立方メートル記号」などは、Unicode では扱えますが Shift_JIS では表現できません。
HTML として表示する分には、実体参照として出力されることで Shift_JIS のサイトでも
その文字を正しく出力できますが、問題は入力側です。

Web サイト上から文字列を入力してもらった場合、HTML 側の文字コードが何であれ、
Shift_JIS 範囲外の文字が渡されてくる可能性があります(特に人名や住所)。
その場合、どのように振る舞うのかを決めておく必要があるかも知れません。

(1) Shift_JIS で符号化できない文字が含まれているかどうかを判定して、
 ユーザーに再入力を促すようにする。

(2) Shift_JIS で扱えない文字は削除して、残りの文字列を処理する。

(3) Shift_JIS で扱えない文字を、別の文字("?" あるいは "?" など)に
 置換しておいてから処理する。
引用返信 編集キー/
■57337 / inTopicNo.15)  Re[9]: 文字コードの取得
□投稿者/ TAKE (32回)-(2011/02/23(Wed) 09:13:21)
文字集合とは↓これでしょうか?
u

VBの時代には
unicodeに変換すれば
簡単に文字数判定できましたが
C#は簡単にはいかないようですね。
解決済み
引用返信 編集キー/
■57345 / inTopicNo.16)  Re[10]: 文字コードの取得
□投稿者/ 魔界の仮面弁士 (2087回)-(2011/02/23(Wed) 12:16:20)
No57337 (TAKE さん) に返信
> 文字集合とは↓これでしょうか?
文字集合(キャラクターセット)とは、使用できる文字の集まりです。
大雑把にいえば、使用可能な文字の一覧表だと思っていただければ良いかと。

その上で、それらの文字をどのような符号化方式(エンコード)でバイナリ変換するのか、という話です。

ASP.NET では UTF-8 が使われる事が多いかと思いますが(ケータイ向けサイトは別として)、
その上で、システム上の制限として使えない文字種を定義する必要があるかどうかですね。

・どんな文字を使う必要があるのか。
 (外国人名を現地語で入力させる必要があるか、サロゲートペアは許可するか、外字はどうするかなど)

・それらを何の符号化で保持/出力するのか。
 (DB の文字コードは何か、Shift_JIS データの作成が必要かなど)


実際にはこの他、どのフォントを使うかという話もあるのでしょうけれども、
システムの全体要件が見えないので、それについては触れずにおきます。
そもそもの話は「文字コード不明のテキストファイルを読み込む」手法についての
話題だったはずなので、かなり脱線気味ではありますが。


脱線ついでに文字集合と符号化方式について述べておくと、たとえば Unicode という文字集合の場合、
そのひとつの文字集合に対して UTF-8 や UTF-16 といった異なる符号化方式が存在しています。

文字集合としての Unicode については、Unicode 2.1 とか Unicode 4.0.0 といった
バージョンの違いで扱える文字種が異なります。6.0 以降では、"emoji symbols"
(いわゆる、携帯電話の絵文字記号)も追加されています。

対応する符号化方式については、UTF-8 とか UTF-16 とか UTF-32 など何種類かありますが、
とりあえず「あ」の文字を例にとると、この文字は Unicode では U+3042 に割り当てられており、
その U+3042 を符号化してバイナリ表現に変換した場合、符号化方式によって
  UTF-7    では 2B,4D,45,49,2D
  UTF-8    では E3,81,82
  UTF-16LE では 42,30
  UTF-16BE では 30,42
のように、異なるバイナリ表現をとるようになっています。

# ちなみに、Windows が Unicode 系 API で採用しているのは UTF-16 であり、
# System.Text.Encoding.Unicode というのは、UTF-16 の符号化を表しています。


UTF-8 の符号化方式でいえば、U+0800〜U+FFFF の範囲の文字が
xxxxyyyy-yyzzzzzz のビットパターン(2進数表現)で表され、それが
1110xxxx,10yyyyyy,10zzzzzz というバイト並びに変換される仕様です。
上記 U+3042 なら、2進表現では 00110000-01000010 になるので、これが
11100011,10000001,10000010 のビットパターン、すなわち E3,81,82 になる、と。


> VBの時代には
> unicodeに変換すれば
実際には、その変換が問題になる事もあったりします。(NEC文字などの問題など)
Win9x系と WinNT 系でも差異がありましたし。まぁ、普段はあまり意識する事は無いと思いますが。

旧VB の文字列は、内部的には UCS-2 相当なのですが、画面やファイルへの入出力処理では
ローカルのOEMコードに自動的に変換されるため、これが問題を引き起こすことがありました。

たとえば、Shift_JIS (というか、コードページ932)の場合、
"≒" の文字は、0x8790 と 0x81e0 の二か所に割り当てられていますが、
Unicode では U+2252 一か所にのみマッピングされています。

そのため、VB6 や VBA においては
 s1 = ChrW(&H2252)
 s2 = Chr(&H81E0)
 s3 = Chr(&H8790)
 s4 = StrConv(ChrB(&H81) & ChrB(&HE0), vbUnicode, &H411)
 s5 = StrConv(ChrB(&H87) & ChrB(&H90), vbUnicode, &H411)
はいずれも、0x2252 という同一バイナリにエンコードされることになります。
ゆえに一度変換してしまうと、厳密には元に戻すことができません。

この事情は、C# においても同じことです。.NET にしても 32bit版 VB にしても、
文字列(String 型変数)は、文字列を UTF-16 のバイナリで管理しているため、
0x8790 な 2 バイトのテキストファイルと
0x81e0 な 2 バイトのテキストファイルを Shift_JIS としてデコードした場合、
その文字はいずれも U+2252 という同じ文字として保持される事になります。


TAKE さんが先程あげられた
> 文字集合とは↓これでしょうか?
で表された U+2116 "Numero Sign" の文字も同様で、この文字は
Shift_JIS では 0xfa59 と 0x8782 の 2 つがあったりします。


> 簡単に文字数判定できましたが
> C#は簡単にはいかないようですね。
『文字数判定』なら、System.Globalization.StringInfo クラスの
LengthInTextElements プロパティで行えます。
http://www.atmarkit.co.jp/fdotnet/dotnettips/726countchars/countchars.html
http://msdn.microsoft.com/ja-jp/ff382735

『バイト数判定』なら、System.Text.Encoding クラスの GetByteCount メソッドですね。

『文字幅の測定』なら、No57203 で shu さんが書かれているように MeasureString メソッド等で。

解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -