Büyük Harflerden önce boşluk ekleyin


194

"ThisStringHasNoSpacesButItDoesHaveCapitals" dizesi verildiğinde, büyük harflerden önce boşluk eklemenin en iyi yolu nedir? Yani son dize "Bu Dize Boşluk İçermiyor Ama Büyük Harfleri Var"

İşte bir RegEx ile denemem

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

2
Aldığınız yaklaşım hakkında özel bir şikayetiniz var mı? Bu, yönteminizi geliştirmemize yardımcı olabilir.
Blair Conrad

Normal ifade işe yararsa, o zaman buna bağlı kalırdım. Normal ifade, dize manipülasyonu için optamizedir.
Michael Meadows

Sadece daha iyi ya da belki de yerleşik bir yaklaşım olduğunu merak ediyorum. Hatta diğer dillerle başka yaklaşımlar da görmek isterdim.
Bob

2
Değiştirilen dize 'Değiştir' işlevinin döndürdüğü değer olduğundan kodunuz işe yaramadı. Bu kod satırıyla: 'System.Text.RegularExpressions.Regex.Replace (değer, "[AZ]", "$ 0"). Trim ();' mükemmel çalışır. (Ben sadece bu yazı üzerine tökezledi çünkü yorum ve kimse gerçekten, kodunda neyin yanlış olduğunu görmedim.)
Mattu475 22:15

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

Yanıtlar:


203

Normal ifadeler işe yarayacak (Martin Browns'ın cevabına oy verdim), ancak pahalılar (ve şahsen yasak bir şekilde birkaç karakterden daha uzun bir desen buluyorum)

Bu işlev

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 kenede 100.000 kez yapacak, normal ifade 25.000.000 keneyi alacak (ve derlenen normal ifade ile).

Daha iyi (yani daha hızlı) verilen bir değer için daha iyidir, ancak sürdürülmesi daha fazla koddur. "Daha iyi" çoğu zaman rakip gereksinimlerden ödün vermektir.

Bu yardımcı olur umarım :)

Güncelleme
Buna baktığımdan beri uzun bir süre oldu ve kod değiştikten sonra zamanlamaların güncellenmediğini fark ettim (sadece biraz değişti).

'Abbbbbbbbb' ile 100 kez tekrarlanan bir dizede (yani 1.000 bayt), 100.000 dönüşümlük bir çalışma, el kodlamalı işlevi 4,517,177 keneler alır ve aşağıdaki Regex, 59,435,719 alır ve El kodlu işlevi,% 7,6 oranında çalışır. regex.

Güncelleme 2 Kısaltmaları dikkate alacak mı? Şimdi olacak! If ifadesinin mantığı oldukça belirsizdir, bunu buna genişlettiğinizi görebilirsiniz ...

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

... hiç yardımcı olmuyor!

Kısaltmalar hakkında endişelenmeyen orijinal basit yöntem

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]! = '') Yukarıdaki kodu yeniden çalıştırırsanız boşluk eklemeye devam ederseniz, başkentten önce boşluk varsa bu boşluk eklenmesini durdurur mektup.
Paul Talbot

Emin değilim diye soracağımı düşündüm, bu yöntem Martin Brown'ın "DriveIsSCSICompatible" cevabında açıklandığı gibi kısaltmaları ele alıyor mu ideal "Drive SCSI Uyumlu" olur
Coops

Bu, 1 ifadenizin içeriğini yeni güncellenmiş if ifadeleri ile değiştirerek 1 karakter yaptı, yanlış bir şey yapıyor olabilir miyim?
Coops

1
Char.IsLetter (metin [i + 1]) için bir kontrol eklemek, özel karakter ve rakamlara sahip kısaltmalara yardımcı olur (yani ABC_DEF, AB C_DEF olarak bölünmez).
HeXanon

1
Kısaltmalar, KAPALI konumdayken doğru olduğundan emin değilim. Sadece "ASentenceABC" testi "ASentence AB C" ye kadar genişledi. "Bir C AB AB" olmalıdır
Tim Rutter

150

Çözümünüzün ilk T harfinden önce bir boşluk bırakmasıyla ilgili bir sorunu var.

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

