Tambahkan spasi sebelum Capital Letters


193

Diberikan string "ThisStringHasNoSpacesButItDoesHaveCapitals" apa cara terbaik untuk menambahkan spasi sebelum huruf kapital. Jadi string akhirnya adalah "String Ini Tidak Memiliki Spasi Tapi Itu Memiliki Modal"

Ini adalah usaha saya dengan RegEx

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

2
Apakah Anda memiliki keluhan khusus tentang pendekatan yang Anda lakukan? Itu mungkin membantu kami meningkatkan metode Anda.
Blair Conrad

Jika regex bekerja, maka saya akan tetap dengan itu. Regex dioptimalkan untuk manipulasi string.
Michael Meadows

Saya hanya ingin tahu apakah ada pendekatan yang lebih baik atau bahkan built-in. Saya bahkan ingin tahu melihat pendekatan lain dengan bahasa lain.
Bob

2
Kode Anda tidak berfungsi karena string yang dimodifikasi adalah nilai balik dari fungsi 'Ganti'. Dengan baris kode ini: 'System.Text.RegularExpressions.Regex.Replace (value, "[AZ]", "$ 0"). Trim ();' itu akan bekerja dengan sempurna. (Hanya berkomentar karena saya sengaja menemukan posting ini dan tidak ada yang benar-benar melihat, apa yang salah dengan kode Anda.)
Mattu475

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

Jawaban:


203

Regex akan berfungsi dengan baik (saya bahkan memilih jawaban Martin Browns), tetapi biayanya mahal (dan secara pribadi saya menemukan pola yang lebih panjang dari beberapa karakter yang tumpul)

Fungsi ini

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

Akan melakukannya 100.000 kali dalam 2.968.750 kutu, regex akan mengambil 25.000.000 kutu (dan thats dengan regex dikompilasi).

Lebih baik, untuk nilai yang diberikan lebih baik (yaitu lebih cepat) namun lebih banyak kode untuk dipelihara. "Lebih baik" sering berkompromi dengan persyaratan yang bersaing.

Semoga ini membantu :)

Pembaruan
Sudah lama sejak saya melihat ini, dan saya baru menyadari bahwa timing belum diperbarui karena kode berubah (hanya sedikit berubah).

Pada string dengan 'Abbbbbbbbbb' diulang 100 kali (yaitu 1.000 byte), menjalankan 100.000 konversi mengambil fungsi kode tangan 4,517.177 kutu, dan Regex di bawah ini mengambil 59.435.719 membuat fungsi kode Tangan dijalankan di 7,6% dari waktu yang dibutuhkan Regex.

Perbarui 2 Akankah Akronim diperhitungkan? Sekarang akan! Logika statemen if cukup tidak jelas, seperti yang Anda lihat memperluas ke ini ...

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

... tidak membantu sama sekali!

Inilah metode sederhana asli yang tidak khawatir tentang Akronim

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]! = '') Jika Anda menjalankan kembali kode di atas, ia menambahkan spasi, ini akan menghentikan spasi yang ditambahkan jika ada spasi sebelum ibukota surat.
Paul Talbot

Saya tidak yakin jadi saya pikir saya akan bertanya, apakah metode ini menangani akronim seperti yang dijelaskan dalam jawaban Martin Brown "DriveIsSCSICcompatible" idealnya akan menjadi "Drive Is SCSI Compatible"
Coops

Itu membuatnya menjadi 1 karakter dengan mengganti konten pernyataan for Anda dengan pernyataan if yang baru diperbarui, saya mungkin melakukan sesuatu yang salah?
Coops

1
Menambahkan tanda centang untuk char.IsLetter (teks [i + 1]) membantu dengan akronim dengan karakter dan digit khusus (yaitu ABC_DEF tidak akan dipisah sebagai AB C_DEF).
HeXanon

1
Saya tidak yakin bagian akronim benar ketika dimatikan. Saya baru saja menjalankan tes "ASentenceABC" memperluas ke "ASentence AB C". Harusnya "A Kalimat AB C"
Tim Rutter

149

