대문자 앞에 공백을 추가하십시오


194

"ThisStringHasNoSpacesButItDoesHaveCapitals"라는 문자열이 주어지면 대문자 앞에 공백을 추가하는 가장 좋은 방법은 무엇입니까? 끝 문자열은 "이 문자열에는 공백이 없지만 대문자가 있습니다"입니다.

다음은 RegEx를 사용한 시도입니다.

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

2
당신이 취한 접근 방식에 대해 특별한 불만이 있습니까? 귀하의 분석법을 개선하는 데 도움이 될 수 있습니다.
블레어 콘래드

정규식이 작동하면 그걸 고수 할 것입니다. 정규식은 문자열 조작을 위해 최적화되었습니다.
Michael Meadows

더 나은 또는 아마도 내장 된 접근 방식이 있는지 궁금합니다. 다른 언어와의 다른 접근 방식이 궁금합니다.
Bob

2
수정 된 문자열이 '바꾸기'함수의 반환 값이므로 코드가 작동하지 않았습니다. 이 코드 행으로 'System.Text.RegularExpressions.Regex.Replace (value, "[AZ]", "$ 0"). Trim ();' 완벽하게 작동합니다. (이 게시물을 우연히 만났고 아무도 실제로 보지 못했기 때문에 주석을
달았습니다

Regex.Replace ( "ThisStringHasNoSpacesButItDoesHaveCapitals", @ "\ B [AZ]", m => ""+ m);
saquib adil

답변:


203

정규 표현식이 제대로 작동하지만 (Martin Browns 답변에 투표하기까지 했음) 비용이 많이 듭니다.

이 기능

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

2,968,750 틱으로 100,000 번, 정규식은 25,000,000 틱 (및 정규식이 컴파일 된 틱)이 걸립니다.

주어진 값이 더 좋을수록 (즉 더 빠름) 더 낫지 만 유지해야 할 코드는 더 많습니다. "더 나은"은 종종 경쟁 요구 사항을 타협합니다.

도움이 되었기를 바랍니다 :)

업데이트
이것을 살펴본 후 오랜 시간이 걸렸으며 코드가 변경된 후 타이밍이 업데이트되지 않았다는 것을 깨달았습니다.

'Abbbbbbbbb'이 100 번 반복 된 문자열 (즉, 1,000 바이트)이있는 문자열에서 100,000 회의 변환은 수작업으로 코딩 된 함수 4,517,177 틱을 취하고 아래의 정규식은 59,435,719를 수행하여 Hand coded 함수를 수행하는 시간의 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();
}

8
if (char.IsUpper (text [i]) && text [i-1]! = '') 위의 코드를 다시 실행하면 공백을 계속 추가하고 대문자 앞에 공백이 있으면 공백이 추가되지 않습니다. 편지.
Paul Talbot 8:26에

확실하지 않다고 확신합니다.이 방법은 Martin Brown의 답변 "DriveIsSCSICompatible"에 설명 된대로 약어를 처리합니까? 이상적으로 "Drive Is SCSI 호환"이 될 것입니다.
Coops

그것은 당신의 for 문의 내용을 새로 업데이트 된 if 문으로 바꾸어 1 문자로 만들었습니다. 제가 잘못하고 있습니까?
Coops

1
char.IsLetter (text [i + 1])에 대한 검사를 추가하면 특수 문자와 숫자가있는 약어 (예 : ABC_DEF가 AB C_DEF로 분할되지 않음)에 도움이됩니다.
HeXanon

1
두문자어가 꺼져있을 때 두문자어 부분이 올바른지 확실하지 않습니다. 방금 "ASentenceABC"가 "ASentence AB C"로 확장 된 테스트를 실행했습니다. "문장 AB C"여야 함
Tim Rutter

150

솔루션에 첫 번째 문자 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"은 "Drive Is SCSI Compatible"이됩니다.


3
원래 RegEx 및 Trim () 결과를 그대로 유지할 수 없습니까?
PandaWood

3
@PandaWood 당신은 할 수 있지만 다른 메모리 할당과 문자열 복사가 필요합니다. 성능이 걱정된다면 정규식은 어쨌든 가장 좋은 방법은 아닙니다.
Martin Brown

"([^A-Z\\s])([A-Z])"두문자어 도 사용할 수 있습니까?
Ruben9922

82

성능을 테스트하지는 않았지만 linq와 함께 한 줄로 표시합니다.

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

18

나는 이것이 오래된 것임을 알고 있지만, 이것을해야 할 때 사용하는 확장입니다.

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


