По моему мнению, когда речь идет о потенциально сложных основанных на правилах сопоставлении и замене строк - вы не можете стать намного лучше, чем решение на основе регулярных выражений (несмотря на то, что их так трудно читать!). На мой взгляд, это обеспечивает лучшую производительность и эффективность памяти - вы будете удивлены, насколько быстро это будет.
Я бы использовал перегрузку Regex.Replace, которая принимает входную строку, шаблон регулярного выражения и делегат MatchEvaluator . 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, начинающимися с начала строки, или нужными знаками препинания. В бите <code>[.;:] вы можете добавить дополнительные, которые вы хотите (например, [.;:?."]
добавить? И "символы.
Это также означает, что вашему MatchEvaluator не нужно выполнять никаких ненужных объединений строк (чего вы хотите избежать по соображениям производительности).
Все остальные вещи, упомянутые одним из других отвечающих за использование RegexOptions.Compiled, также актуальны с точки зрения производительности. Однако статический метод Regex.Replace предлагает очень похожие преимущества в производительности (есть только дополнительный поиск в словаре).
Как я уже сказал - я буду удивлен, если какое-либо из других решений, не относящихся к регулярным выражениям, будет работать лучше и будет столь же быстрым.
EDIT
Поставил это решение против Ахмада, так как он совершенно справедливо указал, что осмотр может быть менее эффективным, чем делать это по-своему.
Вот грубый тест, который я сделал:
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);
}
В сборке релиза мое решение было примерно на 12% быстрее, чем решение Ахмада (1,48 секунды вместо 1,68 секунды).
Интересно, однако, что если это было сделано с помощью статического метода Regex.Replace, оба были примерно на 80% медленнее, и мое решение было медленнее, чем решение Ахмада.