Добавить пробелы перед "Капитальными письмами"

Учитывая строку "ThisStringHasNoSpacesButItDoesHaveCapitals", что является лучшим способом добавить пробелы перед заглавными буквами. Таким образом, конечная строка будет "Эта строка не имеет пространств, но имеет ли капитал"

Вот моя попытка с RegEx

System.Text.RegularExpressions.Regex.Replace(value, "[A-Z]", " $0")

Ответ 1

Регулярные выражения будут работать нормально (я даже проголосовал за ответ Мартина Брауна), но они дороги (и лично я нахожу любой шаблон длиннее, чем пару символов, которые неистово тупые)

Эта функция

string AddSpacesToSentence(string text, bool preserveAcronyms)
{
        if (string.IsNullOrWhiteSpace(text))
           return string.Empty;
        StringBuilder newText = new StringBuilder(text.Length * 2);
        newText.Append(text[0]);
        for (int i = 1; i < text.Length; i++)
        {
            if (char.IsUpper(text[i]))
                if ((text[i - 1] != ' ' && !char.IsUpper(text[i - 1])) ||
                    (preserveAcronyms && char.IsUpper(text[i - 1]) && 
                     i < text.Length - 1 && !char.IsUpper(text[i + 1])))
                    newText.Append(' ');
            newText.Append(text[i]);
        }
        return newText.ToString();
}

Будет делать это 100 000 раз в 2 968 750 тиках, регулярное выражение будет принимать 25 000 000 тиков (и это с компиляцией регулярных выражений).

Это лучше, для заданного значения лучше (то есть быстрее), однако это больше кода для поддержки. "Лучше" часто является компромиссом конкурирующих требований.

Надеюсь, что это поможет:)

Обновление
Это долгое время, так как я посмотрел на это, и я просто понял, что тайминги не обновлялись с тех пор, как код изменился (он немного изменился).

В строке с аббревиатурой Abbbbbbbb, повторяющейся 100 раз (т.е. 1000 байт), запуск 100 000 преобразований выполняет функцию ручного кодирования 4,517,177 тиков, а нижележащее Regex берет 59 435 719, заставляя функцию кодирования вручную работать в 7,6% случаев он принимает регулярное выражение.

Обновление 2 Учитываются ли эти сокращения? Это будет сейчас! Логика if-состояния довольно неясна, так как вы можете расширить ее до этого...

if (char.IsUpper(text[i]))
    if (char.IsUpper(text[i - 1]))
        if (preserveAcronyms && i < text.Length - 1 && !char.IsUpper(text[i + 1]))
            newText.Append(' ');
        else ;
    else if (text[i - 1] != ' ')
        newText.Append(' ');

... не помогает вообще!

Здесь оригинальный простой метод, который не беспокоится об Акронимах

string AddSpacesToSentence(string text)
{
        if (string.IsNullOrWhiteSpace(text))
           return "";
        StringBuilder newText = new StringBuilder(text.Length * 2);
        newText.Append(text[0]);
        for (int i = 1; i < text.Length; i++)
        {
            if (char.IsUpper(text[i]) && text[i - 1] != ' ')
                newText.Append(' ');
            newText.Append(text[i]);
        }
        return newText.ToString();
}

Ответ 2

У вашего решения есть проблема в том, что он помещает пробел перед первой буквой T, чтобы вы получили

" This String..." instead of "This String..."

Чтобы обойти это, обратите внимание на строчную букву, предшествующую ей, а затем вставьте пространство посередине:

newValue = Regex.Replace(value, "([a-z])([A-Z])", "$1 $2");

Изменить 1:

Если вы используете @"(\p{Ll})(\p{Lu})", он также получит символы с акцентом.

Изменить 2:

Если ваши строки могут содержать аббревиатуры, вы можете использовать это:

newValue = Regex.Replace(value, @"((?<=\p{Ll})\p{Lu})|((?!\A)\p{Lu}(?>\p{Ll}))", " $0");

Итак, "DriveIsSCSICompatible" становится "Дисковод совместим с SCSI"

Ответ 3

Не тестировал производительность, но здесь в одной строке с linq:

var val = "ThisIsAStringToTest";
val = string.Concat(val.Select(x => Char.IsUpper(x) ? " " + x : x.ToString())).TrimStart(' ');