나는 이것을 확장 방법으로 생각합니다. 추가 TrimStart(' ')하면 선행 공간이 제거됩니다.
user1069816 2016 년

1
감사합니다 @ user1069816. SelectMany색인을 포함하는 과부하를 사용하도록 확장을 변경했습니다 .이 방법으로 첫 번째 문자와에 대한 추가 호출의 불필요한 잠재적 인 오버 헤드를 피할 수 TrimStart(' ')있습니다. 롭
Rob Hardy

9

이진 걱정의 코드를 기반으로 간단한 확장 방법을 사용하여 약어를 올바르게 처리하고 반복 할 수 있습니다 (이미 단어 간격이 엉망이 아닙니다). 여기 내 결과가 있습니다.

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

이 함수가 통과하는 단위 테스트 사례는 다음과 같습니다. 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());

여기에 게시 된 다른 솔루션과 마찬가지로 문자열 "RegularOTs"와 함께 실패합니다. 그것은 "일반 O Ts"를 반환합니다
Patee Gutee

8

유니 코드에 오신 것을 환영합니다

이 모든 솔루션은 현대 텍스트에는 본질적으로 잘못되었습니다. 대소 문자를 이해하는 것을 사용해야합니다. Bob이 다른 언어를 요청했기 때문에 Perl에게 몇 가지를 드리겠습니다.

최악에서 최고에 이르는 네 가지 솔루션을 제공합니다. 가장 좋은 것만 항상 옳습니다. 다른 사람들에게는 문제가 있습니다. 다음은 작동하는 것과 작동하지 않는 위치 및 위치를 보여주는 테스트 실행입니다. 공백을 넣은 위치를 볼 수 있도록 밑줄을 사용했으며, 잘못된 것으로 잘못 표시했습니다.

Testing TheLoneRanger
               Worst:    The_Lone_Ranger
               Ok:       The_Lone_Ranger
               Better:   The_Lone_Ranger
               Best:     The_Lone_Ranger
Testing MountMKinleyNationalPark
     [WRONG]   Worst:    Mount_MKinley_National_Park
     [WRONG]   Ok:       Mount_MKinley_National_Park
     [WRONG]   Better:   Mount_MKinley_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

BTW, 여기에있는 거의 모든 사람들이 "Worst"라고 표시된 첫 번째 방법을 선택했습니다. "OK"라고 표시된 두 번째 방법을 선택한 사람이 있습니다. 그러나 저보다 먼저 "더 나은"또는 "최상의"접근 방식을 수행하는 방법을 보여준 사람은 없습니다.

다음은 네 가지 방법으로 테스트 프로그램입니다.

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

# First I'll prove these are fine variable names:
my (
    $TheLoneRanger              ,
    $MountMKinleyNationalPark  ,
    $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
    MountMKinleyNationalPark
    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;
}

이 데이터 세트에서 "최고"와 동일한 점수를 매길 수 있으면 올바르게 수행 한 것입니다. 그때까지는 그렇지 않았습니다. 여기서 다른 어느 누구도 "Ok"보다 나은 작업을 수행하지 않았으며 대부분 "Worst"를 수행했습니다. 누군가 올바른 ℂ♯ 코드를 게시하기를 기대합니다.

StackOverflow의 강조 표시 코드가 다시 비참하게 다루어졌습니다. 그들은 여기에 언급 된 나머지 가난한 접근 방식과 거의 같은 오래된 절름발이를 만들고 있습니다. ASCII를 쉬게하는 데 시간이 오래 걸리지 않습니까? 더 이상 말이되지 않으며, 당신이 가진 전부인 척하는 것은 단순히 잘못입니다. 코드가 잘못되었습니다.


'최상의'답변은 지금까지 가장 가까운 것으로 보이지만 구두점이나 기타 소문자가 아닌 문자를 설명하는 것처럼 보이지 않습니다. 이것은 Java에서 가장 잘 작동하는 것 같습니다. replaceAll ( "(? <= [^^ \\ p {javaUpperCase}]) (? = [\\ p {javaUpperCase}])", "");
Randyaa

흠. 이 예제에서 로마 숫자가 실제로 대문자로 계산되어야하는지 잘 모르겠습니다. 문자 수정 자 예제는 계산되지 않아야합니다. McDonalds.com을 방문하면 공백없이 작성되었음을 알 수 있습니다.
Martin Brown

또한 이것을 완벽하게 만들 수는 없습니다. 예를 들어 "AlexandervonHumboldt"를 정렬하는 예제를보고 싶습니다. "Alexander von Humboldt"로 끝나야합니다. 물론 대문자와 소문자를 지정하지 않은 언어가 있습니다.
Martin Brown

