Найкращий спосіб перетворити Паскаль Кейс на речення


75

Який найкращий спосіб перетворити з Паскаля (верхній регістр верблюда) на речення.

Наприклад, починаючи з

"AwaitingFeedback"

і перетворюючи це на

"Awaiting feedback"

C # кращий, але я міг би перетворити його з Java або подібного.


2
Справа верблюда очікує зворотного зв'язку, а не очікує зворотного зв'язку (справа Паскаля). Крім того, те, що ви хочете зробити, не цілком можливо. Як щодо відключенняGPS? Чи існує достатньо загальне рішення для вирішення цих випадків?
kgiannakakis

@kgiannakakis відповідно змінив питання. Я завжди забуваю, в який бік імена, особливо з верхнім і нижнім верблюдом.
Гаррі Шатлер

Відповіді:


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

У версіях візуальної студії після 2015 року ви можете це зробити

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

Засновано на: Перетворення регістру Паскаль у речення за допомогою регулярного виразу


17

Я вважаю за краще використовувати для цього гуманізатор . Humanizer - це портативна бібліотека класів, яка відповідає усім вашим потребам .NET для маніпулювання та відображення рядків, переліків, дат, часу, часових інтервалів, чисел та величин.

Коротка відповідь

"AwaitingFeedback".Humanize() => Awaiting feedback

Довга та описова відповідь

Гуманізатор може зробити набагато більше роботи. Інші приклади:

"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"

Повний код:

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));
        }
    }
}

Вихідні дані

Очікування зворотного зв’язку

Рядок введення регістру Паскаля перетворюється на речення

Підкреслений вхідний рядок перетворюється на речення Can Return Title Case

може повернути малу літеру

Якщо ви віддаєте перевагу писати свій власний код C #, ви можете досягти цього, написавши деякі матеріали з коду C #, як відповіли інші.


16

Це працює для мене:

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

5
Як це змінює регістр букви після пробілу?
Дрю Ноукс

1
Це не може повернути те , що ви збираєтеся в таких випадках , як AwaitingTFeedbackі Awaiting9Feedback. Відповідь Джефа для мене краща (що повертається Awaiting T Feedbackі Awaiting9 Feedbackвідповідно).
nawfal

15

Ось ідіть ...

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();
        }
    }
}

1
Вам слід ToUpper () першого персонажа, інакше ваша рутина не працює з справжнім camelCase, лише PascalCase
David Wengier

Так, приємний улов, я знав, що щось не так (але не міг накласти на це пальцем), коли він сказав: "Очікуючи зворотного зв'язку" - це справа верблюда!
Автодиктакт

9

Це так само, як @SSTA, але є більш ефективним, ніж виклик TrimStart.

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

9

Знайшов це у джерелі MvcContrib, схоже, тут ще не згадується.

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

4

Ось основний спосіб зробити це, який я придумав, використовуючи 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();
}

Він також розділить номери, які я не вказав, але був би корисний.


4

Тільки тому, що всі використовували регулярний вираз (крім цього хлопця ), ось реалізація, StringBuilderяка в моїх тестах була приблизно в 5 разів швидшою . Включає також перевірку номерів.

"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();
}

2

Я б використав регулярний вираз, вставляючи пробіл перед кожним символом верхнього регістру, а потім опускаючи весь рядок.

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

Я не знаю C #, але я не думаю, що екрани типу \ s є законними в розділі заміни: як мова дізнається, чи повинна вона вставляти пробіл, вкладку чи щось інше? :-)
Філо

Ви маєте рацію, простіше було б замінити його чітким "".
Антуан

Єдине, що я б сказав, це дасть "очікування зворотного зв'язку"
Гаррі Шатлер,

Ну ви обов'язково повинні видалити перший пробіл і верхній перший символ. Можливо, додайте "\ B" перед шаблоном, щоб не збігатися з першим символом.
Антуан

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

Редагувати: не помітив ваших вимог до корпусу, змінений відповідно. Ви можете використовувати обчислювач матчів, щоб зробити кожух, але я думаю, що підрядок простіше. Ви також можете обернути його в 2-й замін регулярного виразу, де ви зміните перший символ

"^\w"

до верхнього

\U (i think)

2

Це легко зробити в JavaScript (або PHP тощо), де ви можете визначити функцію у виклику replace:

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

Хоча я не вирішив початкову проблему обмеження ... :-)

Тепер щодо рішення Java:

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"));

Зверніть увагу на хитрість розділити речення, не втрачаючи жодного символу ...


1

Псевдокод:

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

Кращі способи, можливо, можна зробити, використовуючи регулярний вираз або за допомогою процедур заміни рядків (замінити "X" на "x")


1

Рішення xquery, яке працює як для UpperCamel, так і для lowerCamel:

Для виведення регістру речення (лише перший символ першого слова пишеться з великої літери):

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')))
};

Щоб вивести регістр заголовка (перший символ кожного слова з великої літери):

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'))
};

1

Я виявив, що роблю щось подібне, і я вдячний, що мав вихідну точку з цієї дискусії. Це моє рішення, розміщене як метод розширення класу рядків у контексті консольної програми.

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();
        }
    }
}

1

Більшість попередніх відповідей розділяють абревіатури та цифри, додаючи пробіл перед кожним символом. Я хотів, щоб абревіатури та числа зберігалися разом, тому я маю простий автомат, який виділяє простір кожного разу, коли вхід переходить з одного стану в інший.

    /// <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();
    }

І ось кілька тестів:

    [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);
    }

Приємна відповідь. У своєму проекті я додав bool nextIsLower = i > 0 && i + 1 < source.Length && char.IsLower(source[i + 1]);і змінив вираз if на if ((!upper || nextIsLower) && isUpperOrDigit). Це відокремлює абревіатури від слів, тому CustomABCWith123 стає Custom ABC With 123 замість Custom ABCWith 123 . Можуть бути випадки, якими я не займався, і, звичайно, А і я не працюю.
stritch000

0

Тут переважно вже відповідали

Невелика зміна прийнятої відповіді, щоб перетворити другу та наступні великі літери на малі регістри, тому змініть

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

до

if (char.IsUpper(text[i]))                
{
    newText.Append(' ');            
    newText.Append(char.ToLower(text[i]));
}
else
   newText.Append(text[i]);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.