はじめに
半角カタカナは何かと使い勝手が悪い文字ですので、全角カタカナに置き換えたいと思います。
但し、他の半角文字(数字やアルファベット)は変換したくありません。半角なら半角、全角なら全角のままにしたいので、StrConvを使いませんでした。
いろいろなやり方があると思いますが、文字コードで範囲を指定するなどのテクニカルな方法は避け、べたな方法で対処しました。
定数宣言
変換元のkanakeysと変換後のkanavaluesをstaticに用意します。
濁音、半濁音の扱いが面倒です。
また、存在チェックのためだけに、static List kanakeyListを用意しました。
なお、ヰヱの半角文字は無いみたいです。
private static readonly string[] kanakeys = { "ヴ", "ガ", "ギ", "グ", "ゲ", "ゴ", "ザ", "ジ", "ズ", "ゼ", "ゾ", "ダ", "ヂ", "ヅ", "デ", "ド", "バ", "ビ", "ブ", "ベ", "ボ", "パ", "ピ", "プ", "ペ", "ポ", "ア", "イ", "ウ", "エ", "オ", "カ", "キ", "ク", "ケ", "コ", "サ", "シ", "ス", "セ", "ソ", "タ", "チ", "ツ", "テ", "ト", "ナ", "ニ", "ヌ", "ネ", "ノ", "ハ", "ヒ", "フ", "ヘ", "ホ", "マ", "ミ", "ム", "メ", "モ", "ヤ", "ユ", "ヨ", "ラ", "リ", "ル", "レ", "ロ", "ワ", "ヲ", "ン", "ャ", "ュ", "ョ", "ァ", "ィ", "ェ", "ォ", "゙", "゚", "、", "。", "ー" };
private static readonly string[] kanavalues = { "ヴ", "ガ", "ギ", "グ", "ゲ", "ゴ", "ザ", "ジ", "ズ", "ゼ", "ゾ", "ダ", "ヂ", "ヅ", "デ", "ド", "バ", "ビ", "ブ", "ベ", "ボ", "パ", "ピ", "プ", "ペ", "ポ", "ア", "イ", "ウ", "エ", "オ", "カ", "キ", "ク", "ケ", "コ", "サ", "シ", "ス", "セ", "ソ", "タ", "チ", "ツ", "テ", "ト", "ナ", "ニ", "ヌ", "ネ", "ノ", "ハ", "ヒ", "フ", "ヘ", "ホ", "マ", "ミ", "ム", "メ", "モ", "ヤ", "ユ", "ヨ", "ラ", "リ", "ル", "レ", "ロ", "ワ", "ヲ", "ン", "ャ", "ュ", "ョ", "ァ", "ィ", "ェ", "ォ", "゛", "゜", "、", "。", "ー" };
private static readonly List<char> kanakeyList = new List<char> { 'ア', 'イ', 'ウ', 'エ', 'オ', 'カ', 'キ', 'ク', 'ケ', 'コ', 'サ', 'シ', 'ス', 'セ', 'ソ', 'タ', 'チ', 'ツ', 'テ', 'ト', 'ナ', 'ニ', 'ヌ', 'ネ', 'ノ', 'ハ', 'ヒ', 'フ', 'ヘ', 'ホ', 'マ', 'ミ', 'ム', 'メ', 'モ', 'ヤ', 'ユ', 'ヨ', 'ラ', 'リ', 'ル', 'レ', 'ロ', 'ワ', 'ヲ', 'ン', 'ャ', 'ュ', 'ョ', 'ァ', 'ィ', 'ェ', 'ォ', '゙', '゚', '、', '。', 'ー' };
メソッド
public static string KanaToFull(string target)
{
Debug.WriteLine("in: [" + target + "]");
DateTime dtStart = DateTime.Now;//開始
if(target.Where(x => kanakeyList.Contains(x)).Count() > 0)//対象かどうかチェック
{
for (int idx = 0; idx < kanavalues.Length; idx++)
{
target = target.Replace(kanakeys[idx], kanavalues[idx]);
}
}
DateTime dtEnd = DateTime.Now;//終了
TimeSpan timeSpan = dtEnd - dtStart;//速度測定
Debug.WriteLine("out:[" + target + "]");
Debug.WriteLine(timeSpan.TotalMilliseconds.ToString());
return target;
}
Debug.WriteLine、DateTime、TimeSpan は速度測定用なので、実装時は不要です。
そうすると本体は、以下だけになります。
if(target.Where(x => kanakeyList.Contains(x)).Count() > 0)//対象かどうかチェック
{
for (int idx = 0; idx < kanavalues.Length; idx++)
{
target = target.Replace(kanakeys[idx], kanavalues[idx]);
}
}
少しでも速度を上げるために、kanakeyListに含まれる文字がtargetに存在するかどうかのチェックを行っています。必ず半角カナがあるのでチェックの必要が無い、targetが短くて意識する必要が無い、などであれば、却って速度を低下させるので、取り除いても良いかと思います。
後は、kanakeysの文字列をkanavaluesの文字列にReplaceしているだけです。
kanavalues.Length回Replaceを実行するので速度が気になりますね。
速度測定
試みに、吾輩は猫であるの冒頭2600文字程度を、漢字を除いて半角カナに変換して実行したところ、私の環境では
1回目 | 2回目 | 3回目 | 4回目 | 5回目 |
---|---|---|---|---|
約5ms | 約0.3ms | 約0.3ms | 約0.3ms | 約0.2ms |
約5ms | 約0.4ms | 約0.3ms | 約0.3ms | 約0.2ms |
でした。一旦終了させて再実験しても1回目は少し遅いので、文書が変数上に残っているのかも知れないと思い、舞姫の冒頭約2000文字程度を同じように半角化して代わりばんこに実行したところ、
1回目 | 2回目 | 3回目 | 4回目 |
---|---|---|---|
約5ms/猫 | 約0.3ms/舞 | 約0.3ms/猫 | 約0.1ms/舞 |
約3ms/舞 | 約0.4ms/猫 | 約0.2ms/舞 | 約0.2ms/猫 |
でした。ひょっとしたら、staticな配列であっても呼び出されるまでメモリ上にはおらず、初回ロードのコストなのかも知れません。検証するのも面倒なのでやりませんけど。
例えば100回このメソッドを呼ぶことがあったとして、初回ロード時のコストが5ms、2回目以降のコストが0.3msとすれば、5+0.3*99≒35ms、0.035秒なので、特に速度を意識する必要はなさそうに思います。
補足
指摘頂いているStringBuilderの件、てっきりAdd系しか速度向上に寄与しないと思っていて、そもそもReplace出来るとは思っていませんでした。ありがとうございます。
ListよりHashSetの方が早いですね。ちょっと雑でした。CountとAnyもそうですね。
switchの方が確かに速いのですが、ソースコードの見やすさとの兼ね合いで、Replaceを回してみました。
ご指摘ありがとうございます。