Best way to convert Pascal Case to a sentence

C#String

C# Problem Overview


What is the best way to convert from Pascal Case (upper Camel Case) to a sentence.

For example starting with

"AwaitingFeedback"

and converting that to

"Awaiting feedback"

C# preferable but I could convert it from Java or similar.

C# Solutions


Solution 1 - C#

public static string ToSentenceCase(this string str)
{
    return Regex.Replace(str, "[a-z][A-Z]", m => m.Value[0] + " " + char.ToLower(m.Value[1]));
}

In versions of visual studio after 2015, you can do

public static string ToSentenceCase(this string str)
{
    return Regex.Replace(str, "[a-z][A-Z]", m => $"{m.Value[0]} {char.ToLower(m.Value[1])}");
}

Based on: Converting Pascal case to sentences using regular expression

Solution 2 - C#

I will prefer to use Humanizer for this. Humanizer is a Portable Class Library that meets all your .NET needs for manipulating and displaying strings, enums, dates, times, timespans, numbers and quantities.

Short Answer

"AwaitingFeedback".Humanize() => Awaiting feedback

Long and Descriptive Answer

Humanizer can do a lot more work other examples are:

"PascalCaseInputStringIsTurnedIntoSentence".Humanize() => "Pascal case input string is turned into sentence"
"Underscored_input_string_is_turned_into_sentence".Humanize() => "Underscored input string is turned into sentence"
"Can_return_title_Case".Humanize(LetterCasing.Title) => "Can Return Title Case"
"CanReturnLowerCase".Humanize(LetterCasing.LowerCase) => "can return lower case"

Complete code is :

using Humanizer;
using static System.Console;

namespace HumanizerConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            WriteLine("AwaitingFeedback".Humanize());
            WriteLine("PascalCaseInputStringIsTurnedIntoSentence".Humanize());
            WriteLine("Underscored_input_string_is_turned_into_sentence".Humanize());
            WriteLine("Can_return_title_Case".Humanize(LetterCasing.Title));
            WriteLine("CanReturnLowerCase".Humanize(LetterCasing.LowerCase));
        }
    }
}

Output

> Awaiting feedback > >Pascal case input string is turned into sentence > > Underscored input string is turned into sentence Can Return Title Case > > can return lower case

If you prefer to write your own C# code you can achieve this by writing some C# code stuff as answered by others already.

Solution 3 - C#

Here you go...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CamelCaseToString
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(CamelCaseToString("ThisIsYourMasterCallingYou"));   
        }

        private static string CamelCaseToString(string str)
        {
            if (str == null || str.Length == 0)
                return null;

            StringBuilder retVal = new StringBuilder(32);

            retVal.Append(char.ToUpper(str[0]));
            for (int i = 1; i < str.Length; i++ )
            {
                if (char.IsLower(str[i]))
                {
                    retVal.Append(str[i]);
                }
                else
                {
                    retVal.Append(" ");
                    retVal.Append(char.ToLower(str[i]));
                }
            }

            return retVal.ToString();
        }
    }
}

Solution 4 - C#

This works for me:

Regex.Replace(strIn, "([A-Z]{1,2}|[0-9]+)", " $1").TrimStart()

Solution 5 - C#

This is just like @SSTA, but is more efficient than calling TrimStart.

Regex.Replace("ThisIsMyCapsDelimitedString", "(\\B[A-Z])", " $1")

Solution 6 - C#

Found this in the MvcContrib source, doesn't seem to be mentioned here yet.

return Regex.Replace(input, "([A-Z])", " $1", RegexOptions.Compiled).Trim();

Solution 7 - C#

Here's a basic way of doing it that I came up with using Regex

public static string CamelCaseToSentence(this string value)
{
    var sb = new StringBuilder();
    var firstWord = true;

    foreach (var match in Regex.Matches(value, "([A-Z][a-z]+)|[0-9]+"))
    {
        if (firstWord)
        {
            sb.Append(match.ToString());
            firstWord = false;
        }
        else
        {
            sb.Append(" ");
            sb.Append(match.ToString().ToLower());
        }
    }

    return sb.ToString();
}

It will also split off numbers which I didn't specify but would be useful.

Solution 8 - C#

Just because everyone has been using Regex (except this guy), here's an implementation with StringBuilder that was about 5x faster in my tests. Includes checking for numbers too.

"SomeBunchOfCamelCase2".FromCamelCaseToSentence == "Some Bunch Of Camel Case 2"

