RCIE-ジャンクのコード屋

主に自分のためにコーディングのTIPSを蓄積しています。

(C#)文字コードを判別して、テキストファイルを読み込む方法

作ったもの

 日本語テキストファイルのエンコード方式には、以下のようなものがあります。
UTF-16(LE, BE)
UTF-8
③Shift-JIS
EUC-JP
 これを自動的に判定して読み込む関数を作りました。

前提

using System.IO;
using System.Text;

コード

/// <summary>
/// 自動的にエンコード方式を判定してテキストファイルを読み込みます。
/// </summary>
/// <param name="path">ファイルパス</param>
/// <returns>読み込んだ文字列</returns>
static string ReadTextFile(string path) {
	Encoding encoding;
	return ReadTextFile(path, out encoding);
}
/// <summary>
/// 自動的にエンコード方式を判定してテキストファイルを読み込みます。
/// </summary>
/// <param name="path">ファイルパス</param>
/// <param name="enc">エンコード方式</param>
/// <returns>読み込んだ文字列</returns>
static string ReadTextFile(string path, out Encoding enc) {
	var replacement = new DecoderReplacementFallback("�[FALLBACK]");
	Func<int, Encoding> CodePageをEncodingに = (cp) => {
		var encoding = (Encoding)Encoding.GetEncoding(cp).Clone();
		encoding.DecoderFallback = replacement;
		return encoding;
	};
	int[] aryCP = { 65001, 932, 1200, 1201, 51932 };
	int minLength = int.MaxValue;
	string result = null;
	enc = null;
	byte[] bytes = File.ReadAllBytes(path);
	foreach (var codepage in aryCP) {
		var encoding = CodePageをEncodingに(codepage);
		string s = encoding.GetString(bytes);
		int length = Encoding.UTF8.GetByteCount(s);
		if (length < minLength) {
			minLength = length;
			result = s;
			enc = encoding;
		}
	}
	return result;
}

使い方

Encoding enc;
string content = ReadTextFile(@"C:\Windows\Inst.log", out enc);

解説

 何かが書かれたテキストファイルを様々な文字コードで開くと次のようになったとしましょう。

  • Shift-JIS→C#縺ョ菴ソ縺?焔
  • UTF-8C#の使い手
  • UTF16→䌣껤붿蓦見

 人間が見れば、UTF-8が正しいのは明らかですが、どのように判別すればいいのでしょうか。
 3つを比べてみましょう。文字化けを起こすと、滅多に使わない文字が頻出したり、長い文字列になってしまうことが分かります。そこで実際に、これらの文字をUTF-8で符号化してみると、

  • C#縺ョ菴ソ縺?焔→21bytes
  • C#の使い手→14bytes
  • 䌣껤붿蓦見→21bytes

となり、「C#の使い手」のサイズが最小で、正しそうだということが判定できます。
 正しく復号できなかった時の代替文字は標準では「�」ですが、たったの3bytesなので、ひらがなと同じサイズです。そのため、「�[FALLBACK]」を代替文字列に設定して、復号エラーに対して13bytesのペナルティを与えています。