Ответ 4

Я знаю, что это старый, но это расширение, которое я использую, когда мне нужно сделать это:

public static class Extensions
{
    public static string ToSentence( this string Input )
    {
        return new string(Input.SelectMany((c, i) => i > 0 && char.IsUpper(c) ? new[] { ' ', c } : new[] { c }).ToArray());
    }
}

Это позволит вам использовать MyCasedString.ToSentence()

Ответ 5

Добро пожаловать в Юникод

Все эти решения, по сути, неверны для современного текста. Вам нужно использовать то, что понимает дело. Поскольку Боб попросил другие языки, я дам пару для Perl.

Я предоставляю четыре решения: от худшего до лучшего. Только лучший из них всегда прав. У других проблемы. Вот тестовый прогон, показывающий вам, что работает, а что нет, и где. Я использовал подчеркивания, чтобы вы могли видеть, где были помещены пробелы, и Ive обозначил как неправильное все, что угодно, ну, неправильно.

Testing TheLoneRanger
               Worst:    The_Lone_Ranger
               Ok:       The_Lone_Ranger
               Better:   The_Lone_Ranger
               Best:     The_Lone_Ranger
Testing MountMᶜKinleyNationalPark
     [WRONG]   Worst:    Mount_MᶜKinley_National_Park
     [WRONG]   Ok:       Mount_MᶜKinley_National_Park
     [WRONG]   Better:   Mount_MᶜKinley_National_Park
               Best:     Mount_Mᶜ_Kinley_National_Park
Testing ElÁlamoTejano
     [WRONG]   Worst:    ElÁlamo_Tejano
               Ok:       El_Álamo_Tejano
               Better:   El_Álamo_Tejano
               Best:     El_Álamo_Tejano
Testing TheÆvarArnfjörðBjarmason
     [WRONG]   Worst:    TheÆvar_ArnfjörðBjarmason
               Ok:       The_Ævar_Arnfjörð_Bjarmason
               Better:   The_Ævar_Arnfjörð_Bjarmason
               Best:     The_Ævar_Arnfjörð_Bjarmason
Testing IlCaffèMacchiato
     [WRONG]   Worst:    Il_CaffèMacchiato
               Ok:       Il_Caffè_Macchiato
               Better:   Il_Caffè_Macchiato
               Best:     Il_Caffè_Macchiato
Testing MisterDženanLjubović
     [WRONG]   Worst:    MisterDženanLjubović
     [WRONG]   Ok:       MisterDženanLjubović
               Better:   Mister_Dženan_Ljubović
               Best:     Mister_Dženan_Ljubović
Testing OleKingHenryⅧ
     [WRONG]   Worst:    Ole_King_HenryⅧ
     [WRONG]   Ok:       Ole_King_HenryⅧ
     [WRONG]   Better:   Ole_King_HenryⅧ
               Best:     Ole_King_Henry_Ⅷ
Testing CarlosⅤºElEmperador
     [WRONG]   Worst:    CarlosⅤºEl_Emperador
     [WRONG]   Ok:       CarlosⅤº_El_Emperador
     [WRONG]   Better:   CarlosⅤº_El_Emperador
               Best:     Carlos_Ⅴº_El_Emperador

Кстати, почти каждый здесь выбрал первый путь, один из которых отмечен как "Худший". Некоторые выбрали второй способ, отмеченный "ОК". Но никто другой передо мной не показал вам, как делать "лучший" или "лучший" подход.

Вот тестовая программа с четырьмя методами:

#!/usr/bin/env perl
use utf8;
use strict;
use warnings;

# First I'll prove these are fine variable names:
my (
    $TheLoneRanger              ,
    $MountMᶜKinleyNationalPark  ,
    $ElÁlamoTejano              ,
    $TheÆvarArnfjörðBjarmason   ,
    $IlCaffèMacchiato           ,
    $MisterDženanLjubović         ,
    $OleKingHenryⅧ              ,
    $CarlosⅤºElEmperador        ,
);

# Now I'll load up some string with those values in them:
my @strings = qw{
    TheLoneRanger
    MountMᶜKinleyNationalPark
    ElÁlamoTejano
    TheÆvarArnfjörðBjarmason
    IlCaffèMacchiato
    MisterDženanLjubović
    OleKingHenryⅧ
    CarlosⅤºElEmperador
};

