大文字の前にスペースを追加する


193

「ThisStringHasNoSpacesButItDoesHaveCapitals」という文字列が与えられた場合、大文字の前にスペースを追加する最良の方法は何ですか。したがって、最後の文字列は「この文字列にはスペースがありませんが、大文字は含まれています」になります。

これがRegExでの私の試みです

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

2
あなたが取ったアプローチについて、あなたは特に不満を持っていますか?これは、メソッドの改善に役立つ場合があります。
ブレアコンラッド

正規表現が機能する場合は、それを使用します。正規表現は文字列操作用に最適化されています。
マイケルメドウズ

私は好奇心旺盛ですが、より良い、またはおそらく組み込みのアプローチがあるのです。他の言語で他のアプローチを見てみたいと思っています。
ボブ

2
変更された文字列は 'Replace'関数の戻り値であるため、コードは単に機能しませんでした。次のコード行: 'System.Text.RegularExpressions.Regex.Replace(value、 "[AZ]"、 "$ 0")。Trim();' それは完全に機能します。(私がこの投稿を偶然見つけて誰も実際には見なかったので、コメントしてください。あなたのコードの何が問題でしたか。)
Mattu475

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

回答:


203

正規表現は正常に機能します(私はマーティンブラウンズの回答に賛成票を投じました)が、高価です(個人的には、2文字以上のパターンが法外に鈍くなっていることがわかります)。

この機能

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がかかります。正規表現。

Update 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、

私が求めるだろうと思ったので、私は、わからないブラウンの答え「DriveIsSCSICompatibleは」理想的な「車でSCSI互換」になるマーティンで説明したように、このメソッドハンドルは、略語ん
小屋

それは、あなたのforステートメントの内容を新しく更新されたifステートメントで置き換えることによって1文字にしました。
Coops 2013

1
char.IsLetter(text [i + 1])のチェックを追加すると、特殊文字や数字を含む頭字語に役立ちます(つまり、ABC_DEFはAB C_DEFとして分割されません)。
-HeXanon

1
頭字語の部分がオフになっているときに正しいかわかりません。「ASentenceABC」が「ASentence AB C」に展開するテストを実行したところです。「A文AB C」である必要があります
Tim Rutter

149

あなたの解決策は、最初の文字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 2014年

3
@PandaWoodでは可能ですが、別のメモリ割り当てと文字列のコピーが必要になります。とはいえ、パフォーマンスが心配な場合は、正規表現はおそらく最良の方法ではありません。
マーティンブラウン

"([^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 2015年

1
@ user1069816に感謝します。SelectManyインデックスを含むオーバーロードを使用するように拡張機能を変更しました。これにより、最初の文字と、への追加の呼び出しによる不要な潜在的なオーバーヘッドが回避されTrimStart(' ')ます。ロブ。
Rob Hardy

8

Unicodeへようこそ

これらすべての解決策は、現代のテキストにとって本質的に間違っています。ケースを理解できるものを使用する必要があります。ボブが他の言語を求めてきたので、Perlのカップルをあげます。

最悪の問題から最良の問題まで、4つのソリューションを提供します。常に最高のものだけが正しい。他には問題があります。以下は、何が機能し、何が機能しないか、およびどこにあるかを示すテスト実行です。スペースが配置された場所を確認できるようにアンダースコアを使用し、間違っているものはすべて間違っているとマークしました。

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

ところで、ここのほとんどすべての人が最初の方法、「最悪」とマークされた方法を選択しました。「OK」とマークされた2番目の方法を選択した人もいます。しかし、私より前の誰も、「より良い」または「最良の」アプローチのいずれかを行う方法を示していません。

以下は、4つのメソッドを持つテストプログラムです。

#!/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」より上手くやったことはなく、ほとんどの人が「最悪」をやった。誰かが正しいℂ♯コードを投稿するのを楽しみにしています。

StackOverflowの強調表示コードが再び惨めに愚かであることに気づきました。彼らは、ここで述べられている他の貧弱なアプローチが作ったものとほとんど同じですが、すべて同じではありません。ASCIIを休止させるのは長い時間ではありませんか?それはもう意味をなさなくなり、あなたが持っているすべてが単に間違っているというふりをします。それは悪いコードになります。


あなたの「最良の」答えは今のところ最も近いようですが、先頭の句読点や他の先頭の小文字以外の文字を考慮しているようには見えません。これは私にとって(Javaで)最適に動作するようです:replaceAll( "(?<= [^^ \\ p {javaUpperCase}])(?= [\\ p {javaUpperCase}])"、 "");
Randyaa 2011年

うーん。この例では、ローマ数字が本当に大文字としてカウントされるかどうかはわかりません。文字修飾子の例は間違いなく数えるべきではありません。McDonalds.comにアクセスすると、スペースなしで書かれていることがわかります。
マーティンブラウン

また、これが完璧になることは決してありません。たとえば、「AlexandervonHumboldt」を選別する例を参照してください。これは、「Alexander von Humboldt」として終わるはずです。そしてもちろん、大文字と小文字の区別がない言語もあります。
マーティンブラウン

