示例#1
0
        /// <summary>読み込んであるバイト配列のプリアンブル(BOMヘッダ/マジックナンバー)からファイル文字コード種類特定を試みる</summary>
        /// <param name="len">ファイルサイズ(未読込部分も含む。読み込み済サイズはthis.Lengthを参照)</param>
        /// <returns>確定した場合、ファイル文字コード種類。確定できなかった場合null</returns>
        protected virtual CharCode GetPreamble(long len)
        {
            //【0】ファイル先頭バイトからUTF文字コード(BOMつきUTF)を判定
            CharCode ret = CharCode.GetPreamble(this.Bytes, this.Length);

            //BOMテキストファイルと判定できず&ファイル先頭にバイナリファイル特徴の0x00が登場している場合、追加チェック
            if (ret == null && Array.IndexOf <byte>(this.Bytes, 0x00, 0, this.Length) >= 0)
            {     //UTF16Nの可能性がなければバイナリファイルとみなす
                if (ReadJEnc.SeemsUTF16N(this.Bytes, (int)len) == null)
                { //■バイナリ確定(マジックナンバーからファイル種類を決定)
                    return(FileType.GetBinaryType(this.Bytes, this.Length));
                }
            }
            return(ret); //■BOMから特定できた場合はBOMつきUTF(特定できなかった場合はnull)
        }
 /// <summary>基本コンストラクタ</summary>
 /// <param name="c">自動判別デフォルト文字コード</param>
 /// <param name="EUC">EUCの文字コード</param>
 protected ReadJEnc(CharCode c, CharCode EUC)
 {
     this.CharCode = c;
     this.EUC      = EUC;
 }
        //文字コード判別メソッド================================================

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

            //【1】7bit文字コードの範囲の走査(ASCII判定/非ASCII文字開始位置把握)、およびUTF16N/JISチェック
            JIS escapeSequenceChecker = null; //JISエスケープシーケンス評価
            int asciiEndPos           = 0;    //ループ変数、兼、非ASCII文字を初めて検出した位置

            while (b1 < DEL)                  //非ASCII文字が出現したらループ脱出:b1にはあらかじめ読み込み済
            {
                if (b1 <= BINARY)
                {   //バイナリ文字検出:先頭2バイトでの検出ならUTF16Nの可能性をチェック、否ならバイナリ確定
                    CharCode ret = (asciiEndPos < 2 ? SeemsUTF16N(bytes, len) : null);
                    if (ret != null && (text = ret.GetString(bytes, len)) != 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)
                {   //エスケープシーケンス判定(エスケープコード内容を読み飛ばす)
                    if (escapeSequenceChecker == null)
                    {
                        escapeSequenceChecker = new JIS(bytes, len, asciiEndPos);
                    }
                    asciiEndPos += escapeSequenceChecker.GetEncoding(asciiEndPos);
                }
                //次の文字へ
                if ((++asciiEndPos) >= len)
                {     //全文字チェック完了:非ASCII文字未検出、JISもしくはASCII
                    if (escapeSequenceChecker != null)
                    { //エスケープシーケンスに基づく文字コードが取得できるか確認
                        CharCode ret = escapeSequenceChecker.GetEncoding(out text);
                        if (ret != null)
                        {   //■エスケープシーケンスに基づく文字コードで確定
                            return(ret);
                        }
                    }
                    else if (JIS.hasSOSI(bytes, len))
                    {     //SO,SIによるエスケープを検出した場合は、半角カナJISの可能性を判定
                        if (escapeSequenceChecker == null && (text = CharCode.JIS50222.GetString(bytes, len)) != null)
                        { //■エスケープシーケンスなし、半角カナSOSIのみを使用したJISで確定
                            return(CharCode.JIS50222);
                        }
                    }
                    //■ASCII確定(ただしデコード失敗時はバイナリ)
                    return(((text = CharCode.ASCII.GetString(bytes, len)) != null) ? CharCode.ASCII : null);
                }
                b1 = bytes[asciiEndPos];
            }

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

            for (int cp1252Pos = asciiEndPos; cp1252Pos < len;) //cp1252Posの加算はロジック途中で随時実施
            {
                if (b1 == DEL)
                {   //制御文字0x7F登場なら、ごくわずかなJISの可能性以外全消滅。JISの可能性を消しきれるか判定
                    cp1252Score = int.MinValue;
                    utfScore    = int.MinValue;
                    eucScore    = int.MinValue;
                    sjisScore   = int.MinValue;
                    if (escapeSequenceChecker == null || (cp1252Pos++) >= len || (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) < len)
                    {
                        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) >= len)
                        {
                            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バイト目以降は(デコード時にチェックアウトできる前提で)冗長なエンコードやサロゲート等を許容している
                        //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チェック ※EUCの文字コード範囲はEUC-JP/TW/CN/KRでほぼ共通
                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バイト目=EUC-JPのかな文字(orEUC-TWの4バイト文字)指定。2バイトの半角カナ文字チェック
                            if (b2 < 0xA1 || b2 > 0xDF)
                            {
                                eucScore = int.MinValue; break;
                            }                                                               //EUC可能性消滅
                            //検出OK,EUC文字数を加算(半角文字)
                            if (prevChar == PREV_KANA)
                            {
                                eucScore += 6;
                            }
                            //漢字圏テキスト文字コードのうちEUC-TWに限り全角文字相当の扱いとする(0x8E,0xA2-0xB0,0xA1-0xFE,0xA1-0xFEの4バイト文字の判定に流用)
                            else if (this.EUCTW)
                            {
                                if (prevChar == PREV_ZENKAKU)
                                {
                                    eucScore += 6;
                                }
                                else
                                {
                                    eucScore += 2; prevChar = PREV_ZENKAKU;
                                }
                            }
                            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 < len && (b1 = bytes[cp1252Pos]) < DEL)
                {
                    if (b1 <= BINARY)
                    {   //■バイナリ確定
                        text = null;
                        return(null);
                    }
                    if (b1 == 0x1B)
                    {   //エスケープシーケンス判定(エスケープコード内容を読み飛ばす)
                        if (escapeSequenceChecker == null)
                        {
                            escapeSequenceChecker = new JIS(bytes, len, cp1252Pos);
                        }
                        cp1252Pos += escapeSequenceChecker.GetEncoding(cp1252Pos);
                    }
                    cp1252Pos++;
                }
            }

            //【3】SJISなどの各国語文字コードチェック(非ASCII登場位置からチェック開始:ただしDEL検出時などは可能性なし)
            if (sjisScore != int.MinValue)
            {
                sjisScore = this.GetEncoding(bytes, asciiEndPos, len);
            }

            //【4】ポイントに応じ文字コードを決定(実際にそのエンコーディングで読み出し成功すればOKとみなす)
            if (escapeSequenceChecker != null)
            {   //JIS系可能性高:エスケープシーケンスに基づく文字コードが取得できるか確認
                CharCode ret = escapeSequenceChecker.GetEncoding(out text);
                if (ret != null)
                {
                    return(ret);
                }                                //■エスケープシーケンスに基づく文字コードで確定
            }
            if (eucScore > 0 && eucScore > sjisScore && eucScore > utfScore)
            {     //EUC可能性高
                if (cp1252Score > eucScore)
                { //ただしCP1252の可能性が高ければCP1252を先にチェック
                    if ((text = CharCode.ANSI.GetString(bytes, len)) != null)
                    {
                        return(CharCode.ANSI);
                    }                                                                                   //■CP1252で読みこみ成功
                }
                if (existsEUC0x8F && (text = CharCode.EUCH.GetString(bytes, len)) != null)
                {
                    return(CharCode.EUCH);
                }                                                                                                   //■EUC補助漢字読みこみ成功
                if ((text = this.EUC.GetString(bytes, len)) != null)
                {
                    return(this.EUC);
                }                                                                         //■EUCで読みこみ成功
            }
            if (utfScore > 0 && utfScore >= sjisScore)
            {   //UTF可能性高
                if ((text = CharCode.UTF8N.GetString(bytes, len)) != null)
                {
                    return(CharCode.UTF8N);
                }                                                                                     //■UTF-8Nで読みこみ成功
            }
            if (sjisScore >= 0)
            {   //SJISなどの各国語指定に合致したなら、そのコードでの読み出しを試みる(ただしCP1252の可能性が高ければCP1252を先にチェック)
                if (cp1252Score > sjisScore && (text = CharCode.ANSI.GetString(bytes, len)) != null)
                {
                    return(CharCode.ANSI);
                }                                                                                                              //■CP1252で読みこみ成功
                if ((text = this.CharCode.GetString(bytes, len)) != null)
                {
                    return(this.CharCode);
                }                                                                                   //■指定された文字コード(日本語の場合はSJIS、それ以外の場合はBig5/GB18030/UHC)で読みこみ成功
            }
            if (cp1252Score > 0)
            {   //CP1252の可能性のみ残っているのでチェック
                if ((text = CharCode.ANSI.GetString(bytes, len)) != null)
                {
                    return(CharCode.ANSI);
                }                                                                                   //■CP1252で読みこみ成功
            }
            //■いずれにも該当しなかった場合は、バイナリファイル扱いとする
            text = null;
            return(null);
        }