my($new, $best, $ok);
my $mask = "  %10s   %-8s  %s\n";

for my $old (@strings) {
    print "Testing $old\n";
    ($best = $old) =~ s/(?<=\p{Lowercase})(?=[\p{Uppercase}\p{Lt}])/_/g;

    ($new = $old) =~ s/(?<=[a-z])(?=[A-Z])/_/g;
    $ok = ($new ne $best) && "[WRONG]";
    printf $mask, $ok, "Worst:", $new;

    ($new = $old) =~ s/(?<=\p{Ll})(?=\p{Lu})/_/g;
    $ok = ($new ne $best) && "[WRONG]";
    printf $mask, $ok, "Ok:", $new;

    ($new = $old) =~ s/(?<=\p{Ll})(?=[\p{Lu}\p{Lt}])/_/g;
    $ok = ($new ne $best) && "[WRONG]";
    printf $mask, $ok, "Better:", $new;

    ($new = $old) =~ s/(?<=\p{Lowercase})(?=[\p{Uppercase}\p{Lt}])/_/g;
    $ok = ($new ne $best) && "[WRONG]";
    printf $mask, $ok, "Best:", $new;
}

Когда вы можете набрать то же самое, что и "Лучшее" в этом наборе данных, вы знаете, что вы сделали это правильно. До тех пор, у вас нет. Никто больше здесь не сделал лучше, чем "Хорошо", и большинство из них сделали это "Худшим". Я с нетерпением жду, чтобы кто-то опубликовал правильный код.

Я замечаю, что выделение кода StackOverflows снова ужасно stoopid. Theyre делая все те же самые старые хромые как (большинство, но не все) остальных бедных подходов, упомянутых здесь, сделали. Разве не прошло много времени, чтобы поставить ASCII на отдых? Это уже имеет смысл, и притворяться, что все, что у вас есть, просто неправильно. Это делает неправильный код.

Ответ 6

Я решил сделать простой метод расширения, основанный на двоичном коде Worrier, который будет обрабатывать аббревиатуры правильно и будет повторяемым (не будет калечить уже разнесенные слова). Вот мой результат.

public static string UnPascalCase(this string text)
{
    if (string.IsNullOrWhiteSpace(text))
        return "";
    var newText = new StringBuilder(text.Length * 2);
    newText.Append(text[0]);
    for (int i = 1; i < text.Length; i++)
    {
        var currentUpper = char.IsUpper(text[i]);
        var prevUpper = char.IsUpper(text[i - 1]);
        var nextUpper = (text.Length > i + 1) ? char.IsUpper(text[i + 1]) || char.IsWhiteSpace(text[i + 1]): prevUpper;
        var spaceExists = char.IsWhiteSpace(text[i - 1]);
        if (currentUpper && !spaceExists && (!nextUpper || !prevUpper))
                newText.Append(' ');
        newText.Append(text[i]);
    }
    return newText.ToString();
}

Вот те случаи unit test, которые выполняет эта функция. В этот список я добавил большинство предложенных tchrist случаев. Три из них не проходят (два - только римские цифры):

Assert.AreEqual("For You And I", "ForYouAndI".UnPascalCase());
Assert.AreEqual("For You And The FBI", "ForYouAndTheFBI".UnPascalCase());
Assert.AreEqual("A Man A Plan A Canal Panama", "AManAPlanACanalPanama".UnPascalCase());
Assert.AreEqual("DNS Server", "DNSServer".UnPascalCase());
Assert.AreEqual("For You And I", "For You And I".UnPascalCase());
Assert.AreEqual("Mount Mᶜ Kinley National Park", "MountMᶜKinleyNationalPark".UnPascalCase());
Assert.AreEqual("El Álamo Tejano", "ElÁlamoTejano".UnPascalCase());
Assert.AreEqual("The Ævar Arnfjörð Bjarmason", "TheÆvarArnfjörðBjarmason".UnPascalCase());
Assert.AreEqual("Il Caffè Macchiato", "IlCaffèMacchiato".UnPascalCase());
//Assert.AreEqual("Mister Dženan Ljubović", "MisterDženanLjubović".UnPascalCase());
//Assert.AreEqual("Ole King Henry Ⅷ", "OleKingHenryⅧ".UnPascalCase());
//Assert.AreEqual("Carlos Ⅴº El Emperador", "CarlosⅤºElEmperador".UnPascalCase());
Assert.AreEqual("For You And The FBI", "For You And The FBI".UnPascalCase());
Assert.AreEqual("A Man A Plan A Canal Panama", "A Man A Plan A Canal Panama".UnPascalCase());
Assert.AreEqual("DNS Server", "DNS Server".UnPascalCase());
Assert.AreEqual("Mount Mᶜ Kinley National Park", "Mount Mᶜ Kinley National Park".UnPascalCase());

