/// <summary> /// シナリオファイルを読み込む /// </summary> /// <returns>シナリオファイルが読み込めたかどうか</returns> public bool LoadScenario() { //ReadJEncを利用してファイルの読み込み using (Hnx8.ReadJEnc.FileReader reader = new FileReader(this.File)) { Hnx8.ReadJEnc.CharCode c = reader.Read(this.File); Console.WriteLine("-----------------------------------------------------------------------"); Console.WriteLine("Loading ScenarioFile【{0}】...", this.File.Name); string text = reader.Text; if (c is CharCode.Text) { //読み込んだファイルがテキスト Console.WriteLine("Encoding: {0}", c.Name); ScenarioGrammarParser parser = new ScenarioGrammarParser(); try { this.Data = parser.Parse(text); Console.WriteLine("Parse Successful !"); return(true); } catch (NullReferenceException) { Console.Error.WriteLine("Scenario: 【{0}】header mismatched.", this.File.FullName); } } else { //テキストファイルではない Console.Error.WriteLine("【{0}】is not text file.", this.File.FullName); } } Console.Error.WriteLine("Scenario Load Error"); return(false); }
///// <summary> ///// 簡易エンコード判定 ///// </summary> ///// <param name="filePath"></param> ///// <returns></returns> //public static Encoding GetEncoding(string filePath) //{ // int codepage = (int)Const.EncodingCodePage.ShiftJIS; // using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) // { // byte[] bs = new byte[1]; // fs.Read(bs, 0, bs.Length); // if (bs.Length > 0) // { // if (bs[0] >= 161 && bs[0] <= 193) // codepage = (int)Const.EncodingCodePage.EucJP; // if (bs[0] >= 208 && bs[0] <= 239) // codepage = (int)Const.EncodingCodePage.UTF8; // } // fs.Close(); // } // // 判定できないときはshift-jisをとりあえず返す // return Encoding.GetEncoding(codepage); //} /// <summary> /// ライブラリによるエンコード判定 /// </summary> /// <param name="filePath"></param> /// <returns></returns> public static Encoding GetEncoding(string filePath) { FileInfo file = new FileInfo(filePath); using (Hnx8.ReadJEnc.FileReader reader = new FileReader(file)) { //判別読み出し実行 Hnx8.ReadJEnc.CharCode c = reader.Read(file); Encoding enc = c.GetEncoding(); // これでも取れなかった場合はしょうがない return(enc != null ? enc : Encoding.Default); } }
/// <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> /// <returns>シナリオが正常に読み込めたかどうか</returns> public bool Load() { if (!File.Exists(FilePath)) { //ファイルが存在しない Console.Error.WriteLine("Scenario: 【{0}】not found.", FileName); return(false); } //ReadJEncを利用してファイルの読み込み FileInfo file = new FileInfo(FilePath); using (Hnx8.ReadJEnc.FileReader reader = new FileReader(file)) { Hnx8.ReadJEnc.CharCode c = reader.Read(file); Console.WriteLine("-----------------------------------------------------------------------"); Console.WriteLine("Loading ScenarioFile【{0}】...", FileName); string text = reader.Text; if (c is CharCode.Text && Regex.IsMatch(text, @"BveTs\s*Scenario", RegexOptions.IgnoreCase)) { //読み込んだファイルがテキスト Console.WriteLine("Encoding: {0}", c.Name); ScenarioGrammarParser parser = new ScenarioGrammarParser(); parser.ErrorListener = ErrorListener; try { this.Data = parser.Parse(text); Console.WriteLine("Parse Successful !"); InitData(); return(true); } catch (NullReferenceException) { Console.Error.WriteLine("Scenario: 【{0}】header mismatched.", FileName); } } else { //テキストファイルではない Console.Error.WriteLine("【{0}】is not text file.", FileName); } } Console.Error.WriteLine("Scenario Load Error"); return(false); }
/// <summary>引数指定文字コードとCP1252を判別対象とする際のコンストラクタ</summary> /// <param name="CharCode">このインスタンスで判別対象とするデフォルト文字コード</param> /// <param name="BOUND">通常文字/この文字コード固有の文字種範囲の境界</param> /// <param name="NODEF"> /// 文字コード未定義の箇所をbitで表現した値 /// <remarks> /// [0]=0x80-0x9Fの未定義箇所 /// [1]=0xA0-0xBFの未定義箇所 /// [2]=0xC0-0xDFの未定義箇所 /// [3]=0xE0-0xFFの未定義箇所 /// </remarks></param> internal SBCS(CharCode CharCode, int BOUND, params uint[] NODEF) : base(CharCode, null) { this.BOUND = BOUND; this.NODEF = NODEF; }
// 文字コード判別メソッド================================================ /// <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) { if (len == 0) { // ■空ファイルにつき非テキストと判断 text = null; return(null); } byte b1 = bytes[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); // EUC検出対象なしなら最初からチェック対象外 int sjisScore = 0; bool existsEUC0x8F = false; // EUC補助漢字を見つけたらtrueを設定 uint NODEF = this.NODEF; // パフォーマンス改善のためローカル変数におろす 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系可能性あり、定義外文字混入チェック&ポイント加算 while (b1 > DEL) { // そのバイト値が未定義コードかどうかチェック(bit表現値とかみあうようビットシフトして照合) if (b1 <= 0x9F && (NODEF & (1u << (b1 % 32))) != 0) { // ビットが立ってる=未定義コード、可能性消滅 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; } #if (!JPONLY) // 漢字圏テキスト文字コードのうちEUC-TWに限り全角文字相当の扱いとする(0x8E,0xA2-0xB0,0xA1-0xFE,0xA1-0xFEの4バイト文字の判定に流用) else if (this.EUC == CharCode.EUCTW) { if (prevChar == PREV_ZENKAKU) { eucScore += 6; } else { eucScore += 2; prevChar = PREV_ZENKAKU; } } #endif 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 = 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系を先にチェック if ((text = this.CP125X.GetString(bytes, len)) != null) { return(this.CP125X); } // ■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系を先にチェック) if (cp1252Score > sjisScore && (text = this.CP125X.GetString(bytes, len)) != null) { return(this.CP125X); } // ■CP1252系で読みこみ成功 if ((text = this.CharCode.GetString(bytes, len)) != null) { return(this.CharCode); } // ■各国語文字コードで読みこみ成功 } if (cp1252Score > 0) { // CP1252系の可能性のみ残っているのでチェック if ((text = this.CP125X.GetString(bytes, len)) != null) { return(this.CP125X); } // ■CP1252系で読みこみ成功 } // ■いずれにも該当しなかった場合は、バイナリファイル扱いとする text = null; return(null); }
/// <summary>CP1252系文字コードのみを判別対象とする際のコンストラクタ</summary> /// <param name="CP125X">CP1252系の判別対象デフォルト文字コード(CP1252以外を指定)</param> /// <param name="NODEF">0x80-0x9Fのうち文字コード未定義の箇所をbitで表現した値</param> protected ReadJEnc(CharCode CP125X, uint NODEF) { this.CharCode = CP125X; this.CP125X = CP125X; this.NODEF = NODEF; }
/// <summary>引数指定文字コード/CP1252/EUCを判別対象とする際のコンストラクタ</summary> /// <param name="CharCode">このインスタンスで判別対象とするデフォルト文字コード</param> /// <param name="EUC">EUCの文字コード</param> protected ReadJEnc(CharCode CharCode, CharCode EUC) { this.CharCode = CharCode; this.EUC = EUC; }
/// <summary>基本コンストラクタ</summary> /// <param name="c">自動判別デフォルト文字コード</param> /// <param name="EUC">EUCの文字コード</param> protected ReadJEnc(CharCode c, CharCode EUC) { this.CharCode = c; this.EUC = EUC; }