Solusi Anda memiliki masalah karena menempatkan spasi sebelum huruf pertama T sehingga Anda dapat

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

Untuk menyiasati tampilan ini, huruf kecil sebelumnya juga dan kemudian masukkan spasi di tengah:

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

Edit 1:

Jika Anda menggunakan @"(\p{Ll})(\p{Lu})" akan mengambil karakter beraksen juga.

Edit 2:

Jika string Anda dapat berisi akronim, Anda mungkin ingin menggunakan ini:

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

Jadi "DriveIsSCSICcompatible" menjadi "Drive Is SCSI Compatible"


3
Tidak bisakah Anda juga membiarkan RegEx dan Trim () hasilnya asli?
PandaWood

3
@ PandaWood Anda bisa tetapi membutuhkan alokasi memori dan salinan string lain. Yang mengatakan jika kinerja khawatir Regex mungkin bukan cara terbaik untuk pergi bagaimanapun.
Martin Brown

Bisakah Anda juga menggunakan "([^A-Z\\s])([A-Z])", bahkan dengan akronim?
Ruben9922

82

Tidak menguji kinerja, tetapi di sini sejalan dengan LINQ:

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

18

Saya tahu ini adalah yang lama, tetapi ini adalah ekstensi yang saya gunakan ketika saya perlu melakukan ini:

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

Ini akan memungkinkan Anda untuk menggunakan MyCasedString.ToSentence()


Saya suka ide ini sebagai metode ekstensi, jika Anda menambahkannya TrimStart(' ')akan menghapus ruang utama.
user1069816

1
Terima kasih @ user1069816. Saya telah mengubah ekstensi untuk menggunakan kelebihan SelectManyyang mencakup indeks, dengan cara ini ia menghindari huruf pertama dan potensi overhead yang tidak perlu dari panggilan tambahan ke TrimStart(' '). Rampok.
Rob Hardy

8

Selamat datang di Unicode

Semua solusi ini pada dasarnya salah untuk teks modern. Anda perlu menggunakan sesuatu yang mengerti kasus. Karena Bob meminta bahasa lain, saya akan memberikan pasangan untuk Perl.

Saya memberikan empat solusi, mulai dari yang terburuk hingga yang terbaik. Hanya yang terbaik selalu benar. Yang lain memiliki masalah. Berikut ini adalah uji coba untuk menunjukkan kepada Anda apa yang berhasil dan yang tidak, dan di mana. Saya telah menggunakan garis bawah sehingga Anda dapat melihat di mana spasi telah diletakkan, dan saya telah menandai apa pun yang salah, yah, salah.

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, hampir semua orang di sini telah memilih cara pertama, yang ditandai "Terburuk". Beberapa telah memilih cara kedua, ditandai "OK". Tetapi tidak ada orang lain sebelum saya yang menunjukkan kepada Anda bagaimana melakukan pendekatan "Lebih Baik" atau "Terbaik".

Berikut adalah program pengujian dengan empat metodenya:

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

Saat Anda dapat skor yang sama dengan "Terbaik" pada dataset ini, Anda akan tahu bahwa Anda telah melakukannya dengan benar. Sampai saat itu, kamu belum. Tidak ada orang lain di sini yang melakukan lebih baik daripada "Ok", dan sebagian besar melakukannya "Terburuk". Saya berharap dapat melihat seseorang memposting kode ℂ♯ yang benar.

Saya perhatikan bahwa kode highlight StackOverflow bungkuk lagi. Mereka membuat semua lumpuh yang sama seperti (kebanyakan tetapi tidak semua) dari sisa pendekatan miskin yang disebutkan di sini. Bukankah sudah lama melewati ASCII untuk beristirahat? Itu tidak masuk akal lagi, dan berpura-pura semua yang Anda miliki hanyalah salah. Itu membuat kode yang buruk.


jawaban 'Terbaik' Anda tampaknya paling dekat sejauh ini, tetapi sepertinya itu bukan tanda baca utama atau huruf besar non-huruf kecil lainnya. Ini sepertinya bekerja paling baik untuk saya (dalam java): replaceAll ("(? <= [^^ \\ p {javaUpperCase}])) (? = [\\ p {javaUpperCase}])", "");
Randyaa