8

私は頭字語を適切に処理し、繰り返し可能なBinary Worrierのコードに基づいた単純な拡張メソッドを作成することに着手しました(すでに間隔を置いた単語を壊すことはありません)。これが私の結果です。

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

この関数が通過する単体テストケースを次に示します。私はtchristが提案したケースのほとんどをこのリストに追加しました。通過しない3つ(2つはローマ数字のみ)は​​コメント化されています。

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」で失敗します。「Regular O Ts」を返します
Patee Gutee

4

Binary Worrier、私はあなたの提案されたコードを使用しました、そしてそれはかなり良いです、私はそれに少しだけ追加しました:

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

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

条件を追加しました!char.IsUpper(text[i - 1])。これにより、「AverageNOX」のようなものが「Average NO X」に変わる原因となるバグが修正されました。

悲しいことに、これにはまだ「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])))テスト結果:「From Start」、「From THE Start」、「From A Start」。ただしi < text.Length - 1、forループ条件で最後の文字を無視して範囲外の例外を防ぐ必要があります。
CallMeLaNN 2011年

ああ、まったく同じです。!(a && b)および(!a ||!b)は、lower =!upperであるためです。
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#になるはずですか?もしそうなら、どの名前空間がリストされますか?ArrayListまたはList <string>ですか?
マーティンブラウン

List <string>で結構です。申し訳ありません。
Cory Foy

@Martin彼は常に正しい構文を持っていました、それは<pre><code>code</code></pre>Markdown構文の代わりにブロックに隠されていました。彼に反対票を投じる必要はありません(それがあなたの場合)。
ジョージストッカー

3

あなたがいることを確認していない文字列の先頭にスペースを入れて、あなたがしている連続した大文字の間にそれらを置きます。ここでの回答のいくつかは、これらのポイントの1つまたは両方を扱っていません。正規表現以外の方法がありますが、それを使用したい場合は、これを試してください:

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

\B否定される\bことは非単語境界を表しているので、。これは、パターンがで「Y」に一致するXYzabcが、Yzabcまたはでは一致しないことを意味しますX Yzabc。少しおまけとして、スペースを含む文字列でこれを使用すると、2倍にはなりません。


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]*)"
マティアス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からインスピレーションを受けた、2行の単純正規表現。これは、文字列の任意の場所にあるアキュロニムを含む、名前を解決します。

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

彼は私たちが生産タラでそのための修正持ち、略語は言及しなかった、OPは議事堂の前にスペースを望んでいた@PateeGutee
5ジョニー・

修正を表示できますか?データにこのような文字列が含まれていて、誤った結果が返されます。ありがとう。
Patee Gutee、

@PateeGutee申し訳ありませんが、私はあなたが望んでいたものを誤解しています。複数化は別の問題であり、「RegularOTs」で何が発生するのか "Regular OTs"または "Regular OT s"
johnny 5

1
@PateeGutee私はあなたのために私の答えを更新しました、私はそれがうまくいくはずだと信じています
ジョニー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#固有の質問であることを逃し、ここにRubyの回答を投稿しました:(
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 2014年

1

にとって良い機会のようですAggregate。これは読みやすいように設計されており、必ずしも高速である必要はありません。

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

0

マーティンブラウンの回答に加えて、数字にも問題がありました。例:「Location2」または「Jan22」は、それぞれ「Location 2」および「Jan 22」である必要があります。

マーティンブラウンの答えを使用して、これを行うための私の正規表現を次に示します。

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

以下に、各部分の意味を理解するためのいくつかの優れたサイトを示します。

Javaベースの正規表現アナライザー(ただし、ほとんどの.net正規表現で機能します)

アクションスクリプトベースのアナライザー

あなたはすべてを置き換えない限り、上記の正規表現は、アクションスクリプトのサイト上では動作しません\p{Ll}[a-z]\p{Lu}[A-Z]、と、\p{Nd}[0-9]


0

Binary Worriersの提案と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文字だけからなる入力文字列のためのソリューション。正規表現は、組み込まれて、負の後読みを文字列の先頭に表示されます資本(大文字)文字を無視します。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

以下は、単語の前にスペースを入れない、より完全なソリューションです。

注:複数の正規表現を使用しました(簡潔ではありませんが、頭字語と1文字の単語も処理します)

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

こんにちは@AndyRobinson、ありがとう。複数の正規表現置換を使用するように変更しました。より簡潔な方法があるかどうかはわかりませんが、現在は機能します。
CrazyTim 2015

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か何かが必要です。それが他の回答の1つが下で指摘しているものだと思いますが、彼の回答はRegExに基づいているため、並べ替えのため、彼があなたと話していたかどうかはわかりません。
b_levitt、2015

良いキャッチ...私のテストに開始と終了のマーカーを追加する必要があります...今修正されました。
Serj Sagan、2015

ここに投稿された他のソリューションと同様に、文字列「RegularOTs」で失敗します。「Regular 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.