C# Tips | 文字列処理:ケバブケース変換

C# C#
スポンサーリンク

はじめに 「ケバブケース変換」は“フロントエンド世界との共通語”

C# の世界では UserName(PascalCase)や userName(camelCase)が主流ですが、
フロントエンドや一部の Web API の世界では、user-name のような「kebab-case(ケバブケース)」がよく使われます。

CSS のプロパティ名(background-color
一部の REST API のパラメータ(user-id
JavaScript のオプション名や設定ファイル

こういう「ケバブケース文化」と、C# の PascalCase/camelCase の間をつないでくれるのが、
「ケバブケース変換ユーティリティ」です。

ここでは、プログラミング初心者向けに、

ケバブケースとは何か
PascalCase/camelCase から kebab-case への変換ルール
実務で使える変換ユーティリティの実装
略語(ID, URL など)や null への安全な対応

を、例題付きでかみ砕いて説明していきます。


ケバブケースのルールを言葉で整理する

まず、「kebab-case(ケバブケース)」を言葉で定義しておきます。

すべて小文字で書く。
単語の区切りはハイフン - でつなぐ。

例としては、

user-name
order-id
created-at

などです。

snake_case(user_name)との違いは、「区切りが _- か」だけです。
つまり、「snake_case 変換が分かれば、ケバブケース変換もほぼ同じノリで書ける」と思って大丈夫です。


PascalCase/camelCase → kebab-case の基本アルゴリズム

考え方のベースは「スネークケース変換」と同じ

UserNameuserNameuser-name にしたいとき、やりたいことはこうです。

文字列を左から 1 文字ずつ見る。
「小文字 → 大文字」に切り替わるところを「単語の境目」とみなす。
境目の前に - を挟む。
最後に全部小文字にする。

snake_case のときは _ を挟みましたが、ここではそれを - に変えるだけです。

まずはシンプル実装から

using System;
using System.Text;

public static class KebabCaseConverter
{
    public static string ToKebabCase(string? value)
    {
        if (string.IsNullOrWhiteSpace(value))
        {
            return string.Empty;
        }

        string trimmed = value.Trim();
        var sb = new StringBuilder();
        char[] chars = trimmed.ToCharArray();

        for (int i = 0; i < chars.Length; i++)
        {
            char c = chars[i];

            bool isUpper = char.IsUpper(c);

            if (i > 0 && isUpper)
            {
                char prev = chars[i - 1];

                bool prevIsLower = char.IsLower(prev);
                bool prevIsDigit = char.IsDigit(prev);

                if (prevIsLower || prevIsDigit)
                {
                    sb.Append('-');
                }
            }

            sb.Append(char.ToLowerInvariant(c));
        }

        return sb.ToString();
    }
}
C#

ここでの重要ポイントは次の通りです。

null や空白だけの入力は、例外にせず空文字にそろえる。
前後の空白は Trim() で落としてから処理する。
「現在の文字が大文字」かつ「直前が小文字または数字」のときだけ - を挟む。
最後に char.ToLowerInvariant で小文字にそろえる。

このルールだけで、かなり自然な kebab-case に変換できます。


動作確認とパターン別の挙動

代表的な例を試してみる

Console.WriteLine(KebabCaseConverter.ToKebabCase("UserName"));      // user-name
Console.WriteLine(KebabCaseConverter.ToKebabCase("userName"));      // user-name
Console.WriteLine(KebabCaseConverter.ToKebabCase("OrderID"));       // order-id
Console.WriteLine(KebabCaseConverter.ToKebabCase("CreatedAt"));     // created-at
Console.WriteLine(KebabCaseConverter.ToKebabCase("URL"));           // url
Console.WriteLine(KebabCaseConverter.ToKebabCase("User1Name"));     // user1-name
Console.WriteLine(KebabCaseConverter.ToKebabCase("  UserName  "));  // user-name
Console.WriteLine(KebabCaseConverter.ToKebabCase(null));            // 空文字
C#

ここでの挙動を少し深掘りします。

UserNameuser-name
UserName の境目(rN)で - を挟んでいます。

userNameuser-name
先頭が小文字でも同じルールで動きます。

OrderIDorder-id
OrderID の境目(rI)で - を挟み、ID は 1 単語として扱われます。

URLurl
全部大文字なので、途中に境目がないとみなして、そのまま 1 単語として小文字化します。

User1Nameuser1-name
1 は数字なので、「数字 → 大文字」の境目でも - を挟んでいます。

この挙動は、CSS や多くの Web API の命名とかなり相性が良いです。


略語(ID, URL, API など)をどう扱うか

「全部大文字のブロックは 1 単語」とみなす

OrderIDURLPath のような名前は、略語が絡む典型パターンです。

今の実装だと、

OrderIDorder-id
URLPathurl-path

となります。

URLPath の場合、「URL」が 1 ブロック、「Path」が 1 ブロックとして扱われ、
その境目に - が入るイメージです。

多くの実務では、この挙動で十分自然です。

もし、「URLPathurlpath にしたい」といった特殊な要件があるなら、
それは個別の例外ルールとして別途処理する必要がありますが、
汎用ユーティリティとしては、今のルールがバランスの良い落としどころです。


既に snake_case のものを kebab-case にしたい場合

_ を - に置き換えるだけで済むケース

すでに user_name のような snake_case になっている文字列を、
user-name にしたいだけなら、話はもっとシンプルです。

やることは、

全部小文字にそろえる(念のため)
_- に置き換える

だけです。

public static class KebabCaseConverter
{
    public static string SnakeToKebab(string? value)
    {
        if (string.IsNullOrWhiteSpace(value))
        {
            return string.Empty;
        }

        string lower = value.Trim().ToLowerInvariant();
        return lower.Replace('_', '-');
    }
}
C#

例です。

Console.WriteLine(KebabCaseConverter.SnakeToKebab("user_name"));   // user-name
Console.WriteLine(KebabCaseConverter.SnakeToKebab("USER_ID"));     // user-id
Console.WriteLine(KebabCaseConverter.SnakeToKebab("  created_at ")); // created-at
C#

「DB のカラム名(snake_case)を、そのまま API のパラメータ名(kebab-case)にしたい」
といった場面では、このくらいのユーティリティで十分です。


実務での使いどころと設計のポイント

どこで kebab-case に変換するかを決める

ケバブケース変換も、「どの層でやるか」を決めておくと設計がスッキリします。

フロントエンド向けの API を作るとき。
内部では PascalCase/camelCase で持っておき、
JSON のキーやクエリパラメータを組み立てる直前で kebab-case に変換する。

設定ファイルやオプション名を扱うとき。
C# のプロパティ名は PascalCase のままにしておき、
設定ファイル側のキー(kebab-case)とのマッピングに変換ユーティリティを使う。

例えば、こんなイメージです。

string propertyName = "UserName";
string optionKey = KebabCaseConverter.ToKebabCase(propertyName); // user-name

// 設定ファイルから "user-name" を探す、など
C#

「C# の世界の命名」と「外の世界の命名」を、
ケバブケース変換ユーティリティで橋渡しするイメージを持っておくと、
どこに置くべき処理かが見えやすくなります。


まとめ 「ケバブケース変換ユーティリティ」は“フロントとバックの言語通訳”

kebab-case 変換は、単なる記号遊びではなく、

「C#(PascalCase/camelCase)と、フロントエンドや外部APIの命名ルールの違いを吸収する通訳」

として機能します。

押さえておきたいポイントはこうです。

kebab-case は「全部小文字+単語区切りは -」というシンプルなルール。
PascalCase/camelCase → kebab-case は、「大文字の前に - を挟んで、最後に全部小文字にする」ロジックで実装できる。
null や空白だけの入力は、例外にせず空文字にそろえると、呼び出し側が楽になる。
略語(ID, URL など)は、「全部大文字のブロックは 1 単語」とみなすシンプルなルールで、多くの実務ケースをカバーできる。
「どの層で kebab-case に変換するか」(API 出力、設定ファイルとのマッピングなど)を決めておくと、命名の一貫性が保ちやすい。

ここまで理解できれば、「手で user-name と書いている」段階から一歩進んで、
「C# の名前から自動で kebab-case を生成する」実務的なユーティリティを、自分の手で設計・実装できるようになっていきます。

タイトルとURLをコピーしました