En mi opinión, cuando se trata de potencialmente compleja cadena coincidente basado en normas y reemplazar - no se puede conseguir mucho mejor que una solución basada en expresiones regulares (a pesar de que son tan difíciles de leer!). Esto ofrece el mejor rendimiento y la mejor eficiencia de memoria, en mi opinión, te sorprenderá lo rápido que será.
Yo usaría el Regex.Replace overload that accepts an input string, regex pattern and a MatchEvaluator delegate. Un MatchEvaluator es una función que acepta un objeto Match
como entrada y devuelve un reemplazo de cadena.
Aquí está el código:
public static string Capitalise(string input)
{
//now the first character
return Regex.Replace(input, @"(?<=(^|[.;:])\s*)[a-z]",
(match) => { return match.Value.ToUpper(); });
}
La expresión regular utiliza el constructo (búsqueda hacia atrás por cero positivo de ancho) para restringir las capturas sólo para caracteres az precedido por el inicio de la cadena, o la puntuacion (< =?) marcas que quieres En el bit [.;:]
, puede agregar los adicionales que desee (por ejemplo, [.;:?."]
para agregar "y" caracteres.
Esto significa, también, que su MatchEvaluator no tiene que hacer ninguna unión innecesaria de cadenas (que desea evitar por motivos de rendimiento).
Todas las demás cosas mencionadas por uno de los otros que responden sobre el uso de RegexOptions.Compiled también son relevantes desde el punto de vista del rendimiento. Sin embargo, el método Regex.Replace estático ofrece beneficios de rendimiento muy similares (solo hay una búsqueda adicional en el diccionario).
Como digo, me sorprendería si alguna de las otras soluciones no regex aquí funcionara mejor y fuera tan rápida.
EDITAR
poner esta solución en contra de Ahmad como él con toda razón señaló que una mirada alrededor podría ser menos eficiente que hacerlo a su manera.
Aquí está el crudo de referencia que hice:
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);
}
En una versión de lanzamiento, el mi solución era aproximadamente un 12% más rápido que el de Ahmad (1,48 segundos en lugar de 1,68 segundos).
Curiosamente, sin embargo, si se hizo a través del método Regex.Replace estático, ambos fueron aproximadamente un 80% más lentos, y mi solución fue más lenta que la de Ahmad.
Yo diría: conviértanse en una matriz de caracteres, recorran usando un ciclo while, capitalicen cuando sea apropiado, guarden de nuevo en una cadena. Algunas líneas de código, pero debe ser rápido. –
@Hamish: Esa no es una mala respuesta; ciertamente es mucho mejor que manipular una Cadena repetidamente. Sin embargo, creo que StringBuffer sería más simple. –
No se olvide de '?', '!', ':' Y tal vez '\ n' (si omiten los signos de puntuación) – Andres