文字列に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」カテゴリの記事
- XMLスキーマ定義に従ってXMLファイルを読み込む(2)(2012.10.28)
- XMLスキーマ定義に従ってXMLファイルを読み込む(1)(2012.10.18)
- OLEドラッグ&ドロップ対応にする方法(オブジェクト編)(2011.02.04)
- OLEドラッグ&ドロップ対応にする方法(ファイル編)(2011.01.28)
この記事へのコメントは終了しました。
« 7-zip32.dllでサロゲートペア文字を使用した場合の動作結果 | トップページ | ハッシュ値計算を途中経過取得可能な様に記述する »
コメント