Hmm. Saya tidak yakin angka romawi harus benar-benar dihitung sebagai huruf besar dalam contoh ini. Contoh pengubah huruf jelas tidak harus dihitung. Jika Anda membuka McDonalds.com, Anda akan melihatnya ditulis tanpa spasi.
Martin Brown

Perlu juga dicatat bahwa Anda tidak akan pernah mendapatkan ini menjadi sempurna. Misalnya saya ingin melihat contoh yang menguraikan "AlexandervonHumboldt", yang seharusnya berakhir sebagai "Alexander von Humboldt". Lalu tentu saja ada bahasa yang tidak memiliki Destruction Capital dan Huruf Kecil.
Martin Brown

8

Saya mulai membuat metode ekstensi sederhana berdasarkan kode Binary Worrier's yang akan menangani akronim dengan benar, dan dapat diulangi (tidak akan memotong-motong kata yang sudah spasi). Ini hasil saya.

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

Berikut adalah unit-unit uji kasus yang dilewati fungsi ini. Saya menambahkan sebagian besar kasus yang disarankan tchrist ke daftar ini. Tiga dari mereka yang tidak lulus (dua hanya angka Romawi) dikomentari:

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

Mirip dengan solusi lain yang diposting di sini, gagal dengan string "RegularOTs". Ia mengembalikan "Reguler O Ts"
Patee Gutee

4

Binary Worrier, saya telah menggunakan kode yang Anda sarankan, dan itu agak bagus, saya hanya memiliki satu tambahan kecil untuk itu:

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

Saya telah menambahkan sebuah kondisi !char.IsUpper(text[i - 1]) . Ini memperbaiki bug yang akan menyebabkan sesuatu seperti 'AverageNOX' diubah menjadi 'Average NO X', yang jelas-jelas salah, karena seharusnya membaca 'Average NOX'.

Sayangnya ini masih memiliki bug bahwa jika Anda memiliki teks 'FromAStart', Anda akan mendapatkan 'From AStart'.

Adakah pemikiran untuk memperbaikinya?


