Tenho um requisito que é relativamente obscuro, mas parece que deveria ser possível usando o BCL.
Para contextualizar, estou analisando uma string de data / hora em Noda Time . Eu mantenho um cursor lógico para minha posição dentro da string de entrada. Portanto, embora a string completa possa ser "3 de janeiro de 2013", o cursor lógico pode estar em 'J'.
Agora, preciso analisar o nome do mês, comparando-o com todos os nomes de meses conhecidos da cultura:
- Sensibilidade à cultura
- Não faz distinção entre maiúsculas e minúsculas
- Apenas do ponto do cursor (não mais tarde; quero ver se o cursor está "olhando" para o nome do mês candidato)
- Rapidamente
- ... e eu preciso saber depois quantos caracteres foram usados
O código atual para fazer isso geralmente funciona, usando CompareInfo.Compare
. É efetivamente assim (apenas para a parte correspondente - há mais código na coisa real, mas não é relevante para a correspondência):
internal bool MatchCaseInsensitive(string candidate, CompareInfo compareInfo)
{
return compareInfo.Compare(text, position, candidate.Length,
candidate, 0, candidate.Length,
CompareOptions.IgnoreCase) == 0;
}
No entanto, isso depende do candidato e da região comparada ter o mesmo comprimento. Bom na maioria das vezes, mas não em alguns casos especiais. Suponha que tenhamos 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";
Agora minha comparação falhará. Eu poderia usar IsPrefix
:
if (compareInfo.IsPrefix(text.Substring(position), candidate,
CompareOptions.IgnoreCase))
mas:
- Isso exige que eu crie uma substring, o que realmente prefiro evitar. (Estou vendo o Noda Time como efetivamente uma biblioteca do sistema; o desempenho da análise pode ser importante para alguns clientes.)
- Não me diz o quanto devo avançar o cursor depois
Na realidade, tenho fortes suspeitas de que isso não acontecerá com muita frequência ... mas eu realmente gostaria de fazer a coisa certa aqui. Também gostaria muito de poder fazer isso sem me tornar um especialista em Unicode ou implementá-lo sozinho :)
(Criado como bug 210 na Hora de Noda, caso alguém queira seguir alguma conclusão eventual.)
Gosto da ideia de normalização. Preciso verificar isso em detalhes para a) correção eb) desempenho. Supondo que posso fazer funcionar corretamente, ainda não tenho certeza se valeria a pena mudar tudo - é o tipo de coisa que provavelmente nunca aparecerá na vida real, mas pode prejudicar o desempenho de todos os meus usuários: (
Também verifiquei o BCL - que também não parece lidar com isso corretamente. Código de amostra:
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");
}
}
}
Alterar o nome do mês personalizado para apenas "cama" com um valor de texto "bEd" tem uma boa análise.
Ok, mais alguns pontos de dados:
O custo de uso
Substring
eIsPrefix
é significativo, mas não horrível. Em uma amostra de "Sexta-feira, 12 de abril de 2013 20:28:42" no meu laptop de desenvolvimento, ele altera o número de operações de análise que posso executar em um segundo de cerca de 460K para cerca de 400K. Prefiro evitar essa desaceleração, se possível, mas não é tão ruim.A normalização é menos viável do que eu pensava - porque não está disponível nas Bibliotecas de Classes Portáteis. Eu poderia usá-lo potencialmente apenas para compilações não PCL, permitindo que as compilações PCL fossem um pouco menos corretas. O impacto no desempenho do teste de normalização (
string.IsNormalized
) reduz o desempenho para cerca de 445 mil chamadas por segundo, com o qual posso viver. Ainda não tenho certeza se ele faz tudo o que preciso - por exemplo, um nome de mês contendo "ß" deve corresponder a "ss" em muitas culturas, eu acredito ... e normalizar não faz isso.
text
não for muito longo, você pode fazer if (compareInfo.IndexOf(text, candidate, position, options) == position)
. msdn.microsoft.com/en-us/library/ms143031.aspx Mas se text
for muito longo, vai perder muito tempo pesquisando além de onde é necessário.
String
classe em tudo neste caso e usar um Char[]
diretamente. Você acabará escrevendo mais código, mas é o que acontece quando você quer alto desempenho ... ou talvez você devesse estar programando em C ++ / CLI ;-)