トップ 差分 一覧 ソース 置換 検索 ヘルプ PDF RSS ログイン

文字コードの判定について

http://d.hatena.ne.jp/hnx8/20140824/1408844344

C#でテキストファイル文字コードを自動判定する

 アルゴリズム

  • ASCII制御コードのうち0x00-0x03、ないし0x7F(DEL)が出現した場合
    • 原則として非テキストファイルとみなす
    • ただしファイル先頭2バイトで0x00が登場した場合は、BOMなしUTF16の可能性を調べる
  • 非ASCIIコード(0x80以降)が出現しなかった場合
    • JISエスケープシーケンスがあればJIS、なければASCII
  • 非ASCIIコード(0x80以降)が出現した場合
    • 以下4種類の文字コードに該当するか、可能性を調査する
      • ANSI(CP1252)
      • BOMなしUTF8(CP65001)
      • EUC(CP51932、補助漢字使用時はCP20932相当)
      • ⇒登場するコード範囲の関係で(必ず0x80以上となる)、上記3種はまとめてチェック
      • ShiftJIS(CP932)
      • ⇒2バイト目が0x20-0x7EのASCIIになることもあるので、個別にチェック
    • 文字コード体系として明らかに妥当ではない場合、可能性なしとみなす
    • 半角文字(半角カナ)・全角文字が連続していれば可能性が高いとみなす
    • 逆に不連続であれば可能性は低いものとみなす
      • 誤判別を起こしやすい箇所は個々に配点をチューニング
      • (過去の誤判別事例などをフィードバックしています)
    • 全バイト走査完了後、可能性が高いものから順にテキスト取り出しを試みる
      • デコードエラーが発生しなければその文字コードで確定

 ソースコード抜粋:文字コード判別クラス・判別メソッド本体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
public class ReadJEnc
{
////////////////////////////////////////////////////////////////////////
// <ReadJEnc.cs> ReadJEnc 文字コード自動判別処理本体【抜粋】
//  Copyright (C) 2014 hnx8(H.Takahashi)
//  http://hp.vector.co.jp/authors/VA055804/
//
//  Released under the MIT license
//  http://opensource.org/licenses/mit-license.php
////////////////////////////////////////////////////////////////////////

/// <summary>バイナリと判定するDEL文字コード、兼、ASCII/非ASCIIの境界文字コード</summary>
const byte DEL = (byte)0x7F;
/// <summary>非テキストファイルと判定する制御文字コードの最大値</summary>
const byte BINARY = (byte)0x03; //0x01-0x07位の範囲で調整。0x08(BS)はTeraTerm等ログで出る。0x09(TAB)は普通にテキストで使う。0x03くらいにするのがよい。HNXgrepでは0x03を採用

//文字コード判別メソッド================================================

/// <summary>バイト配列を全走査し、文字コードを自動判別する</summary>
/// <param name="Bytes">判定対象のバイト配列</param>
/// <param name="Length">ファイルサイズ(バイト配列先頭からのデコード対象バイト数)</param>
/// <param name="Text">out 判別した文字コードにより取り出したテキスト文字列(非テキストならnull)</param>
/// <returns>文字コード判別結果(非テキストならnull)</returns>
public CharCode GetEncoding(byte[] Bytes, int Length, out string Text)
{
    byte b1 = (Length > 0) ? Bytes[0] : (byte)0; //汎用バイトデータ読み取り変数初期化
    byte b2;

    //【1】7bit文字コードの範囲の走査(ASCII判定/非ASCII文字開始位置把握)、およびUTF16N/JISチェック
    int jisScore = 0; //JIS用のエスケープシーケンスが登場したらカウントアップ
    int asciiEndPos = 0; //ループ変数、兼、非ASCII文字を初めて検出した位置
    while (b1 < DEL) //非ASCII文字が出現したらループ脱出:b1にはあらかじめ読み込み済
    {
        if (b1 <= BINARY)
        {   //バイナリ文字検出:先頭2バイトでの検出ならUTF16Nの可能性をチェック、否ならバイナリ確定
            CharCode ret = (asciiEndPos < 2 ? SeemsUTF16N(Bytes, Length) : null);
            if (ret != null && (Text = ret.GetString(Bytes, Length)) != null)
            {   //UTF16Nデコード成功:非テキスト文字混入チェック
                int i;
                for (i = -3; i <= BINARY; i++)
                {   //0xFFFD,0xFFFE,0xFFFF,0〜BINARY、DELが混入している場合は非テキストとみなす
                    if (Text.IndexOf((char)i, 0, Text.Length) != -1) { break; }
                }
                if (i > BINARY && Text.IndexOf((char)DEL, 0, Text.Length) == -1)
                {   //■UTF16N確定(非テキスト文字混入なし)
                    return ret;
                }
            }
            Text = null;
            return null; //■バイナリ確定
        }
        if (b1 == 0x1B && (b2 = JIS.isEscape(Bytes, Length, asciiEndPos)) > 0)
        {   //JISエスケープシーケンス検出時はカウント加算
            jisScore++;
            asciiEndPos += b2;
        }
        //次の文字へ
        if ((++asciiEndPos) >= Length)
        {   //全文字チェック完了:非ASCII文字未検出、JISもしくはASCII
            if (jisScore >= 2)
            {   //■JIS確定(詳細種別を判定する)
                return JIS.GetEncoding(Bytes, Length, out Text);
            }
            if (JIS.hasSOSI(Bytes, Length))
            {   //SO,SIによるエスケープを検出した場合【抜粋】
                if ((Text = CharCode.JIS50222.GetString(Bytes, Length)) != null)
                {   //■半角カナSOSIのみを使用したJISで確定
                    return CharCode.JIS50222;
                }
            }
            //■ASCII確定(ただしデコード失敗時はバイナリ)
            return ((Text = CharCode.ASCII.GetString(Bytes, Length)) != null) ? CharCode.ASCII : null;
        }
        b1 = Bytes[asciiEndPos];
    }

    //【2】非ASCII文字を含む範囲の走査、CP1252/UTF8/EUCチェック、JIS残チェック
    int cp1252Score = 0; //いずれも、可能性が否定されたらint.MinValueが設定される
    int utfScore = 0;
    int eucScore = 0;
    int sjisScore = 0;
    bool existsEUC0x8F = false; //EUC補助漢字を見つけたらtrueを設定

    for (int cp1252Pos = asciiEndPos; cp1252Pos < Length; ) //cp1252Posの加算はロジック途中で随時実施
    {
        if (b1 == DEL)
        {   //制御文字0x7F登場なら、ごくわずかなJISの可能性以外全消滅。JISの可能性を消しきれるか判定
            cp1252Score = int.MinValue;
            utfScore = int.MinValue;
            eucScore = int.MinValue;
            sjisScore = int.MinValue;
            if (jisScore == 0 || (cp1252Pos++) >= Length || (b1 = Bytes[cp1252Pos]) < 0x21 || b1 >= DEL)
            {   //JISエスケープ未出現 or ファイル末尾で2バイト目なし or 2バイト目が0x21-0x7E範囲外ならJISの可能性も否定
                Text = null;
                return null; //■バイナリ確定
            }
        }
        //CP1252チェック&0x80以上の文字範囲の把握(notAsciiStartPos〜cp1252Pos)。b1読込済
        int notAsciiStart = cp1252Pos;
        switch (cp1252Score)
        {
            case int.MinValue: //CP1252可能性否定済み、非ASCII文字のスキップのみ実施
                while (b1 > DEL && (++cp1252Pos) < Length) { b1 = Bytes[cp1252Pos]; }
                break;
            default: //CP1252可能性あり、定義外文字混入チェック&CP1252ポイント加算
                while (b1 > DEL)
                {   // 非CP1252チェック用定義(0x2001A002):未定義の81,8D,8F,90,9Dに対応するビットがON
                    //        FEDC BA98 7654 3210         FEDC BA98 7654 3210
                    //        ---- ---- ---- ----         ---- ---- ---- ----
                    // (0x9#) 0010 0000 0000 0001  (0x8#) 1010 0000 0000 0010
                    if (b1 <= 0x9D && (0x2001A002 & (1u << (b1 % 32))) != 0)
                    {   // CP1252未定義文字を検出、可能性消滅
                        cp1252Score = int.MinValue;
                        goto case int.MinValue; //非ASCII文字スキップへ
                    }
                    if ((++cp1252Pos) >= Length) { break; }
                    b1 = Bytes[cp1252Pos];
                }
                //非ASCII文字範囲終了、評価ポイント加算
                //1バイトのみ出現時(SJISよりもCP1252の可能性が高い)、SJIS漢字1文字目と同評価・SJISカナよりも高評価となるようポイント加算
                if (cp1252Pos == notAsciiStart + 1) { cp1252Score += 2; }
                else if (cp1252Pos == notAsciiStart + 2 && (b2 = Bytes[cp1252Pos - 1]) >= 0xC0)
                {   //2バイトのみ出現時、ダイアクリティカルマーク(発音記号等)つきアルファベットなら配点補正
                    if (b2 == (b2 = Bytes[cp1252Pos - 2])) { cp1252Score += 5; } //同一文字重ねはかなり特徴的(SJISカナより可能性高)
                    else if (b2 >= 0xC0)
                    {   //続きor直前のASCII文字がアルファベットっぽければ、SJISカナより可能性が高くなるよう補正                               
                        if (b1 > 0x40 || (notAsciiStart > 0 && Bytes[notAsciiStart - 1] > 0x40)) { cp1252Score += 5; }
                        else { cp1252Score += 3; } //どちらでもなければ、EUCよりは可能性高とする
                    }
                    else { cp1252Score++; } //否ならば低めの加算とする
                }
                else { cp1252Score++; } //いずれにも該当しなければやや低めの加算とする
                break;
        }
        //notAsciiStartPos〜cp1252Pos範囲のUTF8チェック
        if (utfScore >= 0)
        {
            bool prevIsKanji = false;
            for (int utfPos = notAsciiStart; utfPos < cp1252Pos; utfPos++)
            {
                b1 = Bytes[utfPos];
                //1バイト目・2バイト目(ともに0x80以上であることは確認済み)をチェック
                if (b1 < 0xC2 || (++utfPos) >= cp1252Pos || Bytes[utfPos] > 0xBF) { utfScore = int.MinValue; break; } //UTF8可能性消滅
                else if (b1 < 0xE0)
                {   //2バイト文字OK(半角文字とみなして評価)
                    if (prevIsKanji == false) { utfScore += 6; } else { utfScore += 2; prevIsKanji = false; }
                }
                //3バイト目(0x80以上であることは確認済み)をチェック
                else if ((++utfPos) >= cp1252Pos || Bytes[utfPos] > 0xBF) { utfScore = int.MinValue; break; } //UTF8可能性消滅
                else if (b1 < 0xF0)
                {   //3バイト文字OK(全角文字とみなして評価)
                    if (prevIsKanji == true) { utfScore += 8; } else { utfScore += 4; prevIsKanji = true; }
                }
                //4バイト目(0x80以上であることは確認済み)をチェック
                else if ((++utfPos) >= cp1252Pos || Bytes[utfPos] > 0xBF) { utfScore = int.MinValue; break; } //UTF8可能性消滅
                else if (b1 < 0xF5)
                {   //4バイト文字OK(全角文字とみなして評価)
                    if (prevIsKanji == true) { utfScore += 12; } else { utfScore += 6; prevIsKanji = true; }
                }
                else { utfScore = int.MinValue; break; } //UTF8可能性消滅(0xF5以降はUTF8未定義)
            }
        }
        //notAsciiStartPos〜cp1252Pos範囲のEUCチェック
        if (eucScore >= 0)
        {   //前の文字との連続性チェック用定数定義
            const int PREV_KANA = 1; //直前文字は半角カナ
            const int PREV_ZENKAKU = 2; //直前文字は全角
            int prevChar = 0; //前の文字はKANAでもZENKAKUでもない
            for (int eucPos = notAsciiStart; eucPos < cp1252Pos; eucPos++)
            {   //1バイト目(0xA1-0xFE,0x8E,0x8F)・2バイト目(1バイト目に応じ範囲が異なる)のチェック
                b1 = Bytes[eucPos];
                if (b1 == 0xFF || (++eucPos) >= cp1252Pos) { eucScore = int.MinValue; break; } //EUC可能性消滅
                b2 = Bytes[eucPos];
                if (b1 >= 0xA1)
                {   //1バイト目=全角文字指定、2バイト全角文字チェック
                    if (b2 < 0xA1 || b2 == 0xFF) { eucScore = int.MinValue; break; } //EUC可能性消滅
                    //2バイト文字OK(全角)
                    if (prevChar == PREV_ZENKAKU) { eucScore += 5; } else { eucScore += 2; prevChar = PREV_ZENKAKU; }
                }
                else if (b1 == 0x8E)
                {   //1バイト目=かな文字(orEUC-TWの4バイト文字)指定。2バイトの半角カナ文字チェック
                    if (b2 < 0xA1 || b2 > 0xDF) { eucScore = int.MinValue; break; } //EUC可能性消滅
                    //検出OK,EUC文字数を加算(半角文字)【抜粋】
                    if (prevChar == PREV_KANA) { eucScore += 6; }
                    else { eucScore += 2; prevChar = PREV_KANA; }
                }
                else if (b1 == 0x8F
                    && b2 >= 0xA1 && b2 < 0xFF
                    && (++eucPos) < cp1252Pos
                    && (b2 = Bytes[eucPos]) >= 0xA1 && b2 < 0xFF)
                {   //残る可能性は3バイト文字:検出OKならEUC文字数を加算(全角文字、補助漢字)
                    if (prevChar == PREV_ZENKAKU) { eucScore += 8; } else { eucScore += 3; prevChar = PREV_ZENKAKU; }
                    existsEUC0x8F = true; //※補助漢字有
                }
                else { eucScore = int.MinValue; break; } //EUC可能性消滅
            }
        }

        //ASCII文字範囲の読み飛ばし&バイナリチェック&JISチェック、b1に非ASCII文字出現位置のバイト値を格納
        while (cp1252Pos < Length && (b1 = Bytes[cp1252Pos]) < DEL)
        {
            if (b1 <= BINARY)
            {   //■バイナリ確定
                Text = null;
                return null;
            }
            if (b1 == 0x1B && (b2 = JIS.isEscape(Bytes, Length, cp1252Pos)) > 0)
            {   //JISエスケープシーケンスを検出
                jisScore++;
                asciiEndPos += b2;
            }
            cp1252Pos++;
        }
    }

    //【3】SJISチェック(非ASCII登場位置からチェック開始:ただしDEL検出時などは可能性なし)
    if (sjisScore != int.MinValue)
    {
        sjisScore = GetEncoding(Bytes, asciiEndPos, Length);
    }

    //【4】ポイントに応じ文字コードを決定【抜粋】(実際にそのエンコーディングで読み出し成功すればOKとみなす)
    if (jisScore >= 2 && jisScore > (Length / 100000))
    {   //一定以上の比率でJISエスケープシーケンス出現
        return JIS.GetEncoding(Bytes, Length, out Text);  //■JIS確定(詳細種別を判定する)
    }
    if (eucScore > 0 && eucScore > sjisScore && eucScore > utfScore)
    {   //EUC可能性高
        if (cp1252Score > eucScore)
        {   //ただしCP1252の可能性が高ければCP1252を先にチェック
            if ((Text = CharCode.ANSI.GetString(Bytes, Length)) != null) { return CharCode.ANSI; } //■CP1252で読みこみ成功
        }
        if (existsEUC0x8F && (Text = CharCode.EUCH.GetString(Bytes, Length)) != null) { return CharCode.EUCH; }//■EUC補助漢字読みこみ成功
        if ((Text = CharCode.EUC.GetString(Bytes, Length)) != null) { return EUC; } //■EUCで読みこみ成功
    }
    if (utfScore > 0 && utfScore >= sjisScore)
    {   //UTF可能性高
        if ((Text = CharCode.UTF8N.GetString(Bytes, Length)) != null) { return CharCode.UTF8N; } //■UTF-8Nで読みこみ成功
    }
    if (sjisScore >= 0)
    {   //SJIS可能性高(ただしCP1252の可能性が高ければCP1252を先にチェック)
        if (cp1252Score > sjisScore && (Text = CharCode.ANSI.GetString(Bytes, Length)) != null) { return CharCode.ANSI; } //■CP1252で読みこみ成功
        if ((Text = CharCode.SJIS.GetString(Bytes, Length)) != null) { return CharCode; } //■SJISで読みこみ成功
    }
    if (cp1252Score > 0)
    {   //CP1252の可能性のみ残っているのでチェック
        if ((Text = CharCode.ANSI.GetString(Bytes, Length)) != null) { return CharCode.ANSI; } //■CP1252で読みこみ成功
    }
    //■いずれにも該当しなかった場合は、バイナリファイル扱いとする
    Text = null;
    return null;
}