Bu sorunun üstesinden gelmek için, kendisinden önceki küçük harfe bakın ve boşluğu ortasına yerleştirin:

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

Düzenleme 1:

Eğer kullanırsanız @"(\p{Ll})(\p{Lu})"aksanlı karakterler de alır.

Düzenleme 2:

Dizeleriniz kısaltmalar içeriyorsa, bunu kullanmak isteyebilirsiniz:

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

Böylece "DriveIsSCSICompatible", "Drive SCSI Uyumludur" olur


3
Orijinal RegEx ve Trim () sonuçlarını da saklayamaz mısınız?
PandaWood

3
@PandaWood yapabilirsiniz ama başka bir bellek ayırma ve dize kopyalama gerektirir. Performans bir endişe ise, Regex muhtemelen en iyi yol değildir.
Martin Brown

"([^A-Z\\s])([A-Z])"Kısaltmalar ile bile kullanabilir misiniz ?
Ruben9922

82

Performansı test etmedim, ama burada linq ile bir satırda:

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

18

Bu eski bir tane olduğunu biliyorum, ama bu bunu yapmak gerektiğinde kullandığım bir uzantısıdır:

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

Bu, MyCasedString.ToSentence()


Bunu bir uzatma yöntemi olarak seviyorum, eğer eklerseniz TrimStart(' ')önde gelen alanı kaldıracaktır.
user1069816

1
Teşekkürler @ user1069816. Ben SelectManybir dizin içeren aşırı yük kullanmak için uzantı değiştirdi , bu şekilde ilk harfi ve ek bir çağrı gereksiz potansiyel yükünü önler TrimStart(' '). Rob.
Rob Hardy

9

Kısaltmaları düzgün bir şekilde işleyecek ve tekrarlanabilir (zaten boşluklu sözcükleri değiştirmeyecek) İkili Worrier koduna dayalı basit bir uzatma yöntemi yapmak için yola çıktım. İşte benim sonucum.

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

İşte bu fonksiyonun geçtiği birim test durumları. Bu listeye tchrist'in önerdiği vakaların çoğunu ekledim. Geçemeyenlerin üçü (ikisi sadece Romen rakamlarıdır) yorumlanır:

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

Burada yayınlanan diğer çözüm gibi, "RegularOTs" dizesi ile başarısız. "Düzenli O Ts" döndürür
Patee Gutee 29:18

8

Unicode'a Hoşgeldiniz

Tüm bu çözümler modern metin için esasen yanlıştır. Vakayı anlayan bir şey kullanmanız gerekiyor. Bob başka diller istediğinden, Perl için bir çift vereceğim.

En kötüsünden en iyisine kadar dört çözüm sağlıyorum. Sadece en iyisi her zaman haklıdır. Diğerlerinin sorunları var. İşte size neyin işe yarayıp neyin yaramadığını ve nerede olduğunu gösteren bir test çalışması. Boşlukların nereye yerleştirildiğini görebilmeniz için alt çizgi kullandım ve yanlış olan herhangi bir şeyi yanlış işaretledim.

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, hemen hemen herkes burada "En Kötü" işaretli olan ilk yolu seçti. Birkaç tanesi "Tamam" olarak işaretlenmiş ikinci yolu seçti. Ama benden önce hiç kimse size "Daha İyi" veya "En İyi" yaklaşımını nasıl yapacağınızı göstermedi.

İşte dört yöntemi ile test programı:

#!/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;
}

Bu veri kümesindeki "En İyi" ile aynı skoru elde ettiğinizde, bunu doğru yaptığınızı bileceksiniz. O zamana kadar, yapmadın. Buradaki hiç kimse "Tamam" dan daha iyisini yapmamış ve çoğu "En Kötü" yapmamıştır. Birinin doğru post kodunu gönderdiğini görmek için sabırsızlanıyorum.

StackOverflow'un vurgulama kodunun tekrar sefil bir şekilde durduğunu fark ettim. Burada bahsi geçen kötü yaklaşımların geri kalanıyla (hepsi değil, hepsi) aynı eski topallığı yapıyorlar. ASCII'yi dinlendirmek uzun zaman almıyor mu? Artık mantıklı değil ve sahip olduğun tek şey gibi davranmak yanlış. Kötü kod yapar.