public static string FromCamelCaseToSentence(this string input) {
	if(string.IsNullOrEmpty(input)) return input;
	
	var sb = new StringBuilder();
	// start with the first character -- consistent camelcase and pascal case
	sb.Append(char.ToUpper(input[0]));
	
	// march through the rest of it
	for(var i = 1; i < input.Length; i++) {
		// any time we hit an uppercase OR number, it's a new word
		if(char.IsUpper(input[i]) || char.IsDigit(input[i])) sb.Append(' ');
		// add regularly
		sb.Append(input[i]);
	}
	
	return sb.ToString();
}

Solution 9 - C#

string camel = "MyCamelCaseString";
string s = Regex.Replace(camel, "([A-Z])", " $1").ToLower().Trim();
Console.WriteLine(s.Substring(0,1).ToUpper() + s.Substring(1));

Edit: didn't notice your casing requirements, modifed accordingly. You could use a matchevaluator to do the casing, but I think a substring is easier. You could also wrap it in a 2nd regex replace where you change the first character

"^\w"

to upper

\U (i think)

Solution 10 - C#

I'd use a regex, inserting a space before each upper case character, then lowering all the string.

    string spacedString = System.Text.RegularExpressions.Regex.Replace(yourString, "\B([A-Z])", " \k");
    spacedString = spacedString.ToLower();

Solution 11 - C#

It is easy to do in JavaScript (or PHP, etc.) where you can define a function in the replace call:

var camel = "AwaitingFeedbackDearMaster";
var sentence = camel.replace(/([A-Z].)/g, function (c) { return ' ' + c.toLowerCase(); });
alert(sentence);

Although I haven't solved the initial cap problem... :-)

Now, for the Java solution:

String ToSentence(String camel)
{
  if (camel == null) return ""; // Or null...
  String[] words = camel.split("(?=[A-Z])");
  if (words == null) return "";
  if (words.length == 1) return words[0];
  StringBuilder sentence = new StringBuilder(camel.length());
  if (words[0].length() > 0) // Just in case of camelCase instead of CamelCase
  {
    sentence.append(words[0] + " " + words[1].toLowerCase());
  }
  else
  {
    sentence.append(words[1]);
  }
  for (int i = 2; i < words.length; i++)
  {
    sentence.append(" " + words[i].toLowerCase());
  }
  return sentence.toString();
}

System.out.println(ToSentence("AwaitingAFeedbackDearMaster"));
System.out.println(ToSentence(null));
System.out.println(ToSentence(""));
System.out.println(ToSentence("A"));
System.out.println(ToSentence("Aaagh!"));
System.out.println(ToSentence("stackoverflow"));
System.out.println(ToSentence("disableGPS"));
System.out.println(ToSentence("Ahh89Boo"));
System.out.println(ToSentence("ABC"));

Note the trick to split the sentence without loosing any character...

Solution 12 - C#

Pseudo-code:

NewString = "";
Loop through every char of the string (skip the first one)
   If char is upper-case ('A'-'Z')
     NewString = NewString + ' ' + lowercase(char)
   Else
     NewString = NewString + char

Better ways can perhaps be done by using regex or by string replacement routines (replace 'X' with ' x')

Solution 13 - C#

An xquery solution that works for both UpperCamel and lowerCamel case:

To output sentence case (only the first character of the first word is capitalized):

declare function content:sentenceCase($string)
{
let $firstCharacter := substring($string, 1, 1)
let $remainingCharacters := substring-after($string, $firstCharacter)
return
concat(upper-case($firstCharacter),lower-case(replace($remainingCharacters, '([A-Z])', ' $1')))
};

To output title case (first character of each word capitalized):

declare function content:titleCase($string)
{
let $firstCharacter := substring($string, 1, 1)
let $remainingCharacters := substring-after($string, $firstCharacter)
return
concat(upper-case($firstCharacter),replace($remainingCharacters, '([A-Z])', ' $1'))
};

Solution 14 - C#

Found myself doing something similar, and I appreciate having a point-of-departure with this discussion. This is my solution, placed as an extension method to the string class in the context of a console application.

using System;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string piratese = "avastTharMatey";
            string ivyese = "CheerioPipPip";

            Console.WriteLine("{0}\n{1}\n", piratese.CamelCaseToString(), ivyese.CamelCaseToString());
            Console.WriteLine("For Pete\'s sake, man, hit ENTER!");
            string strExit = Console.ReadLine();
        }

    }

    public static class StringExtension
    {
        public static string CamelCaseToString(this string str)
        {
            StringBuilder retVal = new StringBuilder(32);

            if (!string.IsNullOrEmpty(str))
            {
                string strTrimmed = str.Trim();

                if (!string.IsNullOrEmpty(strTrimmed))
                {
                    retVal.Append(char.ToUpper(strTrimmed[0]));

                    if (strTrimmed.Length > 1)
                    {
                        for (int i = 1; i < strTrimmed.Length; i++)
                        {
                            if (char.IsUpper(strTrimmed[i])) retVal.Append(" ");

                            retVal.Append(char.ToLower(strTrimmed[i]));
                        }
                    }
                }
            }
            return retVal.ToString();
        }
    }
}

Solution 15 - C#

Most of the preceding answers split acronyms and numbers, adding a space in front of each character. I wanted acronyms and numbers to be kept together so I have a simple state machine that emits a space every time the input transitions from one state to the other.

    /// <summary>
    /// Add a space before any capitalized letter (but not for a run of capitals or numbers)
    /// </summary>
    internal static string FromCamelCaseToSentence(string input)
    {
        if (string.IsNullOrEmpty(input)) return String.Empty;

        var sb = new StringBuilder();
        bool upper = true;

        for (var i = 0; i < input.Length; i++)
        {
            bool isUpperOrDigit = char.IsUpper(input[i]) || char.IsDigit(input[i]);
            // any time we transition to upper or digits, it's a new word
            if (!upper && isUpperOrDigit)
            {
                sb.Append(' ');
            }
            sb.Append(input[i]);
            upper = isUpperOrDigit;
        }

        return sb.ToString();
    }

And here's some tests:

    [TestCase(null, ExpectedResult = "")]
    [TestCase("", ExpectedResult = "")]
    [TestCase("ABC", ExpectedResult = "ABC")]
    [TestCase("abc", ExpectedResult = "abc")]
    [TestCase("camelCase", ExpectedResult = "camel Case")]
    [TestCase("PascalCase", ExpectedResult = "Pascal Case")]
    [TestCase("Pascal123", ExpectedResult = "Pascal 123")]
    [TestCase("CustomerID", ExpectedResult = "Customer ID")]
    [TestCase("CustomABC123", ExpectedResult = "Custom ABC123")]
    public string CanSplitCamelCase(string input)
    {
        return FromCamelCaseToSentence(input);
    }

Solution 16 - C#

Mostly already answered here

Small chage to the accepted answer, to convert the second and subsequent Capitalised letters to lower case, so change

if (char.IsUpper(text[i]))                
    newText.Append(' ');            
newText.Append(text[i]);

to

if (char.IsUpper(text[i]))                
{
    newText.Append(' ');            
    newText.Append(char.ToLower(text[i]));
}
else
   newText.Append(text[i]);

Solution 17 - C#

Here is my implementation. This is the fastest that I got while avoiding creating spaces for abbreviations.

public static string PascalCaseToSentence(string input)
    {
        if (string.IsNullOrEmpty(input) || input.Length < 2)
            return input;

        var sb = new char[input.Length + ((input.Length + 1) / 2)];
        var len = 0;
        var lastIsLower = false;
        for (int i = 0; i < input.Length; i++)
        {
            var current = input[i];
            if (current < 97)
            {
                if (lastIsLower)
                {
                    sb[len] = ' ';
                    len++;
                }
                lastIsLower = false;
            }
            else
            {
                lastIsLower = true;
            }
            sb[len] = current;

            len++;
        }

        return new string(sb, 0, len);
    }

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionGarry ShutlerView Question on Stackoverflow
Solution 1 - C#StevenView Answer on Stackoverflow
Solution 2 - C#Banketeshvar NarayanView Answer on Stackoverflow
Solution 3 - C#AutodidactView Answer on Stackoverflow
Solution 4 - C#SSTAView Answer on Stackoverflow
Solution 5 - C#Bryan LegendView Answer on Stackoverflow
Solution 6 - C#JefClaesView Answer on Stackoverflow
Solution 7 - C#Garry ShutlerView Answer on Stackoverflow
Solution 8 - C#drzausView Answer on Stackoverflow
Solution 9 - C#Andrew BullockView Answer on Stackoverflow
Solution 10 - C#AntoineView Answer on Stackoverflow
Solution 11 - C#PhiLhoView Answer on Stackoverflow
Solution 12 - C#schnaaderView Answer on Stackoverflow
Solution 13 - C#FraserView Answer on Stackoverflow
Solution 14 - C#sscheiderView Answer on Stackoverflow
Solution 15 - C#Ian MercerView Answer on Stackoverflow
Solution 16 - C#Binary WorrierView Answer on Stackoverflow
Solution 17 - C#juanoraView Answer on Stackoverflow