Skip to content

Instantly share code, notes, and snippets.

@vkobel
Created August 7, 2014 14:22
Show Gist options
  • Star 36 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save vkobel/d7302c0076c64c95ef4b to your computer and use it in GitHub Desktop.
Save vkobel/d7302c0076c64c95ef4b to your computer and use it in GitHub Desktop.
Simple C# extension method to convert a camel case string to underscore notation without any regex
public static class ExtensionMethods {
public static string ToUnderscoreCase(this string str) {
return string.Concat(str.Select((x, i) => i > 0 && char.IsUpper(x) ? "_" + x.ToString() : x.ToString())).ToLower();
}
}
@Tseko
Copy link

Tseko commented Sep 13, 2018

Thank you :)

@kofex
Copy link

kofex commented Jun 25, 2019

thx

@escalonn
Copy link

escalonn commented Sep 23, 2019

arguably should convert IOStream to io_stream, but makes it i_o_stream instead.
Package Humanizer.Core solved my use case (with its string.Underscore extension method)

@Meldiron
Copy link

Thanks ♥

@IaroslavTitov
Copy link

Very handy, thanks!

@koubaa
Copy link

koubaa commented Feb 26, 2020

thanks!

@goforgold
Copy link

goforgold commented Jun 19, 2020

Very thanks!

Can you please help me change this in a way so IBTest becomes ib_test not i_b_test?

@goforgold
Copy link

I think I found my answer from 'UseSnakeCaseNamingConvention` from EF Core codebase.

public static string ToSnakeCase(this string name)
{
  if (string.IsNullOrEmpty(name))
    return name;

  var builder = new StringBuilder(name.Length + Math.Min(2, name.Length / 5));
  var previousCategory = default(UnicodeCategory?);

  for (var currentIndex = 0; currentIndex < name.Length; currentIndex++)
  {
    var currentChar = name[currentIndex];
    if (currentChar == '_')
    {
      builder.Append('_');
      previousCategory = null;
      continue;
    }

    var currentCategory = char.GetUnicodeCategory(currentChar);
    switch (currentCategory)
    {
      case UnicodeCategory.UppercaseLetter:
      case UnicodeCategory.TitlecaseLetter:
        if (previousCategory == UnicodeCategory.SpaceSeparator ||
            previousCategory == UnicodeCategory.LowercaseLetter ||
            previousCategory != UnicodeCategory.DecimalDigitNumber &&
            previousCategory != null &&
            currentIndex > 0 &&
            currentIndex + 1 < name.Length &&
            char.IsLower(name[currentIndex + 1]))
        {
          builder.Append('_');
        }

        currentChar = char.ToLower(currentChar);
        break;

      case UnicodeCategory.LowercaseLetter:
      case UnicodeCategory.DecimalDigitNumber:
        if (previousCategory == UnicodeCategory.SpaceSeparator)
          builder.Append('_');
        break;

      default:
        if (previousCategory != null)
          previousCategory = UnicodeCategory.SpaceSeparator;
        continue;
    }

    builder.Append(currentChar);
    previousCategory = currentCategory;
  }

  return builder.ToString();
}

@hatami57
Copy link

hatami57 commented Oct 1, 2021

This improved method solved the mentioned problems in the comments:

public static class ExtensionMethods
{
    
    public static string ToUnderscoreCase(this string str) =>
        string.Concat(str.Select((x, i) => (i > 0 && char.IsUpper(x) && (char.IsLower(str[i - 1]) || char.IsLower(str[i + 1])))
            ? "_" + x.ToString() : x.ToString())).ToLower();
}

@peter-perot
Copy link

peter-perot commented Mar 8, 2023

public static string ToUnderscoreCase(this string str) =>
string.Concat(str.Select((x, i) => (i > 0 && char.IsUpper(x) && (char.IsLower(str[i - 1]) || char.IsLower(str[i + 1])))
? "_" + x.ToString() : x.ToString())).ToLower();

This causes an exception with "abcDeFG".

Here the corrected method:

static string ToUnderscoreCase(this string str) =>
    string.Concat(
        str.Select((x, i) =>
            i > 0 && char.IsUpper(x) && (char.IsLower(str[i - 1]) || i < str.Length - 1 && char.IsLower(str[i + 1]))
                ? "_" + x
                : x.ToString())).ToLowerInvariant();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment