複数の文を含む文字列があります。すべての文の最初の単語の最初の文字を大文字にする方法. Word の段落書式のようなもの。
例 ."これはコードです。コードは C# です。" 出力は、"これはコードです。コードは C# です" である必要があります。
1 つの方法は、「.」に基づいて文字列を分割することです。最初の文字を大文字にしてから、再び結合します。
より良い解決策はありますか?
複数の文を含む文字列があります。すべての文の最初の単語の最初の文字を大文字にする方法. Word の段落書式のようなもの。
例 ."これはコードです。コードは C# です。" 出力は、"これはコードです。コードは C# です" である必要があります。
1 つの方法は、「.」に基づいて文字列を分割することです。最初の文字を大文字にしてから、再び結合します。
より良い解決策はありますか?
句読点カテゴリを使用して .!?" などを指定する必要を回避する正規表現ソリューションを次に示しますが、それがニーズをカバーしているか、明示的に設定しているかを確認する必要があります。「サポートされている Unicode 一般カテゴリ」の「P」カテゴリを読んでください。MSDN 文字クラス ページにある " セクション。
string input = @"this is some code. the code is in C#? it's great! In ""quotes."" after quotes.";
string pattern = @"(^|\p{P}\s+)(\w+)";
// compiled for performance (might want to benchmark it for your loop)
Regex rx = new Regex(pattern, RegexOptions.Compiled);
string result = rx.Replace(input, m => m.Groups[1].Value
+ m.Groups[2].Value.Substring(0, 1).ToUpper()
+ m.Groups[2].Value.Substring(1));
\p{P}
クラスを使用しない場合は、次のように自分で文字を指定する必要があります。
string pattern = @"(^|[.?!""]\s+)(\w+)";
編集:以下は、3 つのパターンを示す更新された例です。1 つ目は、すべての句読点が大文字と小文字にどのように影響するかを示しています。2 つ目は、クラス減算を使用して特定の句読点カテゴリを選択する方法を示しています。特定の句読点グループを削除しながら、すべての句読点を使用します。3 番目は 2 番目と似ていますが、異なるグループを使用しています。
MSDN のリンクでは、句読点のカテゴリの一部が何を参照しているかが詳しく説明されていないため、内訳を次に示します。
_
-
(
[
{
)
]
}
,
、セミコロン、スラッシュなどのその他の:
句読点、、、、、;
\
/
これらのグループが結果に与える影響を注意深く比較してください。これにより、高度な柔軟性が得られます。これが望ましくないと思われる場合は、前に示したように文字クラスで特定の文字を使用できます。
string input = @"foo ( parens ) bar { braces } foo [ brackets ] bar. single ' quote & "" double "" quote.
dash - test. Connector _ test. Comma, test. Semicolon; test. Colon: test. Slash / test. Slash \ test.";
string[] patterns = {
@"(^|\p{P}\s+)(\w+)", // all punctuation chars
@"(^|[\p{P}-[\p{Pc}\p{Pd}\p{Ps}\p{Pe}]]\s+)(\w+)", // all punctuation chars except Pc/Pd/Ps/Pe
@"(^|[\p{P}-[\p{Po}]]\s+)(\w+)" // all punctuation chars except Po
};
// compiled for performance (might want to benchmark it for your loop)
foreach (string pattern in patterns)
{
Console.WriteLine("*** Current pattern: {0}", pattern);
string result = Regex.Replace(input, pattern,
m => m.Groups[1].Value
+ m.Groups[2].Value.Substring(0, 1).ToUpper()
+ m.Groups[2].Value.Substring(1));
Console.WriteLine(result);
Console.WriteLine();
}
最後のパターンを使用すると、「ダッシュ」は大文字ではなく、新しい行にあることに注意してください。大文字にする 1 つの方法は、RegexOptions.Multiline
オプションを使用することです。上記のスニペットを試して、目的の結果が得られるかどうかを確認してください。
また、例のために、上記のループでは RegexOptions.Compiled を使用しませんでした。両方のオプションまたはそれらを一緒に使用するには: RegexOptions.Compiled | RegexOptions.Multiline
.
私の意見では、潜在的に複雑なルールベースの文字列の照合と置換に関しては、正規表現ベースのソリューションよりも優れたものはありません (非常に読みにくいという事実にもかかわらず!)。私の意見では、これが最高のパフォーマンスとメモリ効率を提供します。これがどれほど高速であるかに驚かれることでしょう。
入力文字列、正規表現パターン、および MatchEvaluator デリゲートを受け入れる Regex.Replace オーバーロードを使用します。MatchEvaluator は、Match
オブジェクトを入力として受け取り、文字列の置換を返す関数です。
コードは次のとおりです。
public static string Capitalise(string input)
{
//now the first character
return Regex.Replace(input, @"(?<=(^|[.;:])\s*)[a-z]",
(match) => { return match.Value.ToUpper(); });
}
正規表現は (?<=) コンストラクト (ゼロ幅の正の後読み) を使用して、文字列の先頭が前にある a ~ z 文字、または必要な句読点のみにキャプチャを制限します。[.;:]
ビットでは、必要なものを追加できます (たとえば、[.;:?."]
? および " 文字を追加します。
これは、MatchEvaluator が不必要な文字列の結合を行う必要がないことも意味します (パフォーマンス上の理由からこれは避けたいと考えています)。
RegexOptions.Compiled の使用について他の回答者の 1 人が言及した他のすべてのものは、パフォーマンスの観点からも関連しています。ただし、静的な Regex.Replace メソッドは、非常によく似たパフォーマンス上の利点を提供します (辞書検索が追加されるだけです)。
私が言うように-ここでの他の非正規表現ソリューションのいずれかがより適切に機能し、同じくらい高速である場合、私は驚くでしょう.
編集
このソリューションを Ahmad のソリューションと比較しました。彼は、ルックアラウンドは自分のやり方で行うよりも効率が悪い可能性があると指摘しました。
これが私が行った大まかなベンチマークです。
public string LowerCaseLipsum
{
get
{
//went to lipsum.com and generated 10 paragraphs of lipsum
//which I then initialised into the backing field with @"[lipsumtext]".ToLower()
return _lowerCaseLipsum;
}
}
[TestMethod]
public void CapitaliseAhmadsWay()
{
List<string> results = new List<string>();
DateTime start = DateTime.Now;
Regex r = new Regex(@"(^|\p{P}\s+)(\w+)", RegexOptions.Compiled);
for (int f = 0; f < 1000; f++)
{
results.Add(r.Replace(LowerCaseLipsum, m => m.Groups[1].Value
+ m.Groups[2].Value.Substring(0, 1).ToUpper()
+ m.Groups[2].Value.Substring(1)));
}
TimeSpan duration = DateTime.Now - start;
Console.WriteLine("Operation took {0} seconds", duration.TotalSeconds);
}
[TestMethod]
public void CapitaliseLookAroundWay()
{
List<string> results = new List<string>();
DateTime start = DateTime.Now;
Regex r = new Regex(@"(?<=(^|[.;:])\s*)[a-z]", RegexOptions.Compiled);
for (int f = 0; f < 1000; f++)
{
results.Add(r.Replace(LowerCaseLipsum, m => m.Value.ToUpper()));
}
TimeSpan duration = DateTime.Now - start;
Console.WriteLine("Operation took {0} seconds", duration.TotalSeconds);
}
リリース ビルドでは、私のソリューションは Ahmad のソリューションよりも約 12% 高速でした (1.68 秒に対して 1.48 秒)。
ただし、興味深いことに、静的な Regex.Replace メソッドを使用した場合、どちらも約 80% 遅くなり、私のソリューションは Ahmad のソリューションよりも遅くなりました。
いくつかのオプションがあります。
IEnumerable<char>
、ピリオドの後の最初の文字を大文字にして new を生成する C# イテレータを作成します。ストリーミング ソリューションの利点を提供する場合があります。以下のコードは反復子を使用しています。
public static string ToSentenceCase( string someString )
{
var sb = new StringBuilder( someString.Length );
bool wasPeriodLastSeen = true; // We want first letter to be capitalized
foreach( var c in someString )
{
if( wasPeriodLastSeen && !c.IsWhiteSpace )
{
sb.Append( c.ToUpper() );
wasPeriodLastSeen = false;
}
else
{
if( c == '.' ) // you may want to expand this to other punctuation
wasPeriodLastSeen = true;
sb.Append( c );
}
}
return sb.ToString();
}
理由はわかりませんが、LBushkin の提案に基づいて、yield returnを試してみることにしました。楽しみのためだけに。
static IEnumerable<char> CapitalLetters(string sentence)
{
//capitalize first letter
bool capitalize = true;
char lastLetter;
for (int i = 0; i < sentence.Length; i++)
{
lastLetter = sentence[i];
yield return (capitalize) ? Char.ToUpper(sentence[i]) : sentence[i];
if (Char.IsWhiteSpace(lastLetter) && capitalize == true)
continue;
capitalize = false;
if (lastLetter == '.' || lastLetter == '!') //etc
capitalize = true;
}
}
使用するには:
string sentence = new String(CapitalLetters("this is some code. the code is in C#.").ToArray());