Ответ 7

Binary Worrier, я использовал ваш предложенный код, и это довольно хорошо, у меня есть только одно небольшое дополнение к нему:

public static string AddSpacesToSentence(string text)
{
    if (string.IsNullOrEmpty(text))
        return "";
    StringBuilder newText = new StringBuilder(text.Length * 2);
    newText.Append(text[0]);
            for (int i = 1; i < result.Length; i++)
            {
                if (char.IsUpper(result[i]) && !char.IsUpper(result[i - 1]))
                {
                    newText.Append(' ');
                }
                else if (i < result.Length)
                {
                    if (char.IsUpper(result[i]) && !char.IsUpper(result[i + 1]))
                        newText.Append(' ');

                }
                newText.Append(result[i]);
            }
    return newText.ToString();
}

Я добавил условие !char.IsUpper(text[i - 1]). Это исправило ошибку, которая привела бы к тому, что "AverageNOX" превратится в "Average N O X", что, очевидно, неверно, так как оно должно читать "Average NOX".

К сожалению, это все еще имеет ошибку, что если у вас есть текст FromAStart, вы получите "От AStart".

Любые мысли об исправлении этого?

Ответ 8

Здесь моя:

private string SplitCamelCase(string s) 
{ 
    Regex upperCaseRegex = new Regex(@"[A-Z]{1}[a-z]*"); 
    MatchCollection matches = upperCaseRegex.Matches(s); 
    List<string> words = new List<string>(); 
    foreach (Match match in matches) 
    { 
        words.Add(match.Value); 
    } 
    return String.Join(" ", words.ToArray()); 
}

Ответ 9

Убедитесь, что не помещают пробелы в начале строки, но вы помещаете их между последовательными столицами. Некоторые из ответов здесь не касаются одного или обоих этих пунктов. Существуют и другие способы, чем регулярное выражение, но если вы предпочитаете использовать это, попробуйте следующее:

Regex.Replace(value, @"\B[A-Z]", " $0")

\B является отрицательным \B, поэтому он представляет не-слово-границу. Это означает, что шаблон соответствует "Y" в XYzabc, но не в Yzabc или X Yzabc. В качестве небольшого бонуса вы можете использовать это в строке с пробелами в ней, и она не будет удваивать их.

Ответ 10

То, что у вас есть, отлично работает. Просто не забудьте переназначить value на возвращаемое значение этой функции.

value = System.Text.RegularExpressions.Regex.Replace(value, "[A-Z]", " $0");

Ответ 11

Вот как вы могли бы это сделать в SQL

create  FUNCTION dbo.PascalCaseWithSpace(@pInput AS VARCHAR(MAX)) RETURNS VARCHAR(MAX)
BEGIN
    declare @output varchar(8000)

set @output = ''


Declare @vInputLength        INT
Declare @vIndex              INT
Declare @vCount              INT
Declare @PrevLetter varchar(50)
SET @PrevLetter = ''

SET @vCount = 0
SET @vIndex = 1
SET @vInputLength = LEN(@pInput)

WHILE @vIndex <= @vInputLength
BEGIN
    IF ASCII(SUBSTRING(@pInput, @vIndex, 1)) = ASCII(Upper(SUBSTRING(@pInput, @vIndex, 1)))
       begin 

        if(@PrevLetter != '' and ASCII(@PrevLetter) = ASCII(Lower(@PrevLetter)))
            SET @output = @output + ' ' + SUBSTRING(@pInput, @vIndex, 1)
            else
            SET @output = @output +  SUBSTRING(@pInput, @vIndex, 1) 

        end
    else
        begin
        SET @output = @output +  SUBSTRING(@pInput, @vIndex, 1) 

        end

