Mam wymaganie, które jest stosunkowo niejasne, ale wydaje mi się, że powinno być możliwe przy użyciu BCL.
Dla kontekstu analizuję ciąg daty / godziny w czasie Noda . Utrzymuję logiczny kursor dla mojej pozycji w ciągu wejściowym. Tak więc, chociaż cały ciąg może mieć postać „3 stycznia 2013 r.”, Kursor logiczny może znajdować się na „J”.
Teraz muszę przeanalizować nazwę miesiąca, porównując ją ze wszystkimi znanymi nazwami miesięcy dla kultury:
- Z wrażliwością kulturową
- Bez rozróżniania wielkości liter
- Tylko z punktu kursora (nie później; chcę sprawdzić, czy kursor „patrzy” na nazwę miesiąca kandydującego)
- Szybko
- ... a potem muszę wiedzieć, ile znaków zostało użytych
Aktualny kod to zrobić ogólnie działa, używając CompareInfo.Compare
. Skutecznie działa to tak (tylko dla pasującej części - w rzeczywistości jest więcej kodu, ale nie ma to związku z dopasowaniem):
internal bool MatchCaseInsensitive(string candidate, CompareInfo compareInfo)
{
return compareInfo.Compare(text, position, candidate.Length,
candidate, 0, candidate.Length,
CompareOptions.IgnoreCase) == 0;
}
Jednak zależy to od tego, czy kandydat i region, który porównujemy, są tej samej długości. W porządku przez większość czasu, ale nie w niektórych szczególnych przypadkach. Załóżmy, że mamy coś takiego:
// 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";
Teraz moje porównanie się nie powiedzie. Mógłbym użyć IsPrefix
:
if (compareInfo.IsPrefix(text.Substring(position), candidate,
CompareOptions.IgnoreCase))
ale:
- To wymaga ode mnie stworzenia podciągu, którego naprawdę wolałbym unikać. (Uważam, że czas Noda jest efektywną biblioteką systemową; wydajność analizowania może być ważna dla niektórych klientów).
- Nie mówi mi, jak daleko później przesunąć kursor
W rzeczywistości mocno podejrzewam, że nie będzie się to pojawiać zbyt często ... ale naprawdę chciałbym zrobić tutaj właściwą rzecz. Chciałbym też móc to zrobić bez zostania ekspertem od Unicode lub samodzielnego wdrażania :)
(Zgłoszony jako błąd 210 w Czasie Nody, na wypadek gdyby ktoś chciał wyciągnąć wnioski).
Podoba mi się idea normalizacji. Muszę to szczegółowo sprawdzić pod kątem a) poprawności i b) wykonania. Zakładając, że mogę sprawić, by działał poprawnie, nadal nie jestem pewien, czy warto byłoby to zmienić w ogóle - jest to coś, co prawdopodobnie nigdy nie pojawi się w prawdziwym życiu, ale może zaszkodzić wydajności wszystkich moich użytkowników: (
Sprawdziłem również BCL - który również nie wydaje się obsługiwać tego poprawnie. Przykładowy kod:
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");
}
}
}
Zmiana niestandardowej nazwy miesiąca na „łóżko” z wartością tekstową „bEd” parsuje dobrze.
OK, jeszcze kilka punktów danych:
Koszt użytkowania
Substring
iIsPrefix
jest znaczny, ale nie straszny. Na próbce „Piątek, 12 kwietnia 2013 20:28:42” na moim laptopie deweloperskim zmienia liczbę operacji analizy składniowej, które mogę wykonać w ciągu sekundy, z około 460K do około 400K. Wolałbym raczej uniknąć tego spowolnienia, jeśli to możliwe, ale nie jest tak źle.Normalizacja jest mniej wykonalna niż myślałem - ponieważ nie jest dostępna w przenośnych bibliotekach klas. Potencjalnie mógłbym go używać tylko do kompilacji innych niż PCL, dzięki czemu kompilacje PCL są nieco mniej poprawne. Uderzenie wydajnościowe testowania normalizacji (
string.IsNormalized
) obniża wydajność do około 445 tys. Wywołań na sekundę, z czym mogę żyć. Nadal nie jestem pewien, czy robi wszystko, czego potrzebuję - na przykład nazwa miesiąca zawierająca „ß” powinna pasować do „ss” w wielu kulturach, myślę, że ... a normalizacja tego nie robi.
text
nie jest za długi, możesz to zrobić if (compareInfo.IndexOf(text, candidate, position, options) == position)
. msdn.microsoft.com/en-us/library/ms143031.aspx Ale jeśli text
trwa to bardzo długo, to zmarnuje dużo czasu na szukanie dalej niż to konieczne.
String
klasy w ogóle w tym przypadku i używać Char[]
bezpośrednio. Skończy się na pisaniu więcej kodu, ale tak właśnie się dzieje, gdy zależy Ci na wysokiej wydajności ... a może powinieneś programować w C ++ / CLI ;-)