J'ai une exigence qui est relativement obscure, mais j'ai l'impression que cela devrait être possible en utilisant la BCL.
Pour le contexte, j'analyse une chaîne de date / heure dans Noda Time . Je maintiens un curseur logique pour ma position dans la chaîne d'entrée. Ainsi, alors que la chaîne complète peut être «3 janvier 2013», le curseur logique peut être sur le «J».
Maintenant, je dois analyser le nom du mois, en le comparant à tous les noms de mois connus pour la culture:
- Sensible à la culture
- Insensibilité à la casse
- Juste à partir du point du curseur (pas plus tard; je veux voir si le curseur "regarde" le nom du mois candidat)
- Rapidement
- ... et j'ai besoin de savoir par la suite combien de caractères ont été utilisés
Le code actuel pour ce faire fonctionne généralement, en utilisant CompareInfo.Compare
. C'est effectivement comme ça (juste pour la partie correspondante - il y a plus de code dans la vraie chose, mais ce n'est pas pertinent pour la correspondance):
internal bool MatchCaseInsensitive(string candidate, CompareInfo compareInfo)
{
return compareInfo.Compare(text, position, candidate.Length,
candidate, 0, candidate.Length,
CompareOptions.IgnoreCase) == 0;
}
Cependant, cela dépend du fait que le candidat et la région que nous comparons ont la même longueur. Bien la plupart du temps, mais pas très bien dans certains cas particuliers. Supposons que nous ayons quelque chose comme:
// 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";
Maintenant, ma comparaison échouera. Je pourrais utiliser IsPrefix
:
if (compareInfo.IsPrefix(text.Substring(position), candidate,
CompareOptions.IgnoreCase))
mais:
- Cela m'oblige à créer une sous-chaîne, ce que je préfère éviter. (Je considère Noda Time comme une bibliothèque système; l'analyse des performances peut très bien être importante pour certains clients.)
- Il ne me dit pas à quelle distance faire avancer le curseur par la suite
En réalité, je soupçonne fortement ce ne sera pas venu très souvent ... mais je voudrais vraiment que je fasse la bonne chose ici. J'aimerais aussi vraiment pouvoir le faire sans devenir un expert Unicode ou l'implémenter moi-même :)
(Élevé en tant que bogue 210 dans Noda Time, au cas où quelqu'un voudrait suivre une conclusion éventuelle.)
J'aime l'idée de normalisation. Je dois vérifier cela en détail pour a) l'exactitude et b) les performances. En supposant que je puisse le faire fonctionner correctement, je ne sais toujours pas comment cela vaudrait la peine d'être changé - c'est le genre de chose qui ne se produira probablement jamais dans la vraie vie, mais qui pourrait nuire aux performances de tous mes utilisateurs: (
J'ai également vérifié la BCL - qui ne semble pas non plus gérer cela correctement. Exemple de code:
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");
}
}
}
Changer le nom du mois personnalisé en juste "lit" avec une valeur de texte "bEd" analyse très bien.
D'accord, quelques points de données supplémentaires:
Le coût d'utilisation
Substring
etIsPrefix
est important mais pas horrible. Sur un échantillon de "Vendredi 12 avril 2013 20:28:42" sur mon ordinateur portable de développement, cela change le nombre d'opérations d'analyse que je peux exécuter en une seconde d'environ 460K à environ 400K. Je préfère éviter ce ralentissement si possible, mais ce n'est pas trop mal.La normalisation est moins faisable que je ne le pensais - car elle n'est pas disponible dans les bibliothèques de classes portables. Je pourrais potentiellement l'utiliser uniquement pour les versions non PCL, ce qui permettrait aux versions PCL d'être un peu moins correctes. Le succès des tests de normalisation (
string.IsNormalized
) réduit les performances à environ 445 000 appels par seconde, ce avec quoi je peux vivre. Je ne suis toujours pas sûr qu'il fasse tout ce dont j'ai besoin - par exemple, un nom de mois contenant "ß" devrait correspondre à "ss" dans de nombreuses cultures, je crois ... et la normalisation ne fait pas cela.
text
n'est pas trop long, vous pouvez le faire if (compareInfo.IndexOf(text, candidate, position, options) == position)
. msdn.microsoft.com/en-us/library/ms143031.aspx Mais si text
c'est très long, cela va perdre beaucoup de temps à chercher au-delà de ce qu'il faut.
String
classe du tout dans ce cas et d' utiliser un Char[]
directement. Vous finirez par écrire plus de code, mais c'est ce qui se passe lorsque vous voulez des performances élevées ... ou peut-être que vous devriez programmer en C ++ / CLI ;-)