'En İyi' cevabınız şu ana kadar en yakın gibi görünüyor, ancak önde gelen noktalama işaretlerini veya diğer küçük harf olmayan harfleri içeriyor gibi görünmüyor. Bu benim için en iyi sonuç veriyor gibi görünüyor (java'da): replaceAll ("(? <= [^^ \\ p {javaUpperCase}]) (? = [\\ p {javaUpperCase}])", "");
Randyaa

Hmm. Romen rakamlarının bu örnekte gerçekten büyük harf sayılması gerektiğinden emin değilim. Harf değiştirici örneği kesinlikle sayılmamalıdır. McDonalds.com için giderseniz bir boşluk olmadan yazılmış göreceksiniz.
Martin Brown

Ayrıca, bunun asla mükemmel olmasını sağlayamayacağınıza da dikkat edilmelidir. Örneğin, "Alexander von Humboldt" olarak ortaya çıkması gereken "AlexandervonHumboldt" ı sıralayan bir örnek görmek istiyorum. O zaman elbette Büyük ve Küçük Harf'e sahip olmayan diller var.
Martin Brown

4

İkili Worrier, önerilen kodunu kullandım ve oldukça iyi, buna sadece bir küçük ekim var:

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

Bir koşul ekledim !char.IsUpper(text[i - 1]). Bu, 'Ortalama NOX' gibi bir şeyin 'Ortalama NOX'e dönüştürülmesine neden olacak bir hatayı düzeltti, bu da' Ortalama NOX 'okuması gerektiği için yanlış.

Ne yazık ki bu hala 'FromAStart' metnine sahipseniz, 'AStart'dan' çıkacağınıza dair bir hata var.

Bunu düzeltmek için herhangi bir düşünceniz var mı?


Belki böyle bir şey işe yarayabilir: char.IsUpper (text [i]) && (char.IsLower (text [i - 1]) || (char.IsLower (text [i + 1]))
Martin Brown

1
Bu doğru olanı: if (char.IsUpper(text[i]) && !(char.IsUpper(text[i - 1]) && char.IsUpper(text[i + 1])))Test sonucu: "Başlangıçtan", "Başlangıçtan", "Başlangıçtan", ancak i < text.Length - 1son karakteri yok saymak ve aralık dışı istisnayı önlemek için for döngüsü koşuluna ihtiyacınız vardır.
CallMeLaNN

Aynı şey. ! (a && b) ve (! a ||! b) çünkü alt =! üst.
CallMeLaNN

3

Benimki burada:

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

Bunun C # olması gerekiyor mu? Öyleyse Liste'de hangi ad alanı var? ArrayList veya List <string> mu demek istediniz?
Martin Brown

<string> listesi iyi olur. Bunun için üzgünüm.
Cory Foy

@Martin Her zaman doğru sözdizimine sahipti, <pre><code>code</code></pre>Markdown sözdizimi yerine sadece bir blokta gizlendi . Onu küçümsemenize gerek yok (eğer o sizseniz).
George Stocker

3

Emin olun değildir dizesinin başında boşluk koyarak, ancak edilir ardışık başkentleri arasına koyarak. Buradaki cevapların bazıları bu noktalardan birine veya her ikisine yönelik değildir. Normal ifade dışında başka yollar da vardır, ancak bunu kullanmayı tercih ediyorsanız şunu deneyin:

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

Bu \Bbir negatiftir \b, bu yüzden kelime-dışı bir sınırı temsil eder. Bu, paternin "Y" ile eşleştiği XYzabc, ancak Yzabcveya ile eşleşmediği anlamına gelir X Yzabc. Küçük bir bonus olarak, içinde boşluk olan bir dizede kullanabilirsiniz ve bunları ikiye katlamaz.


3

Bu Regex her büyük harfin önüne bir boşluk karakteri yerleştirir:

using System.Text.RegularExpressions;

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

"$ 1 $ 2" ise, ön boşluk dikkat edin, bunu yapacak.

Sonuç budur:

"This Is A String Without Spaces"

1
Sayıların da ayrılmasını istiyorsanız, bunun yerine bu normal ifade modelini kullanın:"([A-Z0-9])([a-z]*)"
Matthias Thomann

2

Sahip olduklarınız mükemmel çalışıyor. valueBu işlevin dönüş değerine yeniden atamayı unutmayın .

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

2

SQL'de nasıl yapabileceğiniz aşağıda açıklanmıştır

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

Dizede herhangi bir yerde Acyronyms dahil olmak üzere adınızı çözecek olan İki Satır Basit Regex'ten @MartinBrown'dan esinlenilmiştir.

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

Bu çözümü seviyorum. Kısa ve hızlı. Ancak, diğer çözümlere benzer şekilde, "RegularOTs" dizesi ile başarısız olur. Burada denediğim her çözüm "Regular O Ts" döndürüyor
Patee Gutee

@PateeGutee OP, capitols'tan önce yer istedi, kısaltmalardan bahsetmedi, üretim kodunda bunun için bir düzeltmemiz var
johnny 5

Düzeltmeyi gösterebilir misin? Verilerimde böyle dizeler var ve bana yanlış sonuç veriyor. Teşekkürler.
Patee Gutee

@PateeGutee Üzgünüm, ne istediğini yanlış anladım. Çoğullaştırma farklı bir konudur, "Düzenli OT'ler" ne olmasını bekliyorsunuz "Düzenli OT'ler" veya "Düzenli OT'ler"
johnny 5

1
@PateeGutee Cevabını senin için güncelledim, bunun işe yarayacağına inanıyorum
johnny 5

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

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'de Regexp aracılığıyla:

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

1
Ayy üzgünüm. C # -özel bir soru olduğunu kaçırdım ve buraya gönderdim Yakut cevap :(
Artem

1

Kevin Strikers'ı mükemmel bir çözüm buldum ve VB'ye çevirdim. Ben .NET 3.5 kilitli beri, ben de IsNullOrWhiteSpace yazmak zorunda kaldı. Bu onun bütün testlerini geçiyor.

<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

Soru biraz eski ama günümüzde Nuget'te tam olarak bunu yapan ve aynı zamanda insan tarafından okunabilir metne yapılan diğer birçok dönüşümü yapan güzel bir kütüphane var.

Check out Humanizer üzerinde GitHub'dan veya Nuget.

Misal

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

Sadece denedim ve ilk bağlantı koptu. NuGet çalışıyor, ancak paket benim çözümümde derlenmiyor. Güzel bir fikir, eğer işe yaradıysa.
philw

1

İçin iyi bir fırsat gibi görünüyor Aggregate. Bu, özellikle hızlı olması gerekmeksizin okunabilir olacak şekilde tasarlanmıştır.

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

0

Martin Brown'ın Yanıtı'na ek olarak, sayılarla da bir sorunum vardı. Örneğin: "Konum2" veya "Ocak22", sırasıyla "Konum 2" ve "Ocak 22" olmalıdır.

Martin Brown'ın cevabını kullanarak bunu yaptığım için Düzenli İfadem:

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

Her parçanın ne anlama geldiğini anlamak için birkaç harika site:

Java Tabanlı Düzenli İfade Analizörü (ancak çoğu .net regex'sinde çalışır)

Action Script Tabanlı Analizör

Yukarıdaki regex, tüm \p{Ll}ile [a-z], \p{Lu}ile [A-Z]ve \p{Nd}birlikte değiştirilmedikçe eylem komut dosyası sitesinde çalışmaz [0-9].


0

İşte İkili Worriers önerisine ve Richard Priddys'in yorumlarında oluşturmaya dayanan çözümüm, ancak sağlanan dizede beyaz alan olabileceğini de hesaba katarak, mevcut beyaz boşluğun yanına beyaz boşluk eklemeyecek şekilde.

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

Aynı soruyu cevaplayan bir C ++ işlevi arayan herkes için aşağıdakileri kullanabilirsiniz. Bu, @Binary Worrier tarafından verilen cevaptan sonra modellenmiştir. Bu yöntem sadece Kısaltmaları otomatik olarak korur.

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;

Bu işlev için kullandığım test dizeleri ve sonuçları:

  • "merhabaWorld" -> "merhaba Dünya"
  • "HelloWorld" -> "Merhaba Dünya"
  • "HelloABCWorld" -> "Merhaba ABC Dünyası"
  • "HelloWorldABC" -> "Merhaba Dünya ABC"
  • "ABCHelloWorld" -> "ABC Merhaba Dünya"
  • "ABC HELLO WORLD" -> "ABC HELLO WORLD"
  • "ABCHELLOWORLD" -> "ABCHELLOWORLD"
  • "A" -> "A"

0

Bir C # yalnızca ASCII karakterlerden oluşan bir giriş dizesi için çözüm. Regex içeriyor Negatif Geriye İlerleme dizesinin başında görünen bir sermaye (büyük harfle) harfi görmezden. İstenen dizeyi döndürmek için Regex.Replace () öğesini kullanır .

Ayrıca bkz. Regex101.com demosu .

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

Beklenen çıktı:

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

Güncelleme: Burada kısaltmaları (büyük harf dizileri) de ele alan bir varyasyon bulunmaktadır .

Ayrıca bkz. Regex101.com demosu ve ideone.com demosu .

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

Beklenen çıktı:

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

0

İşte kelimelerin önüne boşluk bırakmayan daha kapsamlı bir çözüm:

Not: Birden fazla Regex kullandım (kısa değil, aynı zamanda kısaltmaları ve tek harfli kelimeleri de işleyecek)

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

İçinde :

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

Çıkış :

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

Bu çıktı "Bu Dize NoSpace var ama başkentleri var"
Andy Robinson

Merhaba @AndyRobinson, teşekkürler. Birden çok Regex'in yerini alacak şekilde değiştirdim. Daha özlü bir yol olup olmadığından emin değilim, ama şimdi çalışıyor.
CrazyTim

0

Önceki tüm yanıtlar çok karmaşık görünüyordu.

Ben büyük harflerin karışımı vardı ve _ çok kullanılan, _, "" yapmak için string.Replace () vardı ve büyük harflere boşluk eklemek için aşağıdaki kullanılır.

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

İkili Worrier cevabından esinlenerek buna bir darbe attım.

İşte sonuç:

/// <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 yineleme ve çeşitli dize uzunlukları ve kombinasyonları çalıştıran kronometre kullanarak test yaptı.

İkili Worrier yanıtından ortalama% 50 (belki biraz daha fazla) daha hızlıdır.


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

Bu kısaltma ve kısaltma çoğulları içerir ve kabul edilen cevaptan biraz daha hızlıdır:

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

Bu testleri geçer:

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

kabul edilen cevap değerin null olduğu durumla ilgilidir
Chris F Carroll

Bu, çıktının önüne fazladan bir boşluk ekler, yani HireDate => "Kiralama Tarihi". Bir final gerekiyor.TrimStart falan. Sanırım diğer cevaplardan biri aşağıda belirtiliyor ama yeniden sıralama nedeniyle cevabı RegEx tabanlı olduğu için sizinle konuşup konuşmadığından emin değilim.
b_levitt

İyi yakalama ... testlerime bir başlangıç ​​ve bitiş işareti eklemeliydim ... şimdi düzeltildi.
Serj Sagan

Burada yayınlanan diğer çözüm gibi, "RegularOTs" dizesi ile başarısız. "Düzenli O Ts" döndürür
Patee Gutee 29:18

Kısaltma çoğullarını getirdiğiniz için teşekkürler, bunun için de çalıştım.
Serj Sagan

0

İle foldde bilinen bir uygulama 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());

İsteğe ek olarak, bu uygulama önde gelen, iç, sondaki boşlukları ve kısaltmaları doğru bir şekilde kaydeder, örneğin,

" SpacedWord " => " Spaced Word ",  

"Inner Space" => "Inner Space",  

"SomeACRONYM" => "Some ACRONYM".

0

Küçük harfler, büyük harfler veya rakamlardan sonra boşluk eklemenin basit bir yolu.

    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
Yalnızca kod yanıtları önerilmez. Tıklayınız düzenlemek önceki cevabı / cevapları dan nasıl cevap farklılık açıklamak belki ve kod adresleri nasıl soru özetleyen bazı kelimeler ekleyebilir veya. Şu kaynaktan
Nick
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.