Mungkin sesuatu seperti ini akan berfungsi: char.IsUpper (teks [i]) && (char.IsLower (teks [i - 1]) || (char.IsLower (teks [i + 1]))
Martin Brown

1
Ini yang benar: if (char.IsUpper(text[i]) && !(char.IsUpper(text[i - 1]) && char.IsUpper(text[i + 1])))Hasil pengujian: "Dari Mulai", "Dari Awal", "Dari Awal" tetapi Anda perlu i < text.Length - 1dalam kondisi loop untuk mengabaikan karakter terakhir dan mencegah pengecualian di luar jangkauan.
CallMeLaNN

Oh, sama saja. ! (a && b) dan (! a ||! b) karena lebih rendah =! atas.
CallMeLaNN

3

Ini milik saya:

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

Apakah itu seharusnya C #? Jika demikian, apakah namespace itu Daftar? Apakah maksud Anda ArrayList atau List <string>?
Martin Brown

Daftar <string> akan baik-baik saja. Maaf soal itu.
Cory Foy

@ Martin Dia selalu memiliki sintaks yang benar, itu hanya disembunyikan di <pre><code>code</code></pre>blok bukannya sintaks Markdown. Tidak perlu menurunkannya (jika itu kamu).
George Stocker

3

Pastikan Anda tidak menempatkan spasi di awal string, tetapi Anda yang menempatkan mereka antara ibukota berturut-turut. Beberapa jawaban di sini tidak membahas satu atau kedua poin itu. Ada cara lain selain regex, tetapi jika Anda lebih suka menggunakannya, coba ini:

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

Ini \Bdinegasikan \b, sehingga mewakili batas non-kata. Ini berarti polanya cocok dengan "Y" di XYzabc, tetapi tidak di Yzabcatau X Yzabc. Sebagai bonus kecil, Anda dapat menggunakan ini pada string dengan spasi di dalamnya dan itu tidak akan menggandakannya.


3

Regex ini menempatkan karakter spasi di depan setiap huruf kapital:

using System.Text.RegularExpressions;

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

Pikirkan ruang di depan jika "$ 1 $ 2", inilah yang akan menyelesaikannya.

Inilah hasilnya:

"This Is A String Without Spaces"

1
Jika Anda ingin angka juga dipisahkan, gunakan pola regex ini sebagai gantinya:"([A-Z0-9])([a-z]*)"
Matthias Thomann

2

Apa yang Anda miliki berfungsi dengan sempurna. Ingatlah untuk menetapkan valuekembali nilai pengembalian fungsi ini.

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

2

Inilah cara Anda bisa melakukannya dalam 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

Terinspirasi dari @MartinBrown, Two Lines of Simple Regex, yang akan menyelesaikan nama Anda, termasuk Acyronyms di mana saja di string.

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

Saya suka solusi ini. Ini pendek dan cepat. Namun, mirip dengan solusi lain, gagal dengan string "RegularOTs". Setiap solusi yang saya coba di sini mengembalikan "Regular O Ts"
Patee Gutee

@PateeGutee OP ingin ruang sebelum ibukota, dia tidak menyebutkan singkatan, kami memiliki perbaikan untuk itu dalam cod produksi
johnny 5

Bisakah Anda menunjukkan perbaikannya? Saya memiliki string seperti ini di data saya dan itu memberi saya hasil yang salah. Terima kasih.
Patee Gutee

@PateeGutee Maaf, saya salah membaca apa yang Anda inginkan. Pluralisasi adalah masalah yang berbeda, `RegularOTs 'apa yang Anda harapkan terjadi" PL Reguler "atau" PL OT Reguler "
johnny 5

1
@PateeGutee Saya telah memperbarui jawaban saya untuk Anda, saya yakin itu akan berhasil
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

Di Ruby, melalui Regexp:

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

1
Ups, maaf. Saya telah merindukan bahwa itu adalah pertanyaan spesifik C # dan diposting di sini Ruby menjawab :(
Artem

1

Saya mengambil solusi luar biasa Kevin Strikers dan dikonversi ke VB. Karena saya terkunci di .NET 3.5, saya juga harus menulis IsNullOrWhiteSpace. Ini melewati semua tesnya.

<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

Pertanyaannya agak lama tetapi saat ini ada perpustakaan yang bagus di Nuget yang melakukan hal ini dan juga banyak konversi lainnya ke teks yang dapat dibaca manusia.

Lihat Humanizer di GitHub atau Nuget.

Contoh

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

Baru saja mencobanya dan tautan pertama sekarang rusak. NuGet berfungsi, tetapi paket itu tidak dikompilasi dalam solusi saya. Ide yang bagus, jika berhasil.
philw

1

Sepertinya ini peluang bagus Aggregate. Ini dirancang agar dapat dibaca, tidak harus terutama cepat.

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

0

Selain Jawaban Martin Brown, saya juga memiliki masalah dengan angka. Sebagai Contoh: "Location2", atau "Jan22" harus masing-masing "Location 2", dan "Jan 22".

Inilah Ekspresi Reguler saya untuk melakukan itu, menggunakan jawaban Martin Brown:

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

Berikut adalah beberapa situs hebat untuk mencari tahu apa arti setiap bagian juga:

Analyzer Ekspresi Reguler Berbasis Java (tetapi bekerja untuk sebagian besar .net regex's)

Penganalisa Berbasis Script Aksi

Regex di atas tidak akan berfungsi di situs skrip tindakan kecuali jika Anda mengganti semua \p{Ll}dengan [a-z], \p{Lu}dengan [A-Z], dan \p{Nd}dengan [0-9].


0

Inilah solusi saya, berdasarkan saran Binary Worriers dan membangun dalam komentar Richard Priddys, tetapi juga memperhitungkan bahwa ruang putih mungkin ada dalam string yang disediakan, sehingga tidak akan menambah ruang putih di sebelah ruang putih yang ada.

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

Bagi siapa saja yang mencari fungsi C ++ menjawab pertanyaan yang sama, Anda dapat menggunakan yang berikut ini. Ini dimodelkan setelah jawaban yang diberikan oleh @Binary Worrier. Metode ini hanya mempertahankan Acronyms secara otomatis.

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;

String tes yang saya gunakan untuk fungsi ini, dan hasilnya adalah:

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

Solusi C # untuk string input yang hanya terdiri dari karakter ASCII. The regex menggabungkan lookbehind negatif untuk mengabaikan modal (huruf) huruf yang muncul pada awal string. Menggunakan Regex.Replace () untuk mengembalikan string yang diinginkan.

Lihat juga demo 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);
    }
}

Output yang Diharapkan:

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

Pembaruan: Berikut adalah variasi yang juga akan menangani akronim (urutan huruf besar).

Juga melihat regex101.com demo dan 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);
    }
}

Output yang Diharapkan:

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

0

Berikut adalah solusi yang lebih menyeluruh yang tidak menempatkan spasi di depan kata-kata:

Catatan: Saya telah menggunakan banyak Regex (tidak ringkas tetapi juga akan menangani akronim dan kata-kata huruf tunggal)

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

Dalam :

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

Keluar :

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

Ini menghasilkan "String Ini Memiliki NoSpaces Tapi Itu Memiliki Kapital"
Andy Robinson

Hai @AndyRobinson, terima kasih. Saya berubah untuk menggunakan beberapa pengganti Regex. Tidak yakin apakah ada cara yang lebih ringkas, tetapi berhasil sekarang.
CrazyTim

0

Semua tanggapan sebelumnya tampak terlalu rumit.

Saya memiliki string yang memiliki campuran ibukota dan _ jadi digunakan, string.Replace () untuk membuat _, "" dan menggunakan yang berikut untuk menambahkan spasi pada huruf kapital.

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

Terinspirasi oleh jawaban Binary Worrier, saya mencoba ini.

Inilah hasilnya:

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

Melakukan tes menggunakan stopwatch yang menjalankan 10000000 iterasi dan berbagai panjang string serta kombinasi.

Rata-rata 50% (mungkin sedikit lebih) lebih cepat dari jawaban Binary Worrier.


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

Yang ini termasuk akronim dan bentuk jamak akronim dan sedikit lebih cepat dari jawaban yang diterima:

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

Lulus tes ini:

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

jawaban yang diterima berkaitan dengan kasus di mana nilainya nol
Chris F Carroll

Ini menambahkan ruang ekstra di depan output, yaitu HireDate => "Tanggal Sewa". Membutuhkan final. Mulai Mulai atau sesuatu. Saya pikir itulah yang ditunjukkan oleh salah satu jawaban lain di bawah ini tetapi karena pengurutan ulang saya tidak yakin apakah dia berbicara kepada Anda karena jawabannya didasarkan pada RegEx.
b_levitt

Tangkapan yang bagus ... seharusnya menambahkan penanda awal dan akhir pada pengujian saya ... diperbaiki sekarang.
Serj Sagan

Mirip dengan solusi lain yang diposting di sini, gagal dengan string "RegularOTs". Ia mengembalikan "Reguler O Ts"
Patee Gutee

Terima kasih telah membawa bentuk jamak singkatan, saya telah memperbarui untuk bekerja untuk ini juga.
Serj Sagan

0

Implementasi dengan fold, juga dikenal sebagai 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());

Selain permintaan, implementasi ini dengan benar menghemat spasi, akronim, spasi, dan terdepan, misalnya,

" SpacedWord " => " Spaced Word ",  

"Inner Space" => "Inner Space",  

"SomeACRONYM" => "Some ACRONYM".

0

Cara sederhana untuk menambahkan spasi setelah huruf kecil, huruf besar atau digit.

    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
Jawaban khusus kode tidak disarankan. Silakan klik edit dan tambahkan beberapa kata yang meringkas bagaimana kode Anda menjawab pertanyaan, atau mungkin menjelaskan bagaimana jawaban Anda berbeda dari jawaban / jawaban sebelumnya. Dari Ulasan
Nick
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.