На мой взгляд, когда дело доходит до потенциально сложного сопоставления и замены строк на основе правил, вы не сможете добиться большего успеха, чем решение на основе регулярных выражений (несмотря на то, что их так трудно читать!). На мой взгляд, это обеспечивает наилучшую производительность и эффективность использования памяти — вы будете удивлены, насколько быстро это будет.
Я бы использовал перегрузку 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(); });
}
Регулярное выражение использует конструкцию (?‹=) (положительный просмотр назад с нулевой шириной), чтобы ограничить захват только символами az, которым предшествует начало строки, или нужными вам знаками препинания. В бит [.;:]
вы можете добавить дополнительные, которые хотите (например, [.;:?."]
, чтобы добавить символы ? и ".
Это также означает, что вашему MatchEvaluator не нужно выполнять ненужное соединение строк (которого вы хотите избежать из соображений производительности).
Все остальное, упомянутое одним из других респондентов об использовании RegexOptions.Compiled, также актуально с точки зрения производительности. Однако статический метод Regex.Replace предлагает очень похожие преимущества в производительности (есть только дополнительный поиск по словарю).
Как я уже сказал, я буду удивлен, если какое-либо из других решений, не использующих регулярные выражения, будет работать лучше и будет таким же быстрым.
ИЗМЕНИТЬ
Сопоставили это решение с решением Ахмада, поскольку он совершенно справедливо указал, что осмотр может быть менее эффективным, чем делать это по-своему.
Вот грубый тест, который я сделал:
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% медленнее, и мое решение было медленнее, чем у Ахмада.
person
Andras Zoltan
schedule
25.01.2010