Tengo un requisito que es relativamente oscuro, pero parece que debería ser posible usando el BCL.
Para el contexto, estoy analizando una cadena de fecha / hora en Noda Time . Mantengo un cursor lógico para mi posición dentro de la cadena de entrada. Entonces, mientras que la cadena completa puede ser "3 de enero de 2013", el cursor lógico puede estar en la 'J'.
Ahora, necesito analizar el nombre del mes, comparándolo con todos los nombres de meses conocidos para la cultura:
- Sensible a la cultura
- No distingue entre mayúsculas y minúsculas
- Solo desde el punto del cursor (no más tarde; quiero ver si el cursor está "mirando" el nombre del mes candidato)
- Con rapidez
- ... y luego necesito saber cuántos caracteres se usaron
El código actual para hacer esto generalmente funciona, usando CompareInfo.Compare
. Es efectivamente así (solo para la parte de coincidencia; hay más código en la realidad, pero no es relevante para la coincidencia):
internal bool MatchCaseInsensitive(string candidate, CompareInfo compareInfo)
{
return compareInfo.Compare(text, position, candidate.Length,
candidate, 0, candidate.Length,
CompareOptions.IgnoreCase) == 0;
}
Sin embargo, eso depende de que el candidato y la región que comparamos tengan la misma longitud. Bien la mayor parte del tiempo, pero no bien en algunos casos especiales. Supongamos que tenemos algo como:
// U+00E9 is a single code point for e-acute
var text = "x b\u00e9d y";
int position = 2;
// e followed by U+0301 still means e-acute, but from two code points
var candidate = "be\u0301d";
Ahora mi comparación fallará. Podría usar IsPrefix
:
if (compareInfo.IsPrefix(text.Substring(position), candidate,
CompareOptions.IgnoreCase))
pero:
- Eso requiere que cree una subcadena, que realmente prefiero evitar. (Veo Noda Time efectivamente como una biblioteca del sistema; el rendimiento del análisis puede ser importante para algunos clientes).
- No me dice cuánto avanzar el cursor después
En realidad, sospecho fuertemente que esto no ocurrirá muy a menudo ... pero realmente me gustaría hacer lo correcto aquí. También me gustaría poder hacerlo sin convertirme en un experto en Unicode o implementarlo yo mismo :)
(Planteado como error 210 en Noda Time, en caso de que alguien quiera seguir alguna conclusión final).
Me gusta la idea de la normalización. Necesito verificar eso en detalle para a) corrección yb) desempeño. Suponiendo que pueda hacer que funcione correctamente, todavía no estoy seguro de si valdría la pena cambiarlo todo: es el tipo de cosas que probablemente nunca aparecerán en la vida real, pero que podrían dañar el rendimiento de todos mis usuarios: (
También verifiqué el BCL, que tampoco parece manejar esto correctamente. Código de muestra:
using System;
using System.Globalization;
class Test
{
static void Main()
{
var culture = (CultureInfo) CultureInfo.InvariantCulture.Clone();
var months = culture.DateTimeFormat.AbbreviatedMonthNames;
months[10] = "be\u0301d";
culture.DateTimeFormat.AbbreviatedMonthNames = months;
var text = "25 b\u00e9d 2013";
var pattern = "dd MMM yyyy";
DateTime result;
if (DateTime.TryParseExact(text, pattern, culture,
DateTimeStyles.None, out result))
{
Console.WriteLine("Parsed! Result={0}", result);
}
else
{
Console.WriteLine("Didn't parse");
}
}
}
Cambiar el nombre del mes personalizado a solo "cama" con un valor de texto de "bEd" se analiza bien.
Bien, algunos puntos de datos más:
El costo de usar
Substring
yIsPrefix
es significativo pero no horrible. En una muestra de "Viernes 12 de abril de 2013 20:28:42" en mi computadora portátil de desarrollo, cambia el número de operaciones de análisis que puedo ejecutar en un segundo de aproximadamente 460 K a aproximadamente 400 K. Prefiero evitar esa desaceleración si es posible, pero no está tan mal.La normalización es menos factible de lo que pensaba, porque no está disponible en las bibliotecas de clases portátiles. Potencialmente, podría usarlo solo para compilaciones que no sean PCL, lo que permite que las compilaciones PCL sean un poco menos correctas. El impacto de rendimiento de las pruebas de normalización (
string.IsNormalized
) reduce el rendimiento a aproximadamente 445K llamadas por segundo, con lo que puedo vivir. Todavía no estoy seguro de que haga todo lo que necesito, por ejemplo, un nombre de mes que contenga "ß" debería coincidir con "ss" en muchas culturas, creo ... y la normalización no hace eso.
text
no es demasiado largo, puede hacerlo if (compareInfo.IndexOf(text, candidate, position, options) == position)
. msdn.microsoft.com/en-us/library/ms143031.aspx Pero si text
es muy largo, perderá mucho tiempo buscando más allá de donde se necesita.
String
clase en absoluto en este caso y utilizar una Char[]
forma directa. Terminará escribiendo más código, pero eso es lo que sucede cuando desea un alto rendimiento ... o tal vez debería programar en C ++ / CLI ;-)