set @PrevLetter = SUBSTRING(@pInput, @vIndex, 1) 

    SET @vIndex = @vIndex + 1
END


return @output
END

Ответ 12

Это регулярное выражение помещает пробел перед каждой заглавной буквой:

using System.Text.RegularExpressions;

const string myStringWithoutSpaces = "ThisIsAStringWithoutSpaces";
var myStringWithSpaces = Regex.Replace(myStringWithoutSpaces, "([A-Z])([a-z]*)", " $1$2");

Запомните пространство спереди, если "$ 1 $2", вот что получится.

Это результат:

"This Is A String Without Spaces"

Ответ 13

Вдохновленный от @MartinBrown, Две строки простого регулярного выражения, которые разрешат ваше имя, включая Acyronyms в любой строке.

public string ResolveName(string name)
{
   var tmpDisplay = Regex.Replace(name, "([^A-Z ])([A-Z])", "$1 $2");
   return Regex.Replace(tmpDisplay, "([A-Z]+)([A-Z][^A-Z$])", "$1 $2").Trim();
}

Ответ 14

replaceAll("(?<=[^^\\p{Uppercase}])(?=[\\p{Uppercase}])"," ");

Ответ 15

static string AddSpacesToColumnName(string columnCaption)
    {
        if (string.IsNullOrWhiteSpace(columnCaption))
            return "";
        StringBuilder newCaption = new StringBuilder(columnCaption.Length * 2);
        newCaption.Append(columnCaption[0]);
        int pos = 1;
        for (pos = 1; pos < columnCaption.Length-1; pos++)
        {               
            if (char.IsUpper(columnCaption[pos]) && !(char.IsUpper(columnCaption[pos - 1]) && char.IsUpper(columnCaption[pos + 1])))
                newCaption.Append(' ');
            newCaption.Append(columnCaption[pos]);
        }
        newCaption.Append(columnCaption[pos]);
        return newCaption.ToString();
    }

Ответ 16

В Ruby через Regexp:

"FooBarBaz".gsub(/(?!^)(?=[A-Z])/, ' ') # => "Foo Bar Baz"

Ответ 17

Я выбрал Kevin Strikers отличное решение и преобразован в VB. Поскольку я заблокирован в .NET 3.5, мне также пришлось писать IsNullOrWhiteSpace. Это проходит все его тесты.

<Extension()>
Public Function IsNullOrWhiteSpace(value As String) As Boolean
    If value Is Nothing Then
        Return True
    End If
    For i As Integer = 0 To value.Length - 1
        If Not Char.IsWhiteSpace(value(i)) Then
            Return False
        End If
    Next
    Return True
End Function

<Extension()>
Public Function UnPascalCase(text As String) As String
    If text.IsNullOrWhiteSpace Then
        Return String.Empty
    End If

    Dim newText = New StringBuilder()
    newText.Append(text(0))
    For i As Integer = 1 To text.Length - 1
        Dim currentUpper = Char.IsUpper(text(i))
        Dim prevUpper = Char.IsUpper(text(i - 1))
        Dim nextUpper = If(text.Length > i + 1, Char.IsUpper(text(i + 1)) Or Char.IsWhiteSpace(text(i + 1)), prevUpper)
        Dim spaceExists = Char.IsWhiteSpace(text(i - 1))
        If (currentUpper And Not spaceExists And (Not nextUpper Or Not prevUpper)) Then
            newText.Append(" ")
        End If
        newText.Append(text(i))
    Next
    Return newText.ToString()
End Function

Ответ 18

Кажется хорошей возможностью для Aggregate. Это предназначено для чтения, но не обязательно особенно быстро.

someString
.Aggregate(
   new StringBuilder(),
   (str, ch) => {
      if (char.IsUpper(ch) && str.Length > 0)
         str.Append(" ");
      str.Append(ch);
      return str;
   }
).ToString();

Ответ 19

В дополнение к Martin Brown Answer, у меня тоже была проблема с цифрами. Например: "Location2" или "Jan22" должно быть "Location 2" и "Jan 22" соответственно.

Вот мое регулярное выражение для этого, используя ответ Мартина Брауна:

