キャメルケースな文字をスネークケースに変換する際に、
正規表現を利用して変換するコードがよく見かけますが、
ここではあえて正規表現を使わないで分割を考えることにします。
前提として変換する対象の文字列は英字の大文字小文字のみで構成されているものとします。
単語に分割するということ
ある半角英数からなる文字列を単語に分割するということは、
単語の区切りとなる判断材料が必要になってきます。
キャメルケースの定義を一言でいうとこんな感じでしょうか。
キャメルケースとは、アルファベットで複合語やフレーズを表記する際、各単語や要素語の先頭の文字を大文字で表記する手法のことである。
すなわち、小文字から大文字に切り替わった時に単語の区切りになるということになります。
C#で実装する
以上のことを踏まえて、C#で実装する場合こんな感じになります。
public static IEnumerable<string> ToWords(this string source)
{
// 作業用変数の宣言
var wordbreakIndex = 0; // 現在の単語の始まりのインデックス
var currentWordLength = 0; // 現在の単語の文字数
var current = '\0'; // ループの中で現在参照している文字
var isLowerBefore = false; // 一つ前の文字が小文字だったかどうか
var isUpperCurrent = false; // 現在の文字が大文字かどうか
for(var i = 0; i < source.Length; i++)
{
current = source[i];
isUpperCurrent = char.IsUpper(current);
if(isLowerBefore && isUpperCurrent)
{
// 小文字から大文字に切り替わった時に単語を切り出す。
yield return source.Substring(wordbreakIndex, currentWordLength);
wordbreakIndex = i;
currentWordLength = 0;
}
currentWordLength++;
isLowerBefore = char.IsLower(current);
}
// 最後の単語の返却漏れがないように
yield return source.Substring(wordbreakIndex, source.Length - wordbreakIndex);
}
動作確認・パターンの追加をする
テストパターンが増えるに連れて、どんどんとデグレ検証が大変になるのでテストコードも記述しておきます。
[TestClass]
public class Splitter_ToWordShould
{
[TestMethod]
public void ReturnSplittedWord()
{
ReturnSplittedWordInternal("camelCase", "camel", "Case");
}
private void ReturnSplittedWordInternal(string source, params string[] expected)
{
// Arrange
// Nothing to do...
// Act
IEnumerable<string> actual = source.ToWords();
// Assert
Assert.IsTrue(expected.SequenceEqual(actual));
}
}
テストが通ることを確認します。
余談ですが、テストコードを作成する際にDataTestMethodAttributeを利用して記述しようとしましたが、
paramsを利用するとどうも例外が発生して、解決に時間がかかりそうだったので、上記のようなテストコードになっています。
テストパターンを追加するにもReturnSplittedWordInternal
の呼び出しの箇所を追加すればいいだけになりますので。
テストパターンを追加します。
[TestMethod]
public void ReturnSplittedWord()
{
ReturnSplittedWordInternal("camelCase", "camel", "Case");
+ ReturnSplittedWordInternal("PascalCase", "Pascal", "Case");
}
追加してもテストが成功することを確認します。
まとめ
このように、コーディング量は正規表現を使うより遥かに多いですが、割と簡単に実装することができます。
今回は簡単にするために入力される文字列が英字の大文字小文字のみとしましたが、
- そのほかの文字の入力の追加。
- 例えば数字やカンマ・アンダーバー・パイプ・空白などの区切り文字
- 特定の大文字で構成される単語が混じった場合の考慮
-
MicrosoftSQLServer
のような文字列が入力された場合、
Microsoft
・SQL
・Server
と分割できるように対応する
-
以上のような対応をすると、camelCaseからsnake_caseに変換できるようなメソッドが作れるようになります。