「ThisStringHasNoSpacesButItDoesHaveCapitals」という文字列が与えられた場合、大文字の前にスペースを追加する最良の方法は何ですか。したがって、最後の文字列は「この文字列にはスペースがありませんが、大文字は含まれています」になります。
これがRegExでの私の試みです
System.Text.RegularExpressions.Regex.Replace(value, "[A-Z]", " $0")
「ThisStringHasNoSpacesButItDoesHaveCapitals」という文字列が与えられた場合、大文字の前にスペースを追加する最良の方法は何ですか。したがって、最後の文字列は「この文字列にはスペースがありませんが、大文字は含まれています」になります。
これがRegExでの私の試みです
System.Text.RegularExpressions.Regex.Replace(value, "[A-Z]", " $0")
回答:
正規表現は正常に機能します(私はマーティンブラウンズの回答に賛成票を投じました)が、高価です(個人的には、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();
}
あなたの解決策は、最初の文字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」になります
"([^A-Z\\s])([A-Z])"
頭字語でも使用できますか?
これは古いものですが、これは私がこれを行う必要があるときに使用する拡張機能です。
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(' ')
すると、先頭のスペースが削除されます。
SelectMany
インデックスを含むオーバーロードを使用するように拡張機能を変更しました。これにより、最初の文字と、への追加の呼び出しによる不要な潜在的なオーバーヘッドが回避されTrimStart(' ')
ます。ロブ。
これらすべての解決策は、現代のテキストにとって本質的に間違っています。ケースを理解できるものを使用する必要があります。ボブが他の言語を求めてきたので、Perlのカップルをあげます。
最悪の問題から最良の問題まで、4つのソリューションを提供します。常に最高のものだけが正しい。他には問題があります。以下は、何が機能し、何が機能しないか、およびどこにあるかを示すテスト実行です。スペースが配置された場所を確認できるようにアンダースコアを使用し、間違っているものはすべて間違っているとマークしました。
Testing TheLoneRanger
Worst: The_Lone_Ranger
Ok: The_Lone_Ranger
Better: The_Lone_Ranger
Best: The_Lone_Ranger
Testing MountMᶜKinleyNationalPark
[WRONG] Worst: Mount_MᶜKinley_National_Park
[WRONG] Ok: Mount_MᶜKinley_National_Park
[WRONG] Better: Mount_MᶜKinley_National_Park
Best: Mount_Mᶜ_Kinley_National_Park
Testing ElÁlamoTejano
[WRONG] Worst: ElÁlamo_Tejano
Ok: El_Álamo_Tejano
Better: El_Álamo_Tejano
Best: El_Álamo_Tejano
Testing TheÆvarArnfjörðBjarmason
[WRONG] Worst: TheÆvar_ArnfjörðBjarmason
Ok: The_Ævar_Arnfjörð_Bjarmason
Better: The_Ævar_Arnfjörð_Bjarmason
Best: The_Ævar_Arnfjörð_Bjarmason
Testing IlCaffèMacchiato
[WRONG] Worst: Il_CaffèMacchiato
Ok: Il_Caffè_Macchiato
Better: Il_Caffè_Macchiato
Best: Il_Caffè_Macchiato
Testing MisterDženanLjubović
[WRONG] Worst: MisterDženanLjubović
[WRONG] Ok: MisterDženanLjubović
Better: Mister_Dženan_Ljubović
Best: Mister_Dženan_Ljubović
Testing OleKingHenryⅧ
[WRONG] Worst: Ole_King_HenryⅧ
[WRONG] Ok: Ole_King_HenryⅧ
[WRONG] Better: Ole_King_HenryⅧ
Best: Ole_King_Henry_Ⅷ
Testing CarlosⅤºElEmperador
[WRONG] Worst: CarlosⅤºEl_Emperador
[WRONG] Ok: CarlosⅤº_El_Emperador
[WRONG] Better: CarlosⅤº_El_Emperador
Best: Carlos_Ⅴº_El_Emperador
ところで、ここのほとんどすべての人が最初の方法、「最悪」とマークされた方法を選択しました。「OK」とマークされた2番目の方法を選択した人もいます。しかし、私より前の誰も、「より良い」または「最良の」アプローチのいずれかを行う方法を示していません。
以下は、4つのメソッドを持つテストプログラムです。
#!/usr/bin/env perl
use utf8;
use strict;
use warnings;
# First I'll prove these are fine variable names:
my (
$TheLoneRanger ,
$MountMᶜKinleyNationalPark ,
$ElÁlamoTejano ,
$TheÆvarArnfjörðBjarmason ,
$IlCaffèMacchiato ,
$MisterDženanLjubović ,
$OleKingHenryⅧ ,
$CarlosⅤºElEmperador ,
);
# Now I'll load up some string with those values in them:
my @strings = qw{
TheLoneRanger
MountMᶜKinleyNationalPark
ElÁlamoTejano
TheÆvarArnfjörðBjarmason
IlCaffèMacchiato
MisterDženanLjubović
OleKingHenryⅧ
CarlosⅤºElEmperador
};
my($new, $best, $ok);
my $mask = " %10s %-8s %s\n";
for my $old (@strings) {
print "Testing $old\n";
($best = $old) =~ s/(?<=\p{Lowercase})(?=[\p{Uppercase}\p{Lt}])/_/g;
($new = $old) =~ s/(?<=[a-z])(?=[A-Z])/_/g;
$ok = ($new ne $best) && "[WRONG]";
printf $mask, $ok, "Worst:", $new;
($new = $old) =~ s/(?<=\p{Ll})(?=\p{Lu})/_/g;
$ok = ($new ne $best) && "[WRONG]";
printf $mask, $ok, "Ok:", $new;
($new = $old) =~ s/(?<=\p{Ll})(?=[\p{Lu}\p{Lt}])/_/g;
$ok = ($new ne $best) && "[WRONG]";
printf $mask, $ok, "Better:", $new;
($new = $old) =~ s/(?<=\p{Lowercase})(?=[\p{Uppercase}\p{Lt}])/_/g;
$ok = ($new ne $best) && "[WRONG]";
printf $mask, $ok, "Best:", $new;
}
このデータセットで「最高」と同じスコアを取得できる場合は、正しく実行したことがわかります。それまではそうしていません。ここの他の誰もが「OK」より上手くやったことはなく、ほとんどの人が「最悪」をやった。誰かが正しいℂ♯コードを投稿するのを楽しみにしています。
StackOverflowの強調表示コードが再び惨めに愚かであることに気づきました。彼らは、ここで述べられている他の貧弱なアプローチが作ったものとほとんど同じですが、すべて同じではありません。ASCIIを休止させるのは長い時間ではありませんか?それはもう意味をなさなくなり、あなたが持っているすべてが単に間違っているというふりをします。それは悪いコードになります。
私は頭字語を適切に処理し、繰り返し可能な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());
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」が取得されるというバグがあります。
これを修正することについての考えは?
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ループ条件で最後の文字を無視して範囲外の例外を防ぐ必要があります。
これが私のものです:
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());
}
<pre><code>code</code></pre>
Markdown構文の代わりにブロックに隠されていました。彼に反対票を投じる必要はありません(それがあなたの場合)。
あなたがいることを確認していない文字列の先頭にスペースを入れて、あなたがしている連続した大文字の間にそれらを置きます。ここでの回答のいくつかは、これらのポイントの1つまたは両方を扱っていません。正規表現以外の方法がありますが、それを使用したい場合は、これを試してください:
Regex.Replace(value, @"\B[A-Z]", " $0")
\B
否定される\b
ことは非単語境界を表しているので、。これは、パターンがで「Y」に一致するXYzabc
が、Yzabc
またはでは一致しないことを意味しますX Yzabc
。少しおまけとして、スペースを含む文字列でこれを使用すると、2倍にはなりません。
この正規表現は、すべての大文字の前にスペース文字を配置します。
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"
"([A-Z0-9])([a-z]*)"
これが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
@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();
}
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();
}
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
質問は少し古いですが、最近では、正確にこれを実行するだけでなく、人間が読めるテキストへの他の多くの変換を実行する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"
マーティンブラウンの回答に加えて、数字にも問題がありました。例:「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]
。
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();
}
これと同じ質問に答える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;
この関数に使用したテスト文字列と結果は次のとおりです。
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]
以下は、単語の前にスペースを入れない、より完全なソリューションです。
注:複数の正規表現を使用しました(簡潔ではありませんが、頭字語と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.)
以前のすべての応答は複雑すぎるように見えました。
大文字と_が混在した文字列があり、そのため、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.
}
}
}
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%(多分もう少し)速くなります。
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;
}
これは頭字語と頭字語の複数形を含み、受け入れられた答えよりも少し高速です:
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";
と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".
小文字、大文字または数字の後にスペースを追加する簡単な方法。
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;
}