文字列「ThisStringHasNoSpacesButItDoesHaveCapitals」が与えられた場合、大文字の前にスペースを追加する最良の方法は何ですか。したがって、終了文字列は「この文字列にはスペースはありませんが、大文字があります」となります
正規表現を使った私の試みです
System.Text.RegularExpressions.Regex.Replace(value, "[A-Z]", " $0")
正規表現は問題なく動作しますが(Martin Brownsの回答に投票しました)、それらは高価です(そして、個人的には、いくつかの文字が法外に鈍角であるよりも長いパターンを見つけます)
この機能
string AddSpacesToSentence(string text, bool preserveAcronyms)
{
if (string.IsNullOrWhiteSpace(text))
return string.Empty;
StringBuilder newText = new StringBuilder(text.Length * 2);
newText.Append(text[0]);
for (int i = 1; i < text.Length; i++)
{
if (char.IsUpper(text[i]))
if ((text[i - 1] != ' ' && !char.IsUpper(text[i - 1])) ||
(preserveAcronyms && char.IsUpper(text[i - 1]) &&
i < text.Length - 1 && !char.IsUpper(text[i + 1])))
newText.Append(' ');
newText.Append(text[i]);
}
return newText.ToString();
}
2,968,750ティックで100,000回実行します。正規表現は25,000,000ティック(およびコンパイルされた正規表現を使用)になります。
優れた(つまり高速な)所定の値に対しては優れていますが、維持するコードは増えます。 「より良い」とは、しばしば競合する要件の妥協です。
お役に立てれば :)
更新
これを見てから長い間、コードが変更されてからタイミングが更新されていないことに気付きました(少し変更されただけです)。
'Abbbbbbbbb'が100回(つまり1,000バイト)繰り返される文字列では、100,000回の変換の実行に手コード化された関数4,517,177ティックがかかり、以下の正規表現に59,435,719がかかり、手コード化された関数は7.6%の時間で実行されます正規表現。
更新2頭字語は考慮されますか?今すぐ! ifステートメントのロジックはかなり曖昧です。これを展開するとわかりますが...
if (char.IsUpper(text[i]))
if (char.IsUpper(text[i - 1]))
if (preserveAcronyms && i < text.Length - 1 && !char.IsUpper(text[i + 1]))
newText.Append(' ');
else ;
else if (text[i - 1] != ' ')
newText.Append(' ');
...まったく役に立ちません!
頭字語を気にしない元のsimpleメソッド
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」になります
パフォーマンスをテストしませんでしたが、ここではlinqを使用して1行で記述しています。
var val = "ThisIsAStringToTest";
val = string.Concat(val.Select(x => Char.IsUpper(x) ? " " + x : x.ToString())).TrimStart(' ');
これは古いものですが、これを行う必要があるときに使用する拡張機能です。
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()
を使用できます
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());
これらのソリューションはすべて、現代のテキストにとって本質的に間違っています。大文字と小文字を理解するものを使用する必要があります。 Bobが他の言語を要求したので、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、あなたの提案したコードを使用しましたが、それはかなり良いです、私はそれに1つだけマイナーな追加があります:
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 N O X」に変わるバグが修正されましたが、「Average NOX」と表示されるはずです。
悲しいことに、これにはまだ「FromAStart」というテキストがある場合、「From AStart」が出るというバグがあります。
これを修正する上で何か考えはありますか?
これが私のものです:
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());
}
必ずare n't文字列の先頭にスペースを入れますが、are連続する大文字の間にスペースを入れてください。ここでの回答のいくつかは、これらのポイントの一方または両方に対応していません。正規表現以外の方法もありますが、それを使用する場合は、これを試してください:
Regex.Replace(value, @"\B[A-Z]", " $0")
\B
は否定\b
であるため、非ワード境界を表します。これは、パターンがXYzabc
の「Y」に一致するが、Yzabc
またはX Yzabc
には一致しないことを意味します。少しおまけとして、スペースを含む文字列でこれを使用できます。
あなたが持っているものは完璧に機能します。 value
をこの関数の戻り値に再割り当てすることを忘れないでください。
value = System.Text.RegularExpressions.Regex.Replace(value, "[A-Z]", " $0");
@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();
}
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
この正規表現では、すべての大文字の前にスペース文字が配置されます。
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"
RubyのRegexp経由:
"FooBarBaz".gsub(/(?!^)(?=[A-Z])/, ' ') # => "Foo Bar Baz"
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();
}
replaceAll("(?<=[^^\\p{Uppercase}])(?=[\\p{Uppercase}])"," ");
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
Aggregate
にとって良い機会のようです。これは読みやすいように設計されており、必ずしも高速ではありません。
someString
.Aggregate(
new StringBuilder(),
(str, ch) => {
if (char.IsUpper(ch) && str.Length > 0)
str.Append(" ");
str.Append(ch);
return str;
}
).ToString();
小文字、大文字、または数字の後にスペースを追加する簡単な方法。
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;
}
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;
}
マーティンブラウンの回答に加えて、数字にも問題がありました。たとえば、「Location2」または「Jan22」は、それぞれ「Location 2」および「Jan 22」である必要があります。
これを行うための私の正規表現は、Martin Brownの答えを使用しています。
"((?<=\p{Ll})\p{Lu})|((?!\A)\p{Lu}(?>\p{Ll}))|((?<=[\p{Ll}\p{Lu}])\p{Nd})|((?<=\p{Nd})\p{Lu})"
各部分の意味を理解するのに最適なサイトを次に示します。
Javaベースの正規表現アナライザー(ただし、ほとんどの.net正規表現で機能します)
上記の正規表現は、\p{Ll}
を[a-z]
に、\p{Lu}
を[A-Z]
に、\p{Nd}
をすべて置換しない限り、アクションスクリプトサイトでは機能しません。 [0-9]
。
これまでの回答はすべて複雑すぎるように見えました。
大文字と_が混在した文字列があり、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.
}
}
}
この同じ質問に答える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;
この関数に使用したテスト文字列と結果は次のとおりです。
単語の前にスペースを入れないより完全なソリューションを次に示します。
注:複数の正規表現を使用しました(簡潔ではありませんが、頭字語と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.)
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".
これには頭字語と頭字語の複数形が含まれており、受け入れられている答えよりも少し高速です。
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";
ここに私のソリューションは、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();
}
ASCII文字のみで構成される入力文字列のC#ソリューション。 regexは、大文字(大文字)を無視するためにnegative lookbehindを組み込みます文字列の先頭に表示されます。 Regex.Replace() を使用して、目的の文字列を返します。
regex101.com demo も参照してください。
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 demo および 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);
}
}
期待される出力:
Input: [ThisStringHasNoSpacesASCIIButItDoesHaveCapitalsLINQ]
Output: [This String Has No Spaces ASCII But It Does Have Capitals LINQ]
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%(多分少し)速いです。
質問は少し古いですが、最近では、これを正確に行うほか、人間が読めるテキストへの他の多くの変換を行うNugetのライブラリがあります。
Humanizer on GitHub またはNugetを確認してください。
例
"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"