4

이진 걱정, 나는 당신의 제안 된 코드를 사용했으며, 약간 좋습니다. 단지 하나의 추가 사항이 있습니다.

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 NOX'로 바뀌는 버그가 수정되었습니다. 'Average NOX'를 읽어야하므로 분명히 잘못되었습니다.

슬프게도 여전히 'FromAStart'라는 텍스트가 있으면 'From AStart'가 표시되는 버그가 있습니다.

이것을 고치는 것에 대한 생각이 있습니까?


아마도 다음과 같이 작동 할 것입니다 : char.IsUpper (text [i]) && (char.IsLower (text [i-1]) || (char.IsLower (text [i + 1]))
Martin Brown

1
if (char.IsUpper(text[i]) && !(char.IsUpper(text[i - 1]) && char.IsUpper(text[i + 1])))테스트 결과 : "시작에서", "시작에서", "시작에서" 올바른 결과입니다. 그러나 i < text.Length - 1마지막 문자를 무시하고 범위를 벗어난 예외를 방지하려면 for 루프 조건 이 필요합니다 .
CallMeLaNN

오 똑같아 lower =! upper이므로! (a && b) 및 (! a ||! b)
CallMeLaNN

3

내 꺼야 :

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

이것이 C #이어야합니까? 그렇다면 List에 어떤 네임 스페이스가 있습니까? ArrayList 또는 List <string>을 의미합니까?
Martin Brown

리스트 <string>은 괜찮을 것입니다. 미안합니다.
Cory Foy 2011

@Martin 그는 항상 올바른 구문을 사용했으며 <pre><code>code</code></pre>Markdown 구문 대신 블록에 숨겨져있었습니다 . 그를 하향 투표 할 필요가 없습니다 (귀하의 경우).
George Stocker

3

확인은 확인이 되지 않은 문자열의 시작 부분에 공백을 넣어,하지만 당신은 하는 연속 자본 사이에 그들을 가하고. 여기에있는 답변 중 일부는 그러한 요점 중 하나 또는 둘 다를 다루지 않습니다. 정규 표현식 이외의 다른 방법이 있지만, 그것을 사용하려면 다음을 시도하십시오.

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

\B부정 된이다 \b는 비 단어 경계를 나타냅니다 있도록. 패턴이에서 "Y"와 일치 XYzabc하지만 in Yzabc또는 와는 일치 하지 않음을 의미합니다 X Yzabc. 약간의 보너스로 공백이있는 문자열에 이것을 사용할 수 있으며 두 배로 늘리지 않습니다.


3

이 정규식은 모든 대문자 앞에 공백 문자를 배치합니다.

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"

1
숫자도 분리하려면 다음 정규식 패턴을 대신 사용하십시오."([A-Z0-9])([a-z]*)"
Matthias Thomann

2

당신이 가진 것은 완벽하게 작동합니다. value이 함수의 반환 값에 다시 할당 해야합니다.

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

2

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

2

@MartinBrown에서 영감을 얻은 Two Lines of Simple Regex는 문자열의 아무 곳이나 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();
}

나는이 해결책을 좋아한다. 짧고 빠릅니다. 그러나 다른 솔루션과 마찬가지로 문자열 "RegularOTs"와 함께 실패합니다. 여기에서 시도한 모든 솔루션은 "Regular O Ts"를 반환합니다
Patee Gutee

그는 우리가 생산 대구에서 그에 대한 수정이, 약어를 언급하지 않았다, 영업 이익은의 capitols 전에 공간을 원 @PateeGutee
오 조니

수정 사항을 보여줄 수 있습니까? 내 데이터에 이와 같은 문자열이 있으며 잘못된 결과를 제공합니다. 감사.
Patee Gutee

@PateeGutee 죄송합니다, 당신이 원하는 것을 잘못 읽었습니다. 복수형 화는 다른 문제들입니다.`RegularOTs ' "Regular OTs"또는 "Regular OT s"가 발생할 것으로 예상되는 것
johnny 5

1
@PateeGutee 나는 당신을 위해 내 대답을 업데이트했습니다, 나는 그것이 작동해야한다고 생각합니다
johnny 5


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

1

Ruby에서 Regexp를 통해 :

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

1
앗 미안 해요. 나는 그것이 C # 관련 질문이라는 것을 놓치고 여기에 루비 답변을 게시했습니다 :(
Artem

1

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

1

질문은 조금 오래되었지만 요즘 Nuget에는 사람이 읽을 수있는 텍스트로 많은 다른 변환뿐만 아니라 정확하게 수행하는 멋진 라이브러리가 있습니다.

GitHub 또는 Nuget 에서 Humanizer 를 확인하십시오 .

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

그냥 시도하고 첫 번째 링크가 끊어졌습니다. NuGet은 작동하지만 패키지가 내 솔루션에서 컴파일되지 않습니다. 좋은 아이디어입니다.
philw

1

에 대한 좋은 기회 인 것 같습니다 Aggregate. 이것은 반드시 빠르지는 않지만 읽을 수 있도록 설계되었습니다.

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

0

Martin Brown의 답변 외에도 숫자에 문제가있었습니다. 예를 들어 "Location2"또는 "Jan22"는 각각 "Location 2"및 "Jan 22"여야합니다.

Martin Brown의 답변을 사용하여이를 수행하는 정규 표현식은 다음과 같습니다.

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

다음은 각 부분의 의미를 파악하기위한 훌륭한 사이트입니다.

Java 기반 정규 표현식 분석기 (대부분의 .net 정규식에서 작동)

액션 스크립트 기반 분석기

위의 정규 표현식은 \p{Ll}with [a-z], \p{Lu}with with [A-Z]\p{Nd}with를 모두 바꾸지 않으면 액션 스크립트 사이트에서 작동하지 않습니다 [0-9].


0

이진 걱정 제안 및 Richard Priddys의 의견을 바탕으로 한 내 솔루션은 다음과 같습니다. 또한 제공된 문자열에 공백이있을 수 있으므로 기존 공백 옆에 ​​공백이 추가되지 않습니다.

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

0

이 같은 질문에 대답하는 C ++ 함수를 찾는 사람은 다음을 사용할 수 있습니다. 이것은 @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"

0

C #을 ASCII 문자로 구성되어 입력 문자열에 대한 솔루션을 제공합니다. 정규식 통합 부정적인 lookbehind 문자열의 시작 부분에 나타납니다 자본 (대문자) 편지를 무시합니다. Regex.Replace () 를 사용 하여 원하는 문자열을 반환합니다.

regex101.com 데모 도 참조하십시오 .

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 데모ideone.com 데모 도 참조하십시오 .

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]

0

단어 앞에 공백을 넣지 않는보다 철저한 솔루션이 있습니다.

참고 : 여러 정규 표현식을 사용했습니다 (간결하지는 않지만 약어 및 단일 문자를 처리합니다)

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.)

"이 문자열에는 공백이 있지만 대문자가 있습니다"
Andy Robinson

안녕 @ 앤디 로빈슨, 감사합니다. 여러 정규식 대체를 사용하도록 변경했습니다. 더 간결한 방법이 있는지 확실하지 않지만 지금 작동합니다.
CrazyTim

0

이전의 모든 응답은 너무 복잡해 보였다.

나는 대문자와 _이 혼합 된 문자열을 가지고 있었으므로 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.
        }
    }
}

0

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 반복 및 다양한 문자열 길이 및 조합을 실행하는 스톱워치를 사용하여 테스트했습니다.

Binary Worrier의 답변보다 평균 50 % 빠릅니다.


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

0

여기에는 두문자어와 복수형이 포함되며 허용되는 답변보다 약간 빠릅니다.

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

허용 된 답변은 값이 null 인 경우를 처리합니다.
Chris F Carroll

출력 앞에 여분의 공간을 추가합니다 (예 : HireDate => "Hire Date"). final.TrimStart 또는 무언가가 필요합니다. 나는 그것이 다른 답변 중 하나가 아래에 지적하고 있다고 생각하지만 재정렬 때문에 그의 답변이 RegEx 기반이기 때문에 그가 당신에게 말하고 있는지 확실하지 않습니다.
b_levitt

좋은 캐치 ... 테스트에 시작 및 종료 마커를 추가해야합니다. 이제 수정되었습니다.
Serj Sagan

여기에 게시 된 다른 솔루션과 마찬가지로 문자열 "RegularOTs"와 함께 실패합니다. 그것은 "일반 O Ts"를 반환합니다
Patee Gutee

약어 복수형을 가져 주셔서 감사합니다.이 작업을 수행하도록 업데이트되었습니다.
Serj Sagan

0

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

0

소문자, 대문자 또는 숫자 뒤에 공백을 추가하는 간단한 방법입니다.

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

1
코드 전용 답변은 권장하지 않습니다. 편집을 클릭하고 코드에서 질문을 처리하는 방법을 요약 한 단어를 추가하거나 이전 답변 / 응답과 어떻게 다른지 설명하십시오. 검토에서
Nick
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.