Как преобразовать PascalCase в kebab-case с С#?

Как преобразовать строковое значение в PascalCase (другое имя - UpperCamelCase) в kebab-case с С#?

например. "VeryLongName" до "very-long-name"

Ответ 1

Вот как это сделать с регулярным выражением:

public static class StringExtensions
{
    public static string PascalToKebabCase(this string value)
    {
        if (string.IsNullOrEmpty(value))
            return value;

        return Regex.Replace(
            value,
            "(?<!^)([A-Z][a-z]|(?<=[a-z])[A-Z])",
            "-$1",
            RegexOptions.Compiled)
            .Trim()
            .ToLower();
    }
}

Ответ 2

Вот способ сделать это, не используя Regex:

public static string PascalToKebabCase(this string str)
{
    if (string.IsNullOrEmpty(str))
        return string.Empty;

    var builder = new StringBuilder();
    builder.Append(char.ToLower(str.First()));

    foreach (var c in str.Skip(1))
    {
        if (char.IsUpper(c))
        {
            builder.Append('-');
            builder.Append(char.ToLower(c));
        }
        else
        {
            builder.Append(c);
        }
    }

    return builder.ToString();
}

У вас возникнут проблемы с известными сокращениями, которые используют буквы верхнего регистра. Например: COMObject. Это решение, очевидно, не сработало.

Ответ 3

Вот мое решение, использующее соглашения об использовании заглавных букв Microsoft. https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/capitalization-conventions

public static class StringExtensions
{
    public static string ToKebabCase(this string source)
    {
        if (source is null) return null;

        if(source.Length == 0) return string.Empty;

        StringBuilder builder = new StringBuilder();

        for(var i = 0; i < source.Length; i++)
        {
            if (char.IsLower(source[i])) // if current char is already lowercase
            {
                builder.Append(source[i]);
            }
            else if(i == 0) // if current char is the first char
            {
                builder.Append(char.ToLower(source[i]));
            }
            else if (char.IsLower(source[i - 1])) // if current char is upper and previous char is lower
            {
                builder.Append("-");
                builder.Append(char.ToLower(source[i]));
            }
            else if(i + 1 == source.Length || char.IsUpper(source[i + 1])) // if current char is upper and next char doesn't exist or is upper
            {
                builder.Append(char.ToLower(source[i]));
            }
            else // if current char is upper and next char is lower
            {
                builder.Append("-");
                builder.Append(char.ToLower(source[i]));
            }
        }
        return builder.ToString();
    }
}

Тестовое задание

string[] stringArray = new[]
{
    null,
    "",
    "I",
    "IO",
    "FileIO",
    "SignalR",
    "IOStream",
    "COMObject",
    "WebAPI"
};

foreach (var str in stringArray)
{
    Console.WriteLine($"{str} --> {str.ToKebabCase()}");
}

// Output:
//  -->
//  -->
// I --> i
// IO --> io
// FileIO --> file-io
// SignalR --> signal-r
// IOStream --> io-stream
// COMObject --> com-object
// WebAPI --> web-api