Ho un requisito relativamente oscuro, ma sembra che dovrebbe essere possibile utilizzare il BCL.
Per contesto, sto analizzando una stringa data / ora in Noda Time . Mantengo un cursore logico per la mia posizione all'interno della stringa di input. Quindi, sebbene la stringa completa possa essere "3 gennaio 2013", il cursore logico potrebbe trovarsi sulla "J".
Ora, ho bisogno di analizzare il nome del mese, confrontandolo con tutti i nomi dei mesi noti per la cultura:
- Cultura-sensibile
- Case-insensitively
- Solo dal punto del cursore (non più tardi; voglio vedere se il cursore sta "guardando" il nome del mese candidato)
- Velocemente
- ... e poi ho bisogno di sapere quanti caratteri sono stati usati
Il codice corrente per farlo generalmente funziona, usando CompareInfo.Compare
. È effettivamente così (solo per la parte corrispondente - c'è più codice nella cosa reale, ma non è rilevante per la corrispondenza):
internal bool MatchCaseInsensitive(string candidate, CompareInfo compareInfo)
{
return compareInfo.Compare(text, position, candidate.Length,
candidate, 0, candidate.Length,
CompareOptions.IgnoreCase) == 0;
}
Tuttavia, ciò dipende dal fatto che il candidato e la regione che confrontiamo abbiano la stessa lunghezza. Va bene per la maggior parte del tempo, ma non va bene in alcuni casi speciali. Supponiamo di avere qualcosa come:
// 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";
Ora il mio confronto fallirà. Potrei usare IsPrefix
:
if (compareInfo.IsPrefix(text.Substring(position), candidate,
CompareOptions.IgnoreCase))
ma:
- Ciò mi richiede di creare una sottostringa, che preferirei davvero evitare. (Sto vedendo Noda Time come una libreria di sistema efficace; l'analisi delle prestazioni potrebbe essere importante per alcuni client.)
- Non mi dice di quanto far avanzare il cursore in seguito
In realtà, ho il forte sospetto che non si presenti molto spesso ... ma mi piacerebbe davvero come faccia la cosa giusta qui. Mi piacerebbe anche molto poterlo fare senza diventare un esperto di Unicode o implementarlo da solo :)
(Generato come bug 210 in Noda Time, nel caso qualcuno volesse seguire un'eventuale conclusione.)
Mi piace l'idea della normalizzazione. Devo verificarlo in dettaglio per a) correttezza eb) prestazioni. Supponendo che io possa farlo funzionare correttamente, non sono ancora sicuro di come valga la pena cambiare tutto - è il genere di cose che probabilmente non si presenteranno mai nella vita reale, ma potrebbero danneggiare le prestazioni di tutti i miei utenti: (
Ho anche controllato il BCL, che non sembra gestirlo correttamente. Codice di esempio:
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");
}
}
}
La modifica del nome del mese personalizzato in "letto" con un valore di testo "bEd" viene analizzata correttamente.
Ok, alcuni altri punti dati:
Il costo di utilizzo
Substring
edIsPrefix
è significativo ma non orribile. In un esempio di "Friday April 12 2013 20:28:42" sul mio laptop di sviluppo, cambia il numero di operazioni di analisi che posso eseguire in un secondo da circa 460K a circa 400K. Preferirei evitare quel rallentamento se possibile, ma non è poi così male.La normalizzazione è meno fattibile di quanto pensassi, perché non è disponibile nelle librerie di classi portatili. Potrei potenzialmente usarlo solo per build non PCL, consentendo alle build PCL di essere un po 'meno corrette. Il colpo di prestazioni del test per la normalizzazione (
string.IsNormalized
) porta le prestazioni a circa 445K chiamate al secondo, con cui posso convivere. Non sono ancora sicuro che faccia tutto ciò di cui ho bisogno - per esempio, il nome di un mese contenente "ß" dovrebbe corrispondere a "ss" in molte culture, credo ... e la normalizzazione non lo fa.
text
non è troppo lungo, potresti farlo if (compareInfo.IndexOf(text, candidate, position, options) == position)
. msdn.microsoft.com/en-us/library/ms143031.aspx Ma se text
è molto lungo, sprecherà molto tempo a cercare oltre il punto necessario.
String
classe a tutti , in questo caso e l'uso di unChar[]
direttamente. Finirai per scrivere più codice, ma è quello che succede quando vuoi alte prestazioni ... o forse dovresti programmare in C ++ / CLI ;-)