[C#] nkf を用いた文字コードの判別

Monday, October 15, 2012

オープンソースである nkf を借用して,テキストファイルの文字コードを簡単に判別できます。バージョン 2.0.9 以降の nkf のライセンスは zlib/libpng License です (極めて良心的なライセンスです)。対応文字コードは次の通りです。

  • Shift_JIS
  • EUC-JP
  • ISO-2022-JP
  • UTF-8
  • UTF-16LE
  • UTF-16BE

nkf32.dll を用意し,実行ファイルと同じディレクトリに置く必要があります。nkf32.dll は Vector からダウンロードできます。元々の nkf とは異なるライセンスですので,配布条件を確認してください (とは言っても緩い条件です)。

nkf.exe nkf32.dll Windows 用
http://www.vector.co.jp/soft/win95/util/se295331.html

文字コードの判別を行うには,私が作った EncodingUtilities クラスを利用してください。まずは使い方の見本から。

using System;
using System.Text;

class Program
{
    static void Main()
    {
        string path = @"c:\works\hoge.txt";

        // 文字コードを判別
        Encoding enc = EncodingUtilities.DetectEncoding(path);

        // テキストエディタを作るならこうしとくといい
        if (enc == null || enc == Encoding.ASCII)
        {
            enc = Encoding.Default;
        }

        // 処理...
    }
}

次のリンクからダウンロードできます。断りなく自由に使っていただいて結構です。

ソースコード: EncodingUtilities.cs

// EncodingUtilities.cs

using System;
using System.IO;
using System.Text;

/// <summary>
/// エンコーディングを取り扱うユーティリティを提供します。
/// </summary>
public static class EncodingUtilities
{
    /// <summary>
    /// テキストファイルで使われている文字コードを判別し,対応する Encoding オブジェクトを返します。
    /// </summary>
    /// <param name="path">テキストファイルへのパス。</param>
    /// <returns>Shift_JIS, EUC-JP, ISO-2022-JP, UTF-8, UTF-16, UTF-16BE, US-ASCII, null のいずれかが返ります。</returns>
    public static Encoding DetectEncoding(string path)
    {
        // -g: 自動判別の結果を出力する。
        // -t: 何もしない。
        SetNkfOption("-gt");

        byte[] bytes;
        
        using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
        {
            bytes = new byte[fs.Length];
            fs.Read(bytes, 0, bytes.Length);
        }

        unsafe
        {
            fixed (byte* pbs = bytes)
            {
                StringBuilder strBldr = new StringBuilder(1);
                NkfConvert(strBldr, (char*) pbs);
            }
        }

        int nEnc = NkfGetKanjiCode();

        // US-ASCII も ISO-2022-JP と判別されるので,更に篩にかける。
        if (nEnc == nJIS)
        {
            nEnc = nASCII;  // US-ASCII と仮定する
            for (int i = 0; i < bytes.Length; i++)
            {
                if (bytes[i] == 0x1b) // ISO-2022-JP であれば ESC (0x1B) が含まれる
                {
                    nEnc = nJIS;
                    break;
                }
            }
        }

        switch (nEnc)
        {
            case nSJIS:
                return Encoding.GetEncoding("shift_jis");
            case nEUC:
                return Encoding.GetEncoding("euc-jp");
            case nJIS:
                return Encoding.GetEncoding("iso-2022-jp");
            case nUTF8:
                return Encoding.GetEncoding("utf-8");
            case nUTF16LE:
                return Encoding.GetEncoding("utf-16");
            case nUTF16BE:
                return Encoding.GetEncoding("utf-16BE");
            case nASCII:
                return Encoding.ASCII;
            default:
                return null;
        }
    }

    #region nkf functions

    [System.Runtime.InteropServices.DllImport("nkf32.dll")]
    static extern int SetNkfOption(string optStr);

    [System.Runtime.InteropServices.DllImport("nkf32.dll")]
    unsafe static extern void NkfConvert(StringBuilder outStr, char* inStr);

    [System.Runtime.InteropServices.DllImport("nkf32.dll")]
    static extern int NkfGetKanjiCode();

    #endregion

    #region Fields

    // nSJIS, nEUC, nJIS, nUTF8, nUTF16LE, nUTF16BE の値は NkfGetKanjiCode() の戻り値に対応
    const int nSJIS = 0;
    const int nEUC = 1;
    const int nJIS = 2;
    const int nUTF8 = 3;
    const int nUTF16LE = 4;
    const int nUTF16BE = 5;

    const int nASCII = 1001;

    #endregion
}

「null 以外を返した」という結果は「文字コードを完全に特定できた」という意味を持つ訳ではありません。その点だけ注意してください。これは nkf の仕様に因ります。

コメントを残す

メールアドレスが公開されることはありません。

nine + thirteen =