 ソースコード抜粋:BOMなしUTF16可能性判定メソッド/ShiftJIS判定メソッド

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
/// <summary>BOMなしUTF16の可能性があるか(先頭文字がASCIIか否かをもとに)判定</summary>
/// <param name="Bytes">判定対象のバイト配列</param>
/// <param name="Length">ファイルサイズ</param>
/// <returns>UTF16Nと思われる場合はその文字コード、否ならnull</returns>
public static CharCode SeemsUTF16N(byte[] Bytes, int Length)
{
    if (Length >= 2 && Length % 2 == 0)
    {
        if (Bytes[0] == 0x00)
        {
            if (Bytes[1] > BINARY && Bytes[1] < DEL && (Length == 2 || Bytes[2] == 0))
            {   //▲UTF16BigEndianの可能性あり
                return CharCode.UTF16BE;
            }
        }
        else if (Bytes[1] == 0x00)
        {
            if (Bytes[0] > BINARY && Bytes[0] < DEL && (Length == 2 || Bytes[3] == 0))
            {   //▲UTF16LittleEndianの可能性あり
                return CharCode.UTF16LE;
            }
        }
    }
    return null; //UTF16Nの可能性はないと判断
}

/// <summary>ShiftJISの判定スコア算出(判定開始位置〜ファイル末尾までの範囲を対象)</summary>
/// <param name="Bytes">判定対象のバイト配列</param>
/// <param name="pos">判定開始位置(非ASCII文字コードが初めて登場した位置)</param>
/// <param name="Length">ファイルサイズ(バイト配列先頭からのデコード対象バイト数)</param>
/// <returns>判定スコア算出結果</returns>
protected override int GetEncoding(byte[] Bytes, int pos, int Length)
{
    int score = 0; //初期値ゼロからReadJEnc評価を始める
    byte b1 = Bytes[pos];
    byte b2;
    while (pos < Length)
    {   //前の文字との連続性チェック用定数定義
        const int PREV_KANA = 1; //直前文字は半角カナ
        const int PREV_ZENKAKU = 2; //直前文字は全角
        int prevChar = 0; //前の文字はKANAでもZENKAKUでもない
        while (b1 > DEL)
        {
            if (b1 >= 0xA1 && b1 <= 0xDF)
            {   //1バイト半角カナ:OK(連続はEUCやCP1252よりも高配点とする)
                if (prevChar == PREV_KANA) { score += 3; } else { score += 1; prevChar = PREV_KANA; }
            }
            // 非CP932チェック用定数(0x00000061,0xE0009800):CP932ではデコード不能な未定義文字のビットを1
            //        FEDC BA98 7654 3210         FEDC BA98 7654 3210
            //        ---- ---- ---- ----         ---- ---- ---- ----
            // (0x9#) 0000 0000 0000 0000  (0x8#) 0000 0000 0110 0001 - 80(A0判定でも流用):定義外、85,86:未使用(shift_jis2004などでは使用ありだがCP932ではデコード不能)
            // (0xF#) 1110 0000 0000 0000  (0xE#) 1001 1000 0000 0000 - FD,FE,FF:定義外、EB,EC,EF:未使用 (F0-F9:外字は許容。HNXgrepなど外字不許容とする場合はビットを立てること)
            else if (((b1 < 0xE0 ? 0x00000061 : 0xE0009800) & 1u << (b1 % 32)) != 0
                || (++pos) >= Length
                || (b2 = Bytes[pos]) < 0x40 || b2 > 0xFC)
            {   //1バイト目がおかしい(SJIS定義外/未使用) or 2バイト目把握不能 or 2バイト目SJIS定義外
                return int.MinValue; //可能性消滅
            }
            else
            {   //全角文字数を加算(EUCよりは可能性を低めに見積もっておく)
                if (prevChar == PREV_ZENKAKU) { score += 4; }
                else
                {   //(ただし唐突に0x98以降の第二水準文字が出てきた場合は、UTF-8/EUC/CP1252の可能性が高いのでプラス値なしとする)
                    score += (b1 > 0x98 ? 0 : 2);
                    prevChar = PREV_ZENKAKU;
                }
            }
            //各国語全コード共通:さらに次の文字へ
            if ((++pos) >= Length) { break; }
            b1 = Bytes[pos];
        }
        //各国語全コード共通:半角文字の範囲を読み飛ばし
        while (b1 <= DEL && (++pos) < Length) { b1 = Bytes[pos]; }
    }
    return score;
}

 ソースコード抜粋:文字コード定義クラス・およびBOMつきUTF判別メソッド

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
public class CharCode
{
////////////////////////////////////////////////////////////////////////
// <CharCode.cs> ReadJEnc 文字コード種類定義【抜粋】
//  Copyright (C) 2014 hnx8(H.Takahashi)
//  http://hp.vector.co.jp/authors/VA055804/
//
//  Released under the MIT license
//  http://opensource.org/licenses/mit-license.php
////////////////////////////////////////////////////////////////////////

//Unicode系文字コード

/// <summary>UTF8(BOMあり)</summary>
public static readonly Text UTF8 = new Text("UTF-8", new UTF8Encoding(true, true)); //BOM : 0xEF, 0xBB, 0xBF
/// <summary>UTF32(BOMありLittleEndian)</summary>
public static readonly Text UTF32 = new Text("UTF-32", new UTF32Encoding(false, true, true)); //BOM : 0xFF, 0xFE, 0x00, 0x00
/// <summary>UTF32(BOMありBigEndian)</summary>
public static readonly Text UTF32B = new Text("UTF-32B", new UTF32Encoding(true, true, true)); //BOM : 0x00, 0x00, 0xFE, 0xFF
/// <summary>UTF16(BOMありLittleEndian)</summary><remarks>Windows標準のUnicode</remarks>
public static readonly Text UTF16 = new Text("UTF-16", new UnicodeEncoding(false, true, true)); //BOM : 0xFF, 0xFE
/// <summary>UTF16(BOMありBigEndian)</summary>
public static readonly Text UTF16B = new Text("UTF-16B", new UnicodeEncoding(true, true, true)); //BOM : 0xFE, 0xFF

/// <summary>UTF16(BOM無しLE)</summary>
public static readonly Text UTF16LE = new Text("UTF-16LE", new UnicodeEncoding(false, false, true));
/// <summary>UTF16(BOM無しBE)</summary>
public static readonly Text UTF16BE = new Text("UTF-16BE", new UnicodeEncoding(true, false, true));
/// <summary>UTF8(BOM無し)</summary>
public static readonly Text UTF8N = new Text("UTF-8N", new UTF8Encoding(false, true));

//非Unicode系文字コード

/// <summary>Ascii</summary><remarks>デコードはUTF8Encodingを流用</remarks>
public static readonly Text ASCII = new Text("ASCII", UTF8N.Encoding);
/// <summary>1252 ISO8859 西ヨーロッパ言語</summary>
public static readonly Text ANSI = new Text("ANSI欧米", 1252);

/// <summary>50221 iso-2022-jp 日本語 (JIS-Allow 1 byte Kana) ※MS版</summary>
public static readonly Text JIS = new Text("JIS50221", 50221);
/// <summary>50222 iso-2022-jp 日本語 (JIS-Allow 1 byte Kana - SO/SI) ※MS版</summary><remarks>SO/SIによるカナシフトのみのファイルもCP50222とみなす</remarks>
public static readonly Text JIS50222 = new Text("JIS50222", 50222);

/// <summary>EUC補助漢字(0x8F)あり ※MS-CP20932を利用し強引にデコードする</summary><remarks>エンコードするとファイルが壊れるので注意</remarks>
public static readonly Text EUCH = new EucH("EUC補漢");
/// <summary>51932 euc-jp 日本語 (EUC) ※MS版</summary>
public static readonly Text EUC = new Text("EUCJP", 51932);
/// <summary>932 shift_jis 日本語 (シフト JIS) ※MS独自</summary>
public static readonly Text SJIS = new Text("ShiftJIS", 932);

// 文字コード(ファイル種類)判定メソッド

/// <summary>BOMありUTFファイルの文字コードを判定する</summary>
/// <param name="Bytes">判定対象のバイト配列</param>
/// <param name="Read">バイト配列の読み込み済バイト数</param>
/// <returns>BOMから判定できた文字コード種類、合致なしの場合null</returns>
public static CharCode GetPreamble(byte[] Bytes, int Read)
{   //BOM一致判定
    return GetPreamble(Bytes, Read,
        UTF8, UTF32, UTF32B, UTF16, UTF16B);
}
protected static CharCode GetPreamble(byte[] Bytes, int Read, params CharCode[] Array)
{
    foreach (CharCode c in Array)
    {   //読み込み済バイト配列内容をもとにファイル種類の一致を確認
        byte[] bom = c.Bytes;
        int i = (bom != null ? bom.Length : int.MaxValue); //BOM・マジックナンバー末尾から調べる
        if (Read < i) { continue; } //そもそもファイルサイズが小さい場合は不一致
        do
        {   //全バイト一致ならその文字コードとみなす
            if (i == 0) { return c; }
            i--;
        } while (Bytes[i] == bom[i]); //BOM・マジックナンバー不一致箇所ありならdo脱出
    }
    return null; //ファイル種類決定できず
}

#region テキスト基本クラス定義【抜粋】----------------------------
/// <summary>文字コード種類:テキスト
/// </summary>
public class Text : CharCode
{
    internal Text(string Name, Encoding Encoding) : base(Name, Encoding, Encoding.GetPreamble()) { }
    internal Text(string Name, int enc)
        : base(Name, System.Text.Encoding.GetEncoding(enc, EncoderFallback.ExceptionFallback, DecoderFallback.ExceptionFallback), null) { }
}

/// <summary>ファイル文字コード種類名</summary>
public readonly string Name;
/// <summary>先頭バイト識別データ(BOM/マジックナンバー)</summary>
protected readonly byte[] Bytes = null;
/// <summary>エンコーディング</summary>
private Encoding Encoding;

/// <summary>基本コンストラクタ</summary>
protected CharCode(string Name, Encoding Encoding, byte[] Bytes)
{
    this.Name = Name;
    this.Encoding = Encoding;
    this.Bytes = Bytes;
}

/// <summary>引数のバイト配列から文字列を取り出す。失敗時はnullを返す</summary>
public virtual string GetString(byte[] Bytes, int Length)
{
    try
    {   //BOMサイズを把握し、BOMを除いた部分を文字列として取り出す
        int bomBytes = (this.Bytes == null ? 0 : this.Bytes.Length);
        return Encoding.GetString(Bytes, bomBytes, Length - bomBytes);
    }
    catch (DecoderFallbackException)
    {   //読み出し失敗(マッピングされていない文字があった場合など)
        return null;
    }
}
#endregion
}

[カテゴリ: プログラミング全般]

[通知用URL]



  • Hatenaブックマークに追加
  • livedoorクリップに追加
  • del.icio.usに追加
  • FC2ブックマークに追加

最終更新時間:2015年01月03日 23時37分17秒