"((?<=\p{Ll})\p{Lu})|((?!\A)\p{Lu}(?>\p{Ll}))|((?<=[\p{Ll}\p{Lu}])\p{Nd})|((?<=\p{Nd})\p{Lu})"

Вот несколько отличных сайтов для выяснения того, что означает каждая часть:

Анализатор регулярных выражений на основе Java (но работает для большинства .net regex's)

Действие Script Базовый анализатор

Вышеупомянутое регулярное выражение не будет работать на сайте action Script, если вы не замените все \p{Ll} на [a-z], \p{Lu} на [a-z] и \p{Nd} на [0-9].

Ответ 20

Здесь мое решение, основанное на предложениях Binary Worriers и построении в комментариях Ричарда Приддиса, но также принимая во внимание, что в предоставленной строке может существовать пробел, поэтому он не будет добавлять пробел рядом с существующим пробелом.

public string AddSpacesBeforeUpperCase(string nonSpacedString)
    {
        if (string.IsNullOrEmpty(nonSpacedString))
            return string.Empty;

        StringBuilder newText = new StringBuilder(nonSpacedString.Length * 2);
        newText.Append(nonSpacedString[0]);

        for (int i = 1; i < nonSpacedString.Length; i++)
        {
            char currentChar = nonSpacedString[i];

            // If it is whitespace, we do not need to add another next to it
            if(char.IsWhiteSpace(currentChar))
            {
                continue;
            }

            char previousChar = nonSpacedString[i - 1];
            char nextChar = i < nonSpacedString.Length - 1 ? nonSpacedString[i + 1] : nonSpacedString[i];

            if (char.IsUpper(currentChar) && !char.IsWhiteSpace(nextChar) 
                && !(char.IsUpper(previousChar) && char.IsUpper(nextChar)))
            {
                newText.Append(' ');
            }
            else if (i < nonSpacedString.Length)
            {
                if (char.IsUpper(currentChar) && !char.IsWhiteSpace(nextChar) && !char.IsUpper(nextChar))
                {
                    newText.Append(' ');
                }
            }

            newText.Append(currentChar);
        }

        return newText.ToString();
    }

Ответ 21

Для тех, кто ищет функцию С++, отвечающую на этот же вопрос, вы можете использовать следующее. Это моделируется после ответа @Binary Worrier. Этот метод просто сохраняет автоматические сокращения.

using namespace std;

void AddSpacesToSentence(string& testString)
        stringstream ss;
        ss << testString.at(0);
        for (auto it = testString.begin() + 1; it != testString.end(); ++it )
        {
            int index = it - testString.begin();
            char c = (*it);
            if (isupper(c))
            {
                char prev = testString.at(index - 1);
                if (isupper(prev))
                {
                    if (index < testString.length() - 1)
                    {
                        char next = testString.at(index + 1);
                        if (!isupper(next) && next != ' ')
                        {
                            ss << ' ';
                        }
                    }
                }
                else if (islower(prev)) 
                {
                   ss << ' ';
                }
            }

            ss << c;
        }

        cout << ss.str() << endl;

Строки тестов, которые я использовал для этой функции, и результаты:

  • "helloWorld" → "hello World"
  • "HelloWorld" → "Hello World"
  • "HelloABCWorld" → "Hello ABC World"
  • "HelloWorldABC" → "Hello World ABC"
  • "ABCHelloWorld" → "ABC Hello World"
  • "ABC HELLO WORLD" → "ABC HELLO WORLD"
  • "ABCHELLOWORLD" → "ABCHELLOWORLD"
  • "A" → "A"

Ответ 22

A С# решение для входной строки, состоящей только из символов ASCII. регулярное выражение включает отрицательный lookbehind, чтобы игнорировать письмо с капиталом (верхний регистр), которое появляется в начале строки. Использует Regex.Replace() для возврата нужной строки.

Также см. regex101.com demo.

using System;
using System.Text.RegularExpressions;

public class RegexExample
{
    public static void Main()
    {
        var text = "ThisStringHasNoSpacesButItDoesHaveCapitals";

        // Use negative lookbehind to match all capital letters
        // that do not appear at the beginning of the string.
        var pattern = "(?<!^)([A-Z])";

        var rgx = new Regex(pattern);
        var result = rgx.Replace(text, " $1");
        Console.WriteLine("Input: [{0}]\nOutput: [{1}]", text, result);
    }
}

Ожидаемый результат:

Input: [ThisStringHasNoSpacesButItDoesHaveCapitals]
Output: [This String Has No Spaces But It Does Have Capitals]

Обновление: Здесь вариант, который также будет обрабатывать сокращенные (последовательности букв верхнего регистра).

Также см. regex101.com demo и ideone.com demo.

using System;
using System.Text.RegularExpressions;

public class RegexExample
{
    public static void Main()
    {
        var text = "ThisStringHasNoSpacesASCIIButItDoesHaveCapitalsLINQ";

        // Use positive lookbehind to locate all upper-case letters
        // that are preceded by a lower-case letter.
        var patternPart1 = "(?<=[a-z])([A-Z])";

        // Used positive lookbehind and lookahead to locate all
        // upper-case letters that are preceded by an upper-case
        // letter and followed by a lower-case letter.
        var patternPart2 = "(?<=[A-Z])([A-Z])(?=[a-z])";

        var pattern = patternPart1 + "|" + patternPart2;
        var rgx = new Regex(pattern);
        var result = rgx.Replace(text, " $1$2");

        Console.WriteLine("Input: [{0}]\nOutput: [{1}]", text, result);
    }
}

Ожидаемый результат:

Input: [ThisStringHasNoSpacesASCIIButItDoesHaveCapitalsLINQ]
Output: [This String Has No Spaces ASCII But It Does Have Capitals LINQ]

Ответ 23

Вопрос немного устарел, но в настоящее время в Nuget есть хорошая библиотека, которая делает именно это, а также многие другие преобразования для читаемого человеком текста.

Отъезд Humanizer на GitHub или Nuget.

Пример

"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"
"Underscored_input_String_is_turned_INTO_sentence".Humanize() => "Underscored input String is turned INTO sentence"

// acronyms are left intact
"HTML".Humanize() => "HTML"

Ответ 24

Этот включает в себя сокращения и множественное число аббревиатур и немного быстрее, чем принятый ответ:

public string Sentencify(string value)
{
    if (string.IsNullOrWhiteSpace(value))
        return string.Empty;

    string final = string.Empty;
    for (int i = 0; i < value.Length; i++)
    {
        if (i != 0 && Char.IsUpper(value[i]))
        {
            if (!Char.IsUpper(value[i - 1]))
                final += " ";
            else if (i < (value.Length - 1))
            {
                if (!Char.IsUpper(value[i + 1]) && !((value.Length >= i && value[i + 1] == 's') ||
                                                     (value.Length >= i + 1 && value[i + 1] == 'e' && value[i + 2] == 's')))
                    final += " ";
            }
        }

        final += value[i];
    }

    return final;
}

Проходит эти тесты:

string test1 = "RegularOTs";
string test2 = "ThisStringHasNoSpacesASCIIButItDoesHaveCapitalsLINQ";
string test3 = "ThisStringHasNoSpacesButItDoesHaveCapitals";

Ответ 25

Вот более тщательное решение, которое не помещает пробелы перед словами:

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

Dim s As String = "ThisStringHasNoSpacesButItDoesHaveCapitals"
s = System.Text.RegularExpressions.Regex.Replace(s, "([a-z])([A-Z](?=[A-Z])[a-z]*)", "$1 $2")
s = System.Text.RegularExpressions.Regex.Replace(s, "([A-Z])([A-Z][a-z])", "$1 $2")
s = System.Text.RegularExpressions.Regex.Replace(s, "([a-z])([A-Z][a-z])", "$1 $2")
s = System.Text.RegularExpressions.Regex.Replace(s, "([a-z])([A-Z][a-z])", "$1 $2") // repeat a second time

В:

"ThisStringHasNoSpacesButItDoesHaveCapitals"
"IAmNotAGoat"
"LOLThatsHilarious!"
"ThisIsASMSMessage"

Выход

"This String Has No Spaces But It Does Have Capitals"
"I Am Not A Goat"
"LOL Thats Hilarious!"
"This Is ASMS Message" // (Difficult to handle single letter words when they are next to acronyms.)

Ответ 26

Все предыдущие ответы были слишком сложными.

У меня была строка, которая имела смесь столиц и поэтому использовалась, string.Replace(), чтобы сделать _, "" и использовала следующее, чтобы добавить пробел заглавными буквами.

for (int i = 0; i < result.Length; i++)
{
    if (char.IsUpper(result[i]))
    {
        counter++;
        if (i > 1) //stops from adding a space at if string starts with Capital
        {
            result = result.Insert(i, " ");
            i++; //Required** otherwise stuck in infinite 
                 //add space loop over a single capital letter.
        }
    }
}

Ответ 27

Вдохновленный ответом Binary Worrier, я понял это.

Здесь результат:

/// <summary>
/// String Extension Method
/// Adds white space to strings based on Upper Case Letters
/// </summary>
/// <example>
/// strIn => "HateJPMorgan"
/// preserveAcronyms false => "Hate JP Morgan"
/// preserveAcronyms true => "Hate JPMorgan"
/// </example>
/// <param name="strIn">to evaluate</param>
/// <param name="preserveAcronyms" >determines saving acronyms (Optional => false) </param>
public static string AddSpaces(this string strIn, bool preserveAcronyms = false)
{
    if (string.IsNullOrWhiteSpace(strIn))
        return String.Empty;

    var stringBuilder = new StringBuilder(strIn.Length * 2)
        .Append(strIn[0]);

    int i;

    for (i = 1; i < strIn.Length - 1; i++)
    {
        var c = strIn[i];

        if (Char.IsUpper(c) && (Char.IsLower(strIn[i - 1]) || (preserveAcronyms && Char.IsLower(strIn[i + 1]))))
            stringBuilder.Append(' ');

        stringBuilder.Append(c);
    }

    return stringBuilder.Append(strIn[i]).ToString();
}

Была ли проведена проверка с использованием секундомера, выполняющего итерации 10000000, а также различные длины и комбинации строк.

В среднем 50% (может быть, немного больше) быстрее, чем ответ двоичного Worrier.

Ответ 28

    private string GetProperName(string Header)
    {
        if (Header.ToCharArray().Where(c => Char.IsUpper(c)).Count() == 1)
        {
            return Header;
        }
        else
        {
            string ReturnHeader = Header[0].ToString();
            for(int i=1; i<Header.Length;i++)
            {
                if (char.IsLower(Header[i-1]) && char.IsUpper(Header[i]))
                {
                    ReturnHeader += " " + Header[i].ToString();
                }
                else
                {
                    ReturnHeader += Header[i].ToString();
                }
            }

            return ReturnHeader;
        }

        return Header;
    }

Ответ 29

Реализация с помощью fold, также известная как Aggregate:

    public static string SpaceCapitals(this string arg) =>
       new string(arg.Aggregate(new List<Char>(),
                      (accum, x) => 
                      {
                          if (Char.IsUpper(x) &&
                              accum.Any() &&
                              // prevent double spacing
                              accum.Last() != ' ' &&
                              // prevent spacing acronyms (ASCII, SCSI)
                              !Char.IsUpper(accum.Last()))
                          {
                              accum.Add(' ');
                          }

                          accum.Add(x);

                          return accum;
                      }).ToArray());

В дополнение к запросу, эта реализация правильно сохраняет начальные, внутренние, конечные пробелы и сокращения, например,

" SpacedWord " => " Spaced Word ",  

"Inner Space" => "Inner Space",  

"SomeACRONYM" => "Some ACRONYM".

Ответ 30

    string AddSpacesToSentence(string value, bool spaceLowerChar = true, bool spaceDigitChar = true, bool spaceSymbolChar = false)
    {
        var result = "";

        for (int i = 0; i < value.Length; i++)
        {
            char currentChar = value[i];
            char nextChar = value[i < value.Length - 1 ? i + 1 : value.Length - 1];

            if (spaceLowerChar && char.IsLower(currentChar) && !char.IsLower(nextChar))
            {
                result += value[i] + " ";
            }
            else if (spaceDigitChar && char.IsDigit(currentChar) && !char.IsDigit(nextChar))
            {
                result += value[i] + " ";
            }
            else if(spaceSymbolChar && char.IsSymbol(currentChar) && !char.IsSymbol(nextChar))
            {
                result += value[i];
            }
            else
            {
                result += value[i];
            }
        }

        return result;
    }