« 7-zip32.dllでサロゲートペア文字を使用した場合の動作結果 | トップページ | ハッシュ値計算を途中経過取得可能な様に記述する »

2010年12月16日 (木)

文字列にUnicode固有文字が含まれるかのチェック方法

サロゲートペアのテストしたついでに、Unicode固有文字が含まれるかのチェック方法について、考えてみました。私の知る限り、直接その文字列中にUnicode固有文字があるかをチェックする Win32API や .NET クラスの類はない筈です。
では、どうすればよいでしょう。

一応考えたのが、Unicode と Shift-JIS の文字変換を行なうやり方です。つまり、Unicode → Shift-JIS → Unicode と変換すると、Shift-JIS変換が可能な文字だけで構成されていれば、通常は元の文字列に戻ります。ウムラウト付きの文字の様に、Shift-JIS変換によりウムラウト無しの英字に変換される場合も、それを再度Unicodeに変換すれば、ウムラウト無しのまま変換されるので、結局元の文字列には等しくなりません。また、Shift-JIS変換が不可能な文字が含まれていた場合も、元の文字列とは違った文字列になります。

1.Win32API を使用する方法

では、この方法を Win32API を使用してやってみます。
Unicode から Shift-JIS への変換は、WideCharToMultiByte、逆変換は、MultiByteToWideChar を使用します。

[DllImport( "Kernel32.dll", CharSet=CharSet.Unicode )]
static extern int WideCharToMultiByte(uint CodePage, uint dwFlags,
    [MarshalAs(UnmanagedType.LPWStr)] string lpWideCharStr, int cchWideChar,
    [MarshalAs(UnmanagedType.LPArray)] byte[] lpMultiByteStr, int cbMultiByte,
    IntPtr lpDefaultChar,IntPtr lpUsedDefaultChar);
[DllImport( "Kernel32.dll", CharSet=CharSet.Unicode )]
static extern int MultiByteToWideChar(uint CodePage, uint dwFlags,
    [MarshalAs(UnmanagedType.LPArray)] byte[] lpMultiByteStr, int cbMultiByte,
    [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder lpWideCharStr, int cchWideChar);
/// <summary>
/// Unicode 固有文字が存在するかをチェックして、その結果を返す。
/// (Win32API使用バージョン)
/// </summary>
/// <param name="checkString">チェック対象の文字列</param>
/// <returns>Unicode固有文字が含まれているとき true、含まれないとき false を返す</returns>
private bool IsUnicode1(string checkString)
{
    const uint CP_OEMCP = 1U;
    int len = checkString.Length;
    byte[] translateBuffer = new byte[len * 2 + 2];
    StringBuilder translateString = new StringBuilder(len * 2 + 1);
    WideCharToMultiByte(CP_OEMCP, 0, checkString, checkString.Length,
        translateBuffer, translateBuffer.Length, IntPtr.Zero, IntPtr.Zero);
    MultiByteToWideChar(CP_OEMCP, 0,translateBuffer, -1,
        translateString, translateString.Capacity);
    return (checkString != translateString.ToString());
}

2.マネージドコードで実現する方法

Shift-JIS へのコード変更は、以前の記事「7-zip32.dllのUTF-8モードをC#から使用する(1)」でやったのと同様、System.Text.Encoding を使用すれば実現出来ます。
具体的には、以下の様に記述すればいいでしょう。

/// <summary>
/// Unicode 固有文字が存在するかをチェックして、その結果を返す。
/// マネージドコード版
/// </summary>
/// <param name="checkString">チェック対象の文字列</param>
/// <returns>Unicode固有文字が含まれているとき true、含まれないとき false を返す</returns>
private bool IsUnicode2(string checkString)
{
    byte[] translateBuffer = Encoding.GetEncoding("shift_jis").GetBytes(checkString);
    string translateString = Encoding.GetEncoding("shift_jis").GetString(translateBuffer);
    return (checkString != translateString.ToString());
}

Win32API使用バージョンと比べると、随分シンプルに書けます。

3.どちらの方法が優れているか

プログラム作成という点では、マネージドコード版が圧倒的に優れていますが、実際の判定速度はどうでしょうか。試してみました。

大きなサイズのテキスト(約12.6MB)の判定(10回くり返し)
  処理時間(秒)
Win32API 版 マネージド 版
1回目 20.5 22.3
2回目 20.7 21.9
3回目 21.2 22.1
4回目 21.3 21.6
5回目 21.5 21.8

1KBのテキストの判定(200000回くり返し)
  処理時間(秒)
Win32API 版 マネージド 版
1回目 16.1 17.5
2回目 16.1 17.4
3回目 16.5 17.3
4回目 16.1 17.7
5回目 16.3 17.2

たいした時間差はないものの、Win32API 使用版の方が処理速度は良好な様です。もっともくり返し数を減らすと、処理時間のバラつきが出やすくなるせいか、結構マネージドコードの方がいい結果を出す事も多かったです。そのため、処理時間に差があると断言していいものか、迷うところです。
しかし処理時間差がほぼないと言えるので、生産性/保守性に優れたマネージドコードで処理した方がいいのではないでしょうか。

« 7-zip32.dllでサロゲートペア文字を使用した場合の動作結果 | トップページ | ハッシュ値計算を途中経過取得可能な様に記述する »

C# Tips」カテゴリの記事

コメント

コメントを書く

コメントは記事投稿者が公開するまで表示されません。

(ウェブ上には掲載しません)

トラックバック


この記事へのトラックバック一覧です: 文字列にUnicode固有文字が含まれるかのチェック方法:

« 7-zip32.dllでサロゲートペア文字を使用した場合の動作結果 | トップページ | ハッシュ値計算を途中経過